1/* 2 * Copyright 2003-2009 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stefano Ceccherini <stefano.ceccherini@gmail.com> 7 * Carwyn Jones <turok2@currantbun.com> 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 33enum 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 43static void 44print_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 68static void 69print_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 87static void 88print_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 100static void 101print_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 123static void 124print_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 167BDirectWindow::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 176BDirectWindow::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 185BDirectWindow::~BDirectWindow() 186{ 187 _DisposeData(); 188} 189 190 191// #pragma mark - BWindow API implementation 192 193 194BArchivable* 195BDirectWindow::Instantiate(BMessage* data) 196{ 197 return NULL; 198} 199 200 201status_t 202BDirectWindow::Archive(BMessage* data, bool deep) const 203{ 204 return inherited::Archive(data, deep); 205} 206 207 208void 209BDirectWindow::Quit() 210{ 211 inherited::Quit(); 212} 213 214 215void 216BDirectWindow::DispatchMessage(BMessage* message, BHandler* handler) 217{ 218 inherited::DispatchMessage(message, handler); 219} 220 221 222void 223BDirectWindow::MessageReceived(BMessage* message) 224{ 225 inherited::MessageReceived(message); 226} 227 228 229void 230BDirectWindow::FrameMoved(BPoint newPosition) 231{ 232 inherited::FrameMoved(newPosition); 233} 234 235 236void 237BDirectWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 238{ 239 inherited::WorkspacesChanged(oldWorkspaces, newWorkspaces); 240} 241 242 243void 244BDirectWindow::WorkspaceActivated(int32 index, bool state) 245{ 246 inherited::WorkspaceActivated(index, state); 247} 248 249 250void 251BDirectWindow::FrameResized(float newWidth, float newHeight) 252{ 253 inherited::FrameResized(newWidth, newHeight); 254} 255 256 257void 258BDirectWindow::Minimize(bool minimize) 259{ 260 inherited::Minimize(minimize); 261} 262 263 264void 265BDirectWindow::Zoom(BPoint recPosition, float recWidth, float recHeight) 266{ 267 inherited::Zoom(recPosition, recWidth, recHeight); 268} 269 270 271void 272BDirectWindow::ScreenChanged(BRect screenFrame, color_space depth) 273{ 274 inherited::ScreenChanged(screenFrame, depth); 275} 276 277 278void 279BDirectWindow::MenusBeginning() 280{ 281 inherited::MenusBeginning(); 282} 283 284 285void 286BDirectWindow::MenusEnded() 287{ 288 inherited::MenusEnded(); 289} 290 291 292void 293BDirectWindow::WindowActivated(bool state) 294{ 295 inherited::WindowActivated(state); 296} 297 298 299void 300BDirectWindow::Show() 301{ 302 inherited::Show(); 303} 304 305 306void 307BDirectWindow::Hide() 308{ 309 inherited::Hide(); 310} 311 312 313BHandler* 314BDirectWindow::ResolveSpecifier(BMessage* message, int32 index, 315 BMessage* specifier, int32 what, const char* property) 316{ 317 return inherited::ResolveSpecifier(message, index, specifier, what, 318 property); 319} 320 321 322status_t 323BDirectWindow::GetSupportedSuites(BMessage* data) 324{ 325 return inherited::GetSupportedSuites(data); 326} 327 328 329status_t 330BDirectWindow::Perform(perform_code d, void* arg) 331{ 332 return inherited::Perform(d, arg); 333} 334 335 336void 337BDirectWindow::task_looper() 338{ 339 inherited::task_looper(); 340} 341 342 343BMessage* 344BDirectWindow::ConvertToMessage(void* raw, int32 code) 345{ 346 return inherited::ConvertToMessage(raw, code); 347} 348 349 350// #pragma mark - BDirectWindow specific API 351 352 353void 354BDirectWindow::DirectConnected(direct_buffer_info* info) 355{ 356 // implemented in subclasses 357} 358 359 360status_t 361BDirectWindow::GetClippingRegion(BRegion* region, BPoint* origin) const 362{ 363 if (region == NULL) 364 return B_BAD_VALUE; 365 366 if (IsLocked() || !_LockDirect()) 367 return B_ERROR; 368 369 if (!fInDirectConnect) { 370 _UnlockDirect(); 371 return B_ERROR; 372 } 373 374 // BPoint's coordinates are floats. We can only work 375 // with integers._DaemonStarter 376 int32 originX, originY; 377 if (origin == NULL) { 378 originX = 0; 379 originY = 0; 380 } else { 381 originX = (int32)origin->x; 382 originY = (int32)origin->y; 383 } 384 385#ifndef HAIKU_TARGET_PLATFORM_DANO 386 // Since we are friend of BRegion, we can access its private members. 387 // Otherwise, we would need to call BRegion::Include(clipping_rect) 388 // for every clipping_rect in our clip_list, and that would be much 389 // more overkill than this (tested ). 390 if (!region->_SetSize(fBufferDesc->clip_list_count)) { 391 _UnlockDirect(); 392 return B_NO_MEMORY; 393 } 394 region->fCount = fBufferDesc->clip_list_count; 395 region->fBounds = region->_ConvertToInternal(fBufferDesc->clip_bounds); 396 for (uint32 c = 0; c < fBufferDesc->clip_list_count; c++) { 397 region->fData[c] = region->_ConvertToInternal( 398 fBufferDesc->clip_list[c]); 399 } 400 401 // adjust bounds by the given origin point 402 region->OffsetBy(-originX, -originY); 403#endif 404 405 _UnlockDirect(); 406 407 return B_OK; 408 409} 410 411 412status_t 413BDirectWindow::SetFullScreen(bool enable) 414{ 415 if (fIsFullScreen == enable) 416 return B_OK; 417 418 status_t status = B_ERROR; 419 if (Lock()) { 420 fLink->StartMessage(AS_DIRECT_WINDOW_SET_FULLSCREEN); 421 fLink->Attach<bool>(enable); 422 423 if (fLink->FlushWithReply(status) == B_OK 424 && status == B_OK) { 425 fIsFullScreen = enable; 426 } 427 Unlock(); 428 } 429 return status; 430} 431 432 433bool 434BDirectWindow::IsFullScreen() const 435{ 436 return fIsFullScreen; 437} 438 439 440/*static*/ bool 441BDirectWindow::SupportsWindowMode(screen_id id) 442{ 443 display_mode mode; 444 status_t status = BScreen(id).GetMode(&mode); 445 if (status == B_OK) 446 return (mode.flags & B_PARALLEL_ACCESS) != 0; 447 448 return false; 449} 450 451 452// #pragma mark - Private methods 453 454 455/*static*/ int32 456BDirectWindow::_daemon_thread(void* arg) 457{ 458 return static_cast<BDirectWindow*>(arg)->_DirectDaemon(); 459} 460 461 462int32 463BDirectWindow::_DirectDaemon() 464{ 465 while (!fDaemonKiller) { 466 // This sem is released by the app_server when our 467 // clipping region changes, or when our window is moved, 468 // resized, etc. etc. 469 status_t status; 470 do { 471 status = acquire_sem(fDisableSem); 472 } while (status == B_INTERRUPTED); 473 474 if (status != B_OK) { 475 //fprintf(stderr, "DirectDaemon: failed to acquire direct sem: %s\n", 476 // strerror(status)); 477 return -1; 478 } 479 480#if DEBUG 481 print_direct_buffer_info(*fBufferDesc); 482#endif 483 484 if (_LockDirect()) { 485 if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK) 486 == B_DIRECT_START) 487 fConnectionEnable = true; 488 489 fInDirectConnect = true; 490 DirectConnected(fBufferDesc); 491 fInDirectConnect = false; 492 493 if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK) 494 == B_DIRECT_STOP) 495 fConnectionEnable = false; 496 497 _UnlockDirect(); 498 } 499 500 // The app_server then waits (with a timeout) on this sem. 501 // If we aren't quick enough to release this sem, our app 502 // will be terminated by the app_server 503 if ((status = release_sem(fDisableSemAck)) != B_OK) { 504 //fprintf(stderr, "DirectDaemon: failed to release sem: %s\n", 505 //strerror(status)); 506 return -1; 507 } 508 } 509 510 return 0; 511} 512 513 514bool 515BDirectWindow::_LockDirect() const 516{ 517 // LockDirect() and UnlockDirect() are no-op on BeOS. I tried to call BeOS's 518 // version repeatedly, from the same thread and from different threads, 519 // nothing happened. 520 // They're not needed though, as the direct_daemon_thread doesn't change 521 // any shared data. They are probably here for future enhancements. 522 status_t status = B_OK; 523 524#if DW_NEEDS_LOCKING 525 BDirectWindow* casted = const_cast<BDirectWindow*>(this); 526 527 if (atomic_add(&casted->fDirectLock, 1) > 0) { 528 do { 529 status = acquire_sem(casted->fDirectSem); 530 } while (status == B_INTERRUPTED); 531 } 532 533 if (status == B_OK) { 534 casted->fDirectLockOwner = find_thread(NULL); 535 casted->fDirectLockCount++; 536 } 537#endif 538 539 return status == B_OK; 540} 541 542 543void 544BDirectWindow::_UnlockDirect() const 545{ 546#if DW_NEEDS_LOCKING 547 BDirectWindow* casted = const_cast<BDirectWindow*>(this); 548 549 if (atomic_add(&casted->fDirectLock, -1) > 1) 550 release_sem(casted->fDirectSem); 551 552 casted->fDirectLockCount--; 553#endif 554} 555 556 557void 558BDirectWindow::_InitData() 559{ 560 fConnectionEnable = false; 561 fIsFullScreen = false; 562 fInDirectConnect = false; 563 564 fInitStatus = 0; 565 566 status_t status = B_ERROR; 567 struct direct_window_sync_data syncData; 568 569 fLink->StartMessage(AS_DIRECT_WINDOW_GET_SYNC_DATA); 570 if (fLink->FlushWithReply(status) == B_OK && status == B_OK) 571 fLink->Read<direct_window_sync_data>(&syncData); 572 573 if (status != B_OK) 574 return; 575 576#if DW_NEEDS_LOCKING 577 fDirectLock = 0; 578 fDirectLockCount = 0; 579 fDirectLockOwner = -1; 580 fDirectLockStack = NULL; 581 fDirectSem = create_sem(0, "direct sem"); 582 if (fDirectSem > 0) 583 fInitStatus |= DW_STATUS_SEM_CREATED; 584#endif 585 586 fSourceClippingArea = syncData.area; 587 fDisableSem = syncData.disable_sem; 588 fDisableSemAck = syncData.disable_sem_ack; 589 590 fClonedClippingArea = clone_area("cloned direct area", (void**)&fBufferDesc, 591 B_ANY_ADDRESS, B_READ_AREA, fSourceClippingArea); 592 593 if (fClonedClippingArea > 0) { 594 fInitStatus |= DW_STATUS_AREA_CLONED; 595 596 fDirectDaemonId = spawn_thread(_daemon_thread, "direct daemon", 597 B_DISPLAY_PRIORITY, this); 598 599 if (fDirectDaemonId > 0) { 600 fDaemonKiller = false; 601 if (resume_thread(fDirectDaemonId) == B_OK) 602 fInitStatus |= DW_STATUS_THREAD_STARTED; 603 else 604 kill_thread(fDirectDaemonId); 605 } 606 } 607} 608 609 610void 611BDirectWindow::_DisposeData() 612{ 613 // wait until the connection terminates: we can't destroy 614 // the object until the client receives the B_DIRECT_STOP 615 // notification, or bad things will happen 616 while (fConnectionEnable) 617 snooze(50000); 618 619 _LockDirect(); 620 621 if (fInitStatus & DW_STATUS_THREAD_STARTED) { 622 fDaemonKiller = true; 623 // delete this sem, otherwise the Direct daemon thread 624 // will wait forever on it 625 delete_sem(fDisableSem); 626 status_t retVal; 627 wait_for_thread(fDirectDaemonId, &retVal); 628 } 629 630#if DW_NEEDS_LOCKING 631 if (fInitStatus & DW_STATUS_SEM_CREATED) 632 delete_sem(fDirectSem); 633#endif 634 635 if (fInitStatus & DW_STATUS_AREA_CLONED) 636 delete_area(fClonedClippingArea); 637} 638 639 640void BDirectWindow::_ReservedDirectWindow1() {} 641void BDirectWindow::_ReservedDirectWindow2() {} 642void BDirectWindow::_ReservedDirectWindow3() {} 643void BDirectWindow::_ReservedDirectWindow4() {} 644