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