1 /* 2 * Copyright 2003-2009, Haiku Inc. 3 * Authors: 4 * Stefano Ceccherini <stefano.ceccherini@gmail.com> 5 * Carwyn Jones <turok2@currantbun.com> 6 * 7 * Distributed under the terms of the MIT License. 8 */ 9 10 11 #include <DirectWindow.h> 12 13 #include <stdio.h> 14 #include <string.h> 15 16 #include <Screen.h> 17 18 #include <clipping.h> 19 #include <AppServerLink.h> 20 #include <DirectWindowPrivate.h> 21 #include <ServerProtocol.h> 22 23 24 //#define DEBUG 1 25 #define OUTPUT printf 26 //#define OUTPUT debug_printf 27 28 29 // We don't need this kind of locking, since the directDaemonFunc 30 // doesn't access critical shared data. 31 #define DW_NEEDS_LOCKING 0 32 33 enum dw_status_bits { 34 DW_STATUS_AREA_CLONED = 0x1, 35 DW_STATUS_THREAD_STARTED = 0x2, 36 DW_STATUS_SEM_CREATED = 0x4 37 }; 38 39 40 #if DEBUG 41 42 43 static void 44 print_direct_buffer_state(const direct_buffer_state &state) 45 { 46 char string[128]; 47 int modeState = state & B_DIRECT_MODE_MASK; 48 if (modeState == B_DIRECT_START) 49 strcpy(string, "B_DIRECT_START"); 50 else if (modeState == B_DIRECT_MODIFY) 51 strcpy(string, "B_DIRECT_MODIFY"); 52 else if (modeState == B_DIRECT_STOP) 53 strcpy(string, "B_DIRECT_STOP"); 54 55 if (state & B_CLIPPING_MODIFIED) 56 strcat(string, " | B_CLIPPING_MODIFIED"); 57 if (state & B_BUFFER_RESIZED) 58 strcat(string, " | B_BUFFER_RESIZED"); 59 if (state & B_BUFFER_MOVED) 60 strcat(string, " | B_BUFFER_MOVED"); 61 if (state & B_BUFFER_RESET) 62 strcat(string, " | B_BUFFER_RESET"); 63 64 OUTPUT("direct_buffer_state: %s\n", string); 65 } 66 67 68 static void 69 print_direct_driver_state(const direct_driver_state &state) 70 { 71 if (state == 0) 72 return; 73 74 char string[64]; 75 if (state == B_DRIVER_CHANGED) 76 strcpy(string, "B_DRIVER_CHANGED"); 77 else if (state == B_MODE_CHANGED) 78 strcpy(string, "B_MODE_CHANGED"); 79 80 OUTPUT("direct_driver_state: %s\n", string); 81 } 82 83 84 #if DEBUG > 1 85 86 87 static void 88 print_direct_buffer_layout(const buffer_layout &layout) 89 { 90 char string[64]; 91 if (layout == B_BUFFER_NONINTERLEAVED) 92 strcpy(string, "B_BUFFER_NONINTERLEAVED"); 93 else 94 strcpy(string, "unknown"); 95 96 OUTPUT("layout: %s\n", string); 97 } 98 99 100 static void 101 print_direct_buffer_orientation(const buffer_orientation &orientation) 102 { 103 char string[64]; 104 switch (orientation) { 105 case B_BUFFER_TOP_TO_BOTTOM: 106 strcpy(string, "B_BUFFER_TOP_TO_BOTTOM"); 107 break; 108 case B_BUFFER_BOTTOM_TO_TOP: 109 strcpy(string, "B_BUFFER_BOTTOM_TO_TOP"); 110 break; 111 default: 112 strcpy(string, "unknown"); 113 break; 114 } 115 116 OUTPUT("orientation: %s\n", string); 117 } 118 119 120 #endif // DEBUG > 2 121 122 123 static void 124 print_direct_buffer_info(const direct_buffer_info &info) 125 { 126 print_direct_buffer_state(info.buffer_state); 127 print_direct_driver_state(info.driver_state); 128 129 # if DEBUG > 1 130 OUTPUT("bits: %p\n", info.bits); 131 OUTPUT("pci_bits: %p\n", info.pci_bits); 132 OUTPUT("bytes_per_row: %ld\n", info.bytes_per_row); 133 OUTPUT("bits_per_pixel: %lu\n", info.bits_per_pixel); 134 OUTPUT("pixel_format: %d\n", info.pixel_format); 135 print_direct_buffer_layout(info.layout); 136 print_direct_buffer_orientation(info.orientation); 137 138 # if DEBUG > 2 139 // TODO: this won't work correctly with debug_printf() 140 printf("CLIPPING INFO:\n"); 141 printf("clipping_rects count: %ld\n", info.clip_list_count); 142 143 printf("- window_bounds:\n"); 144 BRegion region; 145 region.Set(info.window_bounds); 146 region.PrintToStream(); 147 148 region.MakeEmpty(); 149 for (uint32 i = 0; i < info.clip_list_count; i++) 150 region.Include(info.clip_list[i]); 151 152 printf("- clip_list:\n"); 153 region.PrintToStream(); 154 # endif 155 # endif 156 157 OUTPUT("\n"); 158 } 159 160 161 #endif // DEBUG 162 163 164 // #pragma mark - 165 166 167 BDirectWindow::BDirectWindow(BRect frame, const char *title, window_type type, 168 uint32 flags, uint32 workspace) 169 : 170 BWindow(frame, title, type, flags, workspace) 171 { 172 _InitData(); 173 } 174 175 176 BDirectWindow::BDirectWindow(BRect frame, const char *title, window_look look, 177 window_feel feel, uint32 flags, uint32 workspace) 178 : 179 BWindow(frame, title, look, feel, flags, workspace) 180 { 181 _InitData(); 182 } 183 184 185 BDirectWindow::~BDirectWindow() 186 { 187 _DisposeData(); 188 } 189 190 191 // #pragma mark - BWindow API implementation 192 193 194 BArchivable * 195 BDirectWindow::Instantiate(BMessage *data) 196 { 197 return NULL; 198 } 199 200 201 status_t 202 BDirectWindow::Archive(BMessage *data, bool deep) const 203 { 204 return inherited::Archive(data, deep); 205 } 206 207 208 void 209 BDirectWindow::Quit() 210 { 211 inherited::Quit(); 212 } 213 214 215 void 216 BDirectWindow::DispatchMessage(BMessage *message, BHandler *handler) 217 { 218 inherited::DispatchMessage(message, handler); 219 } 220 221 222 void 223 BDirectWindow::MessageReceived(BMessage *message) 224 { 225 inherited::MessageReceived(message); 226 } 227 228 229 void 230 BDirectWindow::FrameMoved(BPoint newPosition) 231 { 232 inherited::FrameMoved(newPosition); 233 } 234 235 236 void 237 BDirectWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 238 { 239 inherited::WorkspacesChanged(oldWorkspaces, newWorkspaces); 240 } 241 242 243 void 244 BDirectWindow::WorkspaceActivated(int32 index, bool state) 245 { 246 inherited::WorkspaceActivated(index, state); 247 } 248 249 250 void 251 BDirectWindow::FrameResized(float newWidth, float newHeight) 252 { 253 inherited::FrameResized(newWidth, newHeight); 254 } 255 256 257 void 258 BDirectWindow::Minimize(bool minimize) 259 { 260 inherited::Minimize(minimize); 261 } 262 263 264 void 265 BDirectWindow::Zoom(BPoint recPosition, float recWidth, float recHeight) 266 { 267 inherited::Zoom(recPosition, recWidth, recHeight); 268 } 269 270 271 void 272 BDirectWindow::ScreenChanged(BRect screenFrame, color_space depth) 273 { 274 inherited::ScreenChanged(screenFrame, depth); 275 } 276 277 278 void 279 BDirectWindow::MenusBeginning() 280 { 281 inherited::MenusBeginning(); 282 } 283 284 285 void 286 BDirectWindow::MenusEnded() 287 { 288 inherited::MenusEnded(); 289 } 290 291 292 void 293 BDirectWindow::WindowActivated(bool state) 294 { 295 inherited::WindowActivated(state); 296 } 297 298 299 void 300 BDirectWindow::Show() 301 { 302 inherited::Show(); 303 } 304 305 306 void 307 BDirectWindow::Hide() 308 { 309 inherited::Hide(); 310 } 311 312 313 BHandler * 314 BDirectWindow::ResolveSpecifier(BMessage *msg, int32 index, 315 BMessage *specifier, int32 form, const char *property) 316 { 317 return inherited::ResolveSpecifier(msg, index, specifier, form, property); 318 } 319 320 321 status_t 322 BDirectWindow::GetSupportedSuites(BMessage *data) 323 { 324 return inherited::GetSupportedSuites(data); 325 } 326 327 328 status_t 329 BDirectWindow::Perform(perform_code d, void *arg) 330 { 331 return inherited::Perform(d, arg); 332 } 333 334 335 void 336 BDirectWindow::task_looper() 337 { 338 inherited::task_looper(); 339 } 340 341 342 BMessage * 343 BDirectWindow::ConvertToMessage(void *raw, int32 code) 344 { 345 return inherited::ConvertToMessage(raw, code); 346 } 347 348 349 // #pragma mark - BDirectWindow specific API 350 351 352 void 353 BDirectWindow::DirectConnected(direct_buffer_info *info) 354 { 355 // implemented in subclasses 356 } 357 358 359 status_t 360 BDirectWindow::GetClippingRegion(BRegion *region, BPoint *origin) const 361 { 362 if (region == NULL) 363 return B_BAD_VALUE; 364 365 if (IsLocked() || !_LockDirect()) 366 return B_ERROR; 367 368 if (fInDirectConnect) { 369 _UnlockDirect(); 370 return B_ERROR; 371 } 372 373 // BPoint's coordinates are floats. We can only work 374 // with integers._DaemonStarter 375 int32 originX, originY; 376 if (origin == NULL) { 377 originX = 0; 378 originY = 0; 379 } else { 380 originX = (int32)origin->x; 381 originY = (int32)origin->y; 382 } 383 384 #ifndef HAIKU_TARGET_PLATFORM_DANO 385 // Since we are friend of BRegion, we can access its private members. 386 // Otherwise, we would need to call BRegion::Include(clipping_rect) 387 // for every clipping_rect in our clip_list, and that would be much 388 // more overkill than this (tested ). 389 if (!region->_SetSize(fBufferDesc->clip_list_count)) { 390 _UnlockDirect(); 391 return B_NO_MEMORY; 392 } 393 region->fCount = fBufferDesc->clip_list_count; 394 region->fBounds = region->_ConvertToInternal(fBufferDesc->clip_bounds); 395 for (uint32 c = 0; c < fBufferDesc->clip_list_count; c++) { 396 region->fData[c] = region->_ConvertToInternal( 397 fBufferDesc->clip_list[c]); 398 } 399 400 // adjust bounds by the given origin point 401 region->OffsetBy(-originX, -originY); 402 #endif 403 404 _UnlockDirect(); 405 406 return B_OK; 407 408 } 409 410 411 status_t 412 BDirectWindow::SetFullScreen(bool enable) 413 { 414 if (fIsFullScreen == enable) 415 return B_OK; 416 417 status_t status = B_ERROR; 418 if (Lock()) { 419 fLink->StartMessage(AS_DIRECT_WINDOW_SET_FULLSCREEN); 420 fLink->Attach<bool>(enable); 421 422 if (fLink->FlushWithReply(status) == B_OK 423 && status == B_OK) { 424 fIsFullScreen = enable; 425 } 426 Unlock(); 427 } 428 return status; 429 } 430 431 432 bool 433 BDirectWindow::IsFullScreen() const 434 { 435 return fIsFullScreen; 436 } 437 438 439 /*static*/ bool 440 BDirectWindow::SupportsWindowMode(screen_id id) 441 { 442 display_mode mode; 443 status_t status = BScreen(id).GetMode(&mode); 444 if (status == B_OK) 445 return (mode.flags & B_PARALLEL_ACCESS) != 0; 446 447 return false; 448 } 449 450 451 // #pragma mark - Private methods 452 453 454 /*static*/ int32 455 BDirectWindow::_daemon_thread(void *arg) 456 { 457 return static_cast<BDirectWindow *>(arg)->_DirectDaemon(); 458 } 459 460 461 int32 462 BDirectWindow::_DirectDaemon() 463 { 464 while (!fDaemonKiller) { 465 // This sem is released by the app_server when our 466 // clipping region changes, or when our window is moved, 467 // resized, etc. etc. 468 status_t status; 469 do { 470 status = acquire_sem(fDisableSem); 471 } while (status == B_INTERRUPTED); 472 473 if (status != B_OK) { 474 //fprintf(stderr, "DirectDaemon: failed to acquire direct sem: %s\n", 475 // strerror(status)); 476 return -1; 477 } 478 479 #if DEBUG 480 print_direct_buffer_info(*fBufferDesc); 481 #endif 482 483 if (_LockDirect()) { 484 if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK) 485 == B_DIRECT_START) 486 fConnectionEnable = true; 487 488 fInDirectConnect = true; 489 DirectConnected(fBufferDesc); 490 fInDirectConnect = false; 491 492 if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK) 493 == B_DIRECT_STOP) 494 fConnectionEnable = false; 495 496 _UnlockDirect(); 497 } 498 499 // The app_server then waits (with a timeout) on this sem. 500 // If we aren't quick enough to release this sem, our app 501 // will be terminated by the app_server 502 if ((status = release_sem(fDisableSemAck)) != B_OK) { 503 //fprintf(stderr, "DirectDaemon: failed to release sem: %s\n", 504 //strerror(status)); 505 return -1; 506 } 507 } 508 509 return 0; 510 } 511 512 513 bool 514 BDirectWindow::_LockDirect() const 515 { 516 // LockDirect() and UnlockDirect() are no-op on BeOS. I tried to call BeOS's 517 // version repeatedly, from the same thread and from different threads, 518 // nothing happened. 519 // They're not needed though, as the direct_daemon_thread doesn't change 520 // any shared data. They are probably here for future enhancements. 521 status_t status = B_OK; 522 523 #if DW_NEEDS_LOCKING 524 BDirectWindow *casted = const_cast<BDirectWindow *>(this); 525 526 if (atomic_add(&casted->fDirectLock, 1) > 0) { 527 do { 528 status = acquire_sem(casted->fDirectSem); 529 } while (status == B_INTERRUPTED); 530 } 531 532 if (status == B_OK) { 533 casted->fDirectLockOwner = find_thread(NULL); 534 casted->fDirectLockCount++; 535 } 536 #endif 537 538 return status == B_OK; 539 } 540 541 542 void 543 BDirectWindow::_UnlockDirect() const 544 { 545 #if DW_NEEDS_LOCKING 546 BDirectWindow *casted = const_cast<BDirectWindow *>(this); 547 548 if (atomic_add(&casted->fDirectLock, -1) > 1) 549 release_sem(casted->fDirectSem); 550 551 casted->fDirectLockCount--; 552 #endif 553 } 554 555 556 void 557 BDirectWindow::_InitData() 558 { 559 fConnectionEnable = false; 560 fIsFullScreen = false; 561 fInDirectConnect = false; 562 563 fInitStatus = 0; 564 565 status_t status = B_ERROR; 566 struct direct_window_sync_data syncData; 567 568 fLink->StartMessage(AS_DIRECT_WINDOW_GET_SYNC_DATA); 569 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 570 fLink->Read<direct_window_sync_data>(&syncData); 571 572 if (status != B_OK) 573 return; 574 575 #if DW_NEEDS_LOCKING 576 fDirectLock = 0; 577 fDirectLockCount = 0; 578 fDirectLockOwner = -1; 579 fDirectLockStack = NULL; 580 fDirectSem = create_sem(0, "direct sem"); 581 if (fDirectSem > 0) 582 fInitStatus |= DW_STATUS_SEM_CREATED; 583 #endif 584 585 fSourceClippingArea = syncData.area; 586 fDisableSem = syncData.disable_sem; 587 fDisableSemAck = syncData.disable_sem_ack; 588 589 fClonedClippingArea = clone_area("cloned direct area", (void**)&fBufferDesc, 590 B_ANY_ADDRESS, B_READ_AREA, fSourceClippingArea); 591 592 if (fClonedClippingArea > 0) { 593 fInitStatus |= DW_STATUS_AREA_CLONED; 594 595 fDirectDaemonId = spawn_thread(_daemon_thread, "direct daemon", 596 B_DISPLAY_PRIORITY, this); 597 598 if (fDirectDaemonId > 0) { 599 fDaemonKiller = false; 600 if (resume_thread(fDirectDaemonId) == B_OK) 601 fInitStatus |= DW_STATUS_THREAD_STARTED; 602 else 603 kill_thread(fDirectDaemonId); 604 } 605 } 606 } 607 608 609 void 610 BDirectWindow::_DisposeData() 611 { 612 // wait until the connection terminates: we can't destroy 613 // the object until the client receives the B_DIRECT_STOP 614 // notification, or bad things will happen 615 while (fConnectionEnable) 616 snooze(50000); 617 618 _LockDirect(); 619 620 if (fInitStatus & DW_STATUS_THREAD_STARTED) { 621 fDaemonKiller = true; 622 // delete this sem, otherwise the Direct daemon thread 623 // will wait forever on it 624 delete_sem(fDisableSem); 625 status_t retVal; 626 wait_for_thread(fDirectDaemonId, &retVal); 627 } 628 629 #if DW_NEEDS_LOCKING 630 if (fInitStatus & DW_STATUS_SEM_CREATED) 631 delete_sem(fDirectSem); 632 #endif 633 634 if (fInitStatus & DW_STATUS_AREA_CLONED) 635 delete_area(fClonedClippingArea); 636 } 637 638 639 void BDirectWindow::_ReservedDirectWindow1() {} 640 void BDirectWindow::_ReservedDirectWindow2() {} 641 void BDirectWindow::_ReservedDirectWindow3() {} 642 void BDirectWindow::_ReservedDirectWindow4() {} 643