1/* 2 * Copyright 2009, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Lotz <mmlr@mlotz.ch> 7 * François Revol <revol@free.fr> 8 */ 9 10#include "HTML5HWInterface.h" 11#include "HTML5DrawingEngine.h" 12#include "CanvasEventStream.h" 13#include "CanvasMessage.h" 14 15#include "WebHandler.h" 16#include "WebServer.h" 17#include "WebWorker.h" 18//#include "NetReceiver.h" 19//#include "NetSender.h" 20#include "StreamingRingBuffer.h" 21 22#include "desktop.html.h" 23#include "haiku.js.h" 24 25#include <Autolock.h> 26#include <NetEndpoint.h> 27 28#include <new> 29#include <string.h> 30 31 32#define TRACE(x...) debug_printf("HTML5HWInterface: "x) 33#define TRACE_ALWAYS(x...) debug_printf("HTML5HWInterface: "x) 34#define TRACE_ERROR(x...) debug_printf("HTML5HWInterface: "x) 35 36 37struct callback_info { 38 uint32 token; 39 HTML5HWInterface::CallbackFunction callback; 40 void* cookie; 41}; 42 43 44HTML5HWInterface::HTML5HWInterface(const char* target) 45 : 46 HWInterface(), 47 fTarget(target), 48 fRemoteHost(NULL), 49 fRemotePort(10900), 50 fIsConnected(false), 51 fProtocolVersion(100), 52 fConnectionSpeed(0), 53 fListenPort(10901), 54 fReceiveEndpoint(NULL), 55 fSendBuffer(NULL), 56 fReceiveBuffer(NULL), 57 fServer(NULL), 58// fSender(NULL), 59// fReceiver(NULL), 60 fEventThread(-1), 61 fEventStream(NULL), 62 fCallbackLocker("callback locker") 63{ 64 fDisplayMode.virtual_width = 640; 65 fDisplayMode.virtual_height = 480; 66 fDisplayMode.space = B_RGB32; 67 68 //TODO: Cleanup; parse host ?? 69 fRemoteHost = strdup(fTarget); 70 if (strncmp(fRemoteHost, "html5:", 6) != 0) { 71 fInitStatus = B_BAD_VALUE; 72 return; 73 } 74 char *portStart = fRemoteHost + 5;//strchr(fRemoteHost + 6, ':'); 75 if (portStart != NULL) { 76 portStart[0] = 0; 77 portStart++; 78 if (sscanf(portStart, "%lu", &fRemotePort) != 1) { 79 fInitStatus = B_BAD_VALUE; 80 return; 81 } 82 83 fListenPort = fRemotePort; 84 } 85 86 fReceiveEndpoint = new(std::nothrow) BNetEndpoint(); 87 if (fReceiveEndpoint == NULL) { 88 fInitStatus = B_NO_MEMORY; 89 return; 90 } 91 92 fInitStatus = fReceiveEndpoint->Bind(fListenPort); 93 if (fInitStatus != B_OK) 94 return; 95 96 fSendBuffer = new(std::nothrow) StreamingRingBuffer(16 * 1024); 97 if (fSendBuffer == NULL) { 98 fInitStatus = B_NO_MEMORY; 99 return; 100 } 101 102 fInitStatus = fSendBuffer->InitCheck(); 103 if (fInitStatus != B_OK) 104 return; 105 106 fReceiveBuffer = new(std::nothrow) StreamingRingBuffer(16 * 1024); 107 if (fReceiveBuffer == NULL) { 108 fInitStatus = B_NO_MEMORY; 109 return; 110 } 111 112 fInitStatus = fReceiveBuffer->InitCheck(); 113 if (fInitStatus != B_OK) 114 return; 115 116 fServer = new(std::nothrow) WebServer(fReceiveEndpoint); 117 if (fServer == NULL) { 118 fInitStatus = B_NO_MEMORY; 119 return; 120 } 121 122 WebHandler *handler; 123 handler = new(std::nothrow) WebHandler("output", fSendBuffer); 124 if (handler == NULL) { 125 fInitStatus = B_NO_MEMORY; 126 return; 127 } 128 handler->SetMultipart(); 129 handler->SetType("multipart/x-mixed-replace;boundary=x"); 130 fServer->AddHandler(handler); 131 132 handler = new(std::nothrow) WebHandler("input", fReceiveBuffer); 133 if (handler == NULL) { 134 fInitStatus = B_NO_MEMORY; 135 return; 136 } 137 fServer->AddHandler(handler); 138 139 handler = new(std::nothrow) WebHandler("desktop.html", 140 new(std::nothrow) BMemoryIO(desktop_html, sizeof(desktop_html) - 1)); 141 if (handler == NULL) { 142 fInitStatus = B_NO_MEMORY; 143 return; 144 } 145 fServer->AddHandler(handler); 146 147 handler = new(std::nothrow) WebHandler("haiku.js", 148 new(std::nothrow) BMemoryIO(haiku_js, sizeof(haiku_js) - 1)); 149 if (handler == NULL) { 150 fInitStatus = B_NO_MEMORY; 151 return; 152 } 153 fServer->AddHandler(handler); 154 155 156 fEventStream = new(std::nothrow) CanvasEventStream(); 157 if (fEventStream == NULL) { 158 fInitStatus = B_NO_MEMORY; 159 return; 160 } 161 162// fInitStatus = _Connect(); 163// if (fInitStatus != B_OK) 164// return; 165 fDisplayMode.virtual_width = 800; 166 fDisplayMode.virtual_height = 600; 167 168 169 fEventThread = spawn_thread(_EventThreadEntry, "HTML5 event thread", 170 B_NORMAL_PRIORITY, this); 171 if (fEventThread < 0) { 172 fInitStatus = fEventThread; 173 return; 174 } 175 176 resume_thread(fEventThread); 177} 178 179 180HTML5HWInterface::~HTML5HWInterface() 181{ 182// delete fReceiver; 183 delete fReceiveBuffer; 184 185 delete fSendBuffer; 186// delete fSender; 187 188 delete fReceiveEndpoint; 189// delete fSendEndpoint; 190 delete fServer; 191 192 delete fEventStream; 193 194 free(fRemoteHost); 195} 196 197 198status_t 199HTML5HWInterface::Initialize() 200{ 201 return fInitStatus; 202} 203 204 205status_t 206HTML5HWInterface::Shutdown() 207{ 208 _Disconnect(); 209 return B_OK; 210} 211 212 213DrawingEngine* 214HTML5HWInterface::CreateDrawingEngine() 215{ 216 return new(std::nothrow) HTML5DrawingEngine(this); 217} 218 219 220EventStream* 221HTML5HWInterface::CreateEventStream() 222{ 223 return fEventStream; 224} 225 226 227status_t 228HTML5HWInterface::AddCallback(uint32 token, CallbackFunction callback, 229 void* cookie) 230{ 231 BAutolock lock(fCallbackLocker); 232 int32 index = fCallbacks.BinarySearchIndexByKey(token, &_CallbackCompare); 233 if (index >= 0) 234 return B_NAME_IN_USE; 235 236 callback_info* info = new(std::nothrow) callback_info; 237 if (info == NULL) 238 return B_NO_MEMORY; 239 240 info->token = token; 241 info->callback = callback; 242 info->cookie = cookie; 243 244 fCallbacks.AddItem(info, -index - 1); 245 return B_OK; 246} 247 248 249bool 250HTML5HWInterface::RemoveCallback(uint32 token) 251{ 252 BAutolock lock(fCallbackLocker); 253 int32 index = fCallbacks.BinarySearchIndexByKey(token, &_CallbackCompare); 254 if (index < 0) 255 return false; 256 257 delete fCallbacks.RemoveItemAt(index); 258 return true; 259} 260 261 262callback_info* 263HTML5HWInterface::_FindCallback(uint32 token) 264{ 265 BAutolock lock(fCallbackLocker); 266 return fCallbacks.BinarySearchByKey(token, &_CallbackCompare); 267} 268 269 270int 271HTML5HWInterface::_CallbackCompare(const uint32* key, 272 const callback_info* info) 273{ 274 if (info->token == *key) 275 return 0; 276 277 if (info->token < *key) 278 return -1; 279 280 return 1; 281} 282 283 284int32 285HTML5HWInterface::_EventThreadEntry(void* data) 286{ 287 return ((HTML5HWInterface*)data)->_EventThread(); 288} 289 290 291status_t 292HTML5HWInterface::_EventThread() 293{ 294 CanvasMessage message(fReceiveBuffer, fSendBuffer); 295 while (true) { 296 uint16 code; 297 status_t result = message.NextMessage(code); 298 if (result != B_OK) { 299 TRACE_ERROR("failed to read message from receiver: %s\n", 300 strerror(result)); 301 return result; 302 } 303 304 TRACE("got message code %u with %lu bytes\n", code, message.DataLeft()); 305 306 if (code >= RP_MOUSE_MOVED && code <= RP_MODIFIERS_CHANGED) { 307 // an input event, dispatch to the event stream 308 if (fEventStream->EventReceived(message)) 309 continue; 310 } 311 312 switch (code) { 313 case RP_UPDATE_DISPLAY_MODE: 314 { 315 // TODO: implement, we only handle it in the context of the 316 // initial mode setup on connect 317 break; 318 } 319 320 default: 321 { 322 uint32 token; 323 if (message.Read(token) == B_OK) { 324 callback_info* info = _FindCallback(token); 325 if (info != NULL && info->callback(info->cookie, message)) 326 break; 327 } 328 329 TRACE_ERROR("unhandled HTML5 event code %u\n", code); 330 break; 331 } 332 } 333 } 334} 335 336 337status_t 338HTML5HWInterface::_Connect() 339{ 340#if 0 341 TRACE("connecting to host \"%s\" port %lu\n", fRemoteHost, fRemotePort); 342 status_t result = fSendEndpoint->Connect(fRemoteHost, (uint16)fRemotePort); 343 if (result != B_OK) { 344 TRACE_ERROR("failed to connect to host \"%s\" port %lu\n", fRemoteHost, 345 fRemotePort); 346 return result; 347 } 348 349 CanvasMessage message(fReceiveBuffer, fSendBuffer); 350 message.Start(RP_INIT_CONNECTION); 351 message.Add(fListenPort); 352 result = message.Flush(); 353 if (result != B_OK) { 354 TRACE_ERROR("failed to send init connection message\n"); 355 return result; 356 } 357 358 uint16 code; 359 result = message.NextMessage(code); 360 if (result != B_OK) { 361 TRACE_ERROR("failed to read message from receiver: %s\n", 362 strerror(result)); 363 return result; 364 } 365 366 TRACE("code %u with %lu bytes of data\n", code, message.DataLeft()); 367 if (code != RP_UPDATE_DISPLAY_MODE) { 368 TRACE_ERROR("invalid connection init code %u\n", code); 369 return B_ERROR; 370 } 371 372 int32 width, height; 373 message.Read(width); 374 result = message.Read(height); 375 if (result != B_OK) { 376 TRACE_ERROR("failed to get initial display mode\n"); 377 return result; 378 } 379 380 fDisplayMode.virtual_width = width; 381 fDisplayMode.virtual_height = height; 382#endif 383 return B_OK; 384} 385 386 387void 388HTML5HWInterface::_Disconnect() 389{ 390 if (fIsConnected) { 391 CanvasMessage message(NULL, fSendBuffer); 392 message.Start(RP_CLOSE_CONNECTION); 393 message.Flush(); 394 fIsConnected = false; 395 } 396 397 if (fReceiveEndpoint != NULL) 398 fReceiveEndpoint->Close(); 399} 400 401 402status_t 403HTML5HWInterface::SetMode(const display_mode& mode) 404{ 405 // The display mode depends on the screen resolution of the client, we 406 // don't allow to change it. 407 return B_UNSUPPORTED; 408} 409 410 411void 412HTML5HWInterface::GetMode(display_mode* mode) 413{ 414 if (mode == NULL || !ReadLock()) 415 return; 416 417 *mode = fDisplayMode; 418 ReadUnlock(); 419} 420 421 422status_t 423HTML5HWInterface::GetDeviceInfo(accelerant_device_info* info) 424{ 425 if (!ReadLock()) 426 return B_ERROR; 427 428 info->version = fProtocolVersion; 429 info->dac_speed = fConnectionSpeed; 430 info->memory = 33554432; // 32MB 431 snprintf(info->name, sizeof(info->name), "Haiku, Inc. HTML5HWInterface"); 432 snprintf(info->chipset, sizeof(info->chipset), "Haiku, Inc. Chipset"); 433 snprintf(info->serial_no, sizeof(info->serial_no), fTarget); 434 435 ReadUnlock(); 436 return B_OK; 437} 438 439 440status_t 441HTML5HWInterface::GetFrameBufferConfig(frame_buffer_config& config) 442{ 443 // We don't actually have a frame buffer. 444 return B_UNSUPPORTED; 445} 446 447 448status_t 449HTML5HWInterface::GetModeList(display_mode** _modes, uint32* _count) 450{ 451 AutoReadLocker _(this); 452 453 display_mode* modes = new(std::nothrow) display_mode[1]; 454 if (modes == NULL) 455 return B_NO_MEMORY; 456 457 modes[0] = fDisplayMode; 458 *_modes = modes; 459 *_count = 1; 460 return B_OK; 461} 462 463 464status_t 465HTML5HWInterface::GetPixelClockLimits(display_mode* mode, uint32* low, 466 uint32* high) 467{ 468 return B_UNSUPPORTED; 469} 470 471 472status_t 473HTML5HWInterface::GetTimingConstraints(display_timing_constraints* constraints) 474{ 475 return B_UNSUPPORTED; 476} 477 478 479status_t 480HTML5HWInterface::ProposeMode(display_mode* candidate, const display_mode* low, 481 const display_mode* high) 482{ 483 return B_UNSUPPORTED; 484} 485 486 487status_t 488HTML5HWInterface::SetDPMSMode(uint32 state) 489{ 490 return B_UNSUPPORTED; 491} 492 493 494uint32 495HTML5HWInterface::DPMSMode() 496{ 497 return (IsConnected() ? B_DPMS_ON : B_DPMS_SUSPEND); 498} 499 500 501uint32 502HTML5HWInterface::DPMSCapabilities() 503{ 504 return B_DPMS_ON | B_DPMS_SUSPEND; 505} 506 507 508sem_id 509HTML5HWInterface::RetraceSemaphore() 510{ 511 return -1; 512} 513 514 515status_t 516HTML5HWInterface::WaitForRetrace(bigtime_t timeout) 517{ 518 return B_UNSUPPORTED; 519} 520 521 522void 523HTML5HWInterface::SetCursor(ServerCursor* cursor) 524{ 525 HWInterface::SetCursor(cursor); 526 if (!IsConnected()) 527 return; 528 CanvasMessage message(NULL, fSendBuffer); 529 message.Start(RP_SET_CURSOR); 530 message.AddCursor(Cursor().Get()); 531} 532 533 534void 535HTML5HWInterface::SetCursorVisible(bool visible) 536{ 537 HWInterface::SetCursorVisible(visible); 538 if (!IsConnected()) 539 return; 540 CanvasMessage message(NULL, fSendBuffer); 541 message.Start(RP_SET_CURSOR_VISIBLE); 542 message.Add(visible); 543} 544 545 546void 547HTML5HWInterface::MoveCursorTo(float x, float y) 548{ 549 HWInterface::MoveCursorTo(x, y); 550 if (!IsConnected()) 551 return; 552 CanvasMessage message(NULL, fSendBuffer); 553 message.Start(RP_MOVE_CURSOR_TO); 554 message.Add(x); 555 message.Add(y); 556} 557 558 559void 560HTML5HWInterface::SetDragBitmap(const ServerBitmap* bitmap, 561 const BPoint& offsetFromCursor) 562{ 563 HWInterface::SetDragBitmap(bitmap, offsetFromCursor); 564 if (!IsConnected()) 565 return; 566 CanvasMessage message(NULL, fSendBuffer); 567 message.Start(RP_SET_CURSOR); 568 message.AddCursor(CursorAndDragBitmap().Get()); 569} 570 571 572RenderingBuffer* 573HTML5HWInterface::FrontBuffer() const 574{ 575 return NULL; 576} 577 578 579RenderingBuffer* 580HTML5HWInterface::BackBuffer() const 581{ 582 return NULL; 583} 584 585 586bool 587HTML5HWInterface::IsDoubleBuffered() const 588{ 589 return false; 590} 591 592 593status_t 594HTML5HWInterface::InvalidateRegion(BRegion& region) 595{ 596 CanvasMessage message(NULL, fSendBuffer); 597 if (!IsConnected()) 598 return B_OK; 599 message.Start(RP_INVALIDATE_REGION); 600 message.AddRegion(region); 601 return B_OK; 602} 603 604 605status_t 606HTML5HWInterface::Invalidate(const BRect& frame) 607{ 608 CanvasMessage message(NULL, fSendBuffer); 609 if (!IsConnected()) 610 return B_OK; 611 message.Start(RP_INVALIDATE_RECT); 612 message.Add(frame); 613 return B_OK; 614} 615 616 617status_t 618HTML5HWInterface::CopyBackToFront(const BRect& frame) 619{ 620 return B_OK; 621} 622