1 /* 2 * Copyright 2002-2009, Haiku Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stefano Ceccherini (burton666@libero.it) 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 11 /*! BPrivateScreen is the class which does the real work for 12 the proxy class BScreen (it interacts with the app_server). 13 */ 14 15 16 #include <PrivateScreen.h> 17 18 #include <new> 19 #include <pthread.h> 20 #include <stdlib.h> 21 22 #include <Application.h> 23 #include <Autolock.h> 24 #include <Bitmap.h> 25 #include <Locker.h> 26 #include <ObjectList.h> 27 #include <Window.h> 28 29 #include <AutoLocker.h> 30 31 #include <AppMisc.h> 32 #include <AppServerLink.h> 33 #include <ServerProtocol.h> 34 35 36 using namespace BPrivate; 37 38 39 namespace { 40 41 struct Screens { 42 BObjectList<BPrivateScreen> list; 43 44 Screens() 45 : 46 list(2, true), 47 fLock("screen list") 48 { 49 } 50 51 bool Lock() 52 { 53 return fLock.Lock(); 54 } 55 56 void Unlock() 57 { 58 fLock.Unlock(); 59 } 60 61 static Screens* Default() 62 { 63 if (sDefaultInstance == NULL) 64 pthread_once(&sDefaultInitOnce, &_InitSingleton); 65 66 return sDefaultInstance; 67 } 68 69 private: 70 static void _InitSingleton() 71 { 72 sDefaultInstance = new Screens; 73 } 74 75 private: 76 BLocker fLock; 77 78 static pthread_once_t sDefaultInitOnce; 79 static Screens* sDefaultInstance; 80 }; 81 82 pthread_once_t Screens::sDefaultInitOnce = PTHREAD_ONCE_INIT; 83 Screens* Screens::sDefaultInstance = NULL; 84 85 } // unnamed namespace 86 87 88 BPrivateScreen* 89 BPrivateScreen::Get(BWindow* window) 90 { 91 int32 id = B_MAIN_SCREEN_ID.id; 92 93 if (window != NULL) { 94 BPrivate::AppServerLink link; 95 link.StartMessage(AS_GET_SCREEN_ID_FROM_WINDOW); 96 link.Attach<int32>(_get_object_token_(window)); 97 98 status_t status; 99 if (link.FlushWithReply(status) == B_OK && status == B_OK) 100 link.Read<int32>(&id); 101 } 102 103 return _Get(id, false); 104 } 105 106 107 BPrivateScreen* 108 BPrivateScreen::Get(int32 id) 109 { 110 return _Get(id, true); 111 } 112 113 114 BPrivateScreen* 115 BPrivateScreen::_Get(int32 id, bool check) 116 { 117 // Nothing works without an app_server connection 118 if (be_app == NULL) 119 return NULL; 120 121 Screens* screens = Screens::Default(); 122 AutoLocker<Screens> locker(screens); 123 124 // search for the screen ID 125 126 for (int32 i = screens->list.CountItems(); i-- > 0;) { 127 BPrivateScreen* screen = screens->list.ItemAt(i); 128 129 if (screen->ID() == id) { 130 screen->_Acquire(); 131 return screen; 132 } 133 } 134 135 if (check) { 136 // check if ID is valid 137 if (!_IsValid(id)) 138 return NULL; 139 } 140 141 // we need to allocate a new one 142 143 BPrivateScreen* screen = new (std::nothrow) BPrivateScreen(id); 144 if (screen == NULL) 145 return NULL; 146 147 screens->list.AddItem(screen); 148 return screen; 149 } 150 151 152 void 153 BPrivateScreen::Put(BPrivateScreen* screen) 154 { 155 if (screen == NULL) 156 return; 157 158 Screens* screens = Screens::Default(); 159 AutoLocker<Screens> locker(screens); 160 161 if (screen->_Release()) { 162 if (screen->ID() != B_MAIN_SCREEN_ID.id) { 163 // we always keep the main screen object around - it will 164 // never go away, even if you disconnect all monitors. 165 screens->list.RemoveItem(screen); 166 } 167 } 168 } 169 170 171 BPrivateScreen* 172 BPrivateScreen::GetNext(BPrivateScreen* screen) 173 { 174 Screens* screens = Screens::Default(); 175 AutoLocker<Screens> locker(screens); 176 177 int32 id; 178 status_t status = screen->GetNextID(id); 179 if (status != B_OK) 180 return NULL; 181 182 BPrivateScreen* nextScreen = Get(id); 183 if (nextScreen == NULL) 184 return NULL; 185 186 Put(screen); 187 return nextScreen; 188 } 189 190 191 bool 192 BPrivateScreen::_IsValid(int32 id) 193 { 194 BPrivate::AppServerLink link; 195 link.StartMessage(AS_VALID_SCREEN_ID); 196 link.Attach<int32>(id); 197 198 status_t status; 199 if (link.FlushWithReply(status) != B_OK || status < B_OK) 200 return false; 201 202 return true; 203 } 204 205 206 // #pragma mark - 207 208 209 color_space 210 BPrivateScreen::ColorSpace() 211 { 212 display_mode mode; 213 if (GetMode(B_CURRENT_WORKSPACE_INDEX, &mode) == B_OK) 214 return (color_space)mode.space; 215 216 return B_NO_COLOR_SPACE; 217 } 218 219 220 BRect 221 BPrivateScreen::Frame() 222 { 223 if (system_time() > fLastUpdate + 10000) { 224 // invalidate the settings after 10 msecs 225 BPrivate::AppServerLink link; 226 link.StartMessage(AS_GET_SCREEN_FRAME); 227 link.Attach<int32>(ID()); 228 link.Attach<uint32>(B_CURRENT_WORKSPACE_INDEX); 229 230 status_t status = B_ERROR; 231 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 232 link.Read<BRect>(&fFrame); 233 fLastUpdate = system_time(); 234 } 235 } 236 237 return fFrame; 238 } 239 240 241 bool 242 BPrivateScreen::IsValid() const 243 { 244 return BPrivateScreen::_IsValid(ID()); 245 } 246 247 248 status_t 249 BPrivateScreen::GetNextID(int32& id) 250 { 251 BPrivate::AppServerLink link; 252 link.StartMessage(AS_GET_NEXT_SCREEN_ID); 253 link.Attach<int32>(ID()); 254 255 status_t status; 256 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 257 link.Read<int32>(&id); 258 return B_OK; 259 } 260 261 return status; 262 } 263 264 265 status_t 266 BPrivateScreen::WaitForRetrace(bigtime_t timeout) 267 { 268 // Get the retrace semaphore if it's the first time 269 // we are called. Cache the value then. 270 if (!fRetraceSemValid) 271 fRetraceSem = _RetraceSemaphore(); 272 273 if (fRetraceSem < 0) { 274 // syncing to retrace is not supported by the accelerant 275 return fRetraceSem; 276 } 277 278 status_t status; 279 do { 280 status = acquire_sem_etc(fRetraceSem, 1, B_RELATIVE_TIMEOUT, timeout); 281 } while (status == B_INTERRUPTED); 282 283 return status; 284 } 285 286 287 uint8 288 BPrivateScreen::IndexForColor(uint8 red, uint8 green, uint8 blue, uint8 alpha) 289 { 290 // Looks like this check is necessary 291 if (red == B_TRANSPARENT_COLOR.red 292 && green == B_TRANSPARENT_COLOR.green 293 && blue == B_TRANSPARENT_COLOR.blue 294 && alpha == B_TRANSPARENT_COLOR.alpha) 295 return B_TRANSPARENT_8_BIT; 296 297 uint16 index = ((red & 0xf8) << 7) | ((green & 0xf8) << 2) | (blue >> 3); 298 if (ColorMap()) 299 return fColorMap->index_map[index]; 300 301 return 0; 302 } 303 304 305 rgb_color 306 BPrivateScreen::ColorForIndex(const uint8 index) 307 { 308 if (ColorMap()) 309 return fColorMap->color_list[index]; 310 311 return rgb_color(); 312 } 313 314 315 uint8 316 BPrivateScreen::InvertIndex(uint8 index) 317 { 318 if (ColorMap()) 319 return fColorMap->inversion_map[index]; 320 321 return 0; 322 } 323 324 325 const color_map* 326 BPrivateScreen::ColorMap() 327 { 328 if (fColorMap == NULL) { 329 Screens* screens = Screens::Default(); 330 AutoLocker<Screens> locker(screens); 331 332 if (fColorMap != NULL) { 333 // someone could have been faster than us 334 return fColorMap; 335 } 336 337 // TODO: BeOS R5 here gets the colormap pointer 338 // (with BApplication::ro_offset_to_ptr() ?) 339 // which is contained in a shared area created by the server. 340 BPrivate::AppServerLink link; 341 link.StartMessage(AS_SCREEN_GET_COLORMAP); 342 link.Attach<int32>(ID()); 343 344 status_t status; 345 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 346 fColorMap = (color_map*)malloc(sizeof(color_map)); 347 fOwnsColorMap = true; 348 link.Read<color_map>(fColorMap); 349 } 350 } 351 352 return fColorMap; 353 } 354 355 356 status_t 357 BPrivateScreen::GetBitmap(BBitmap**_bitmap, bool drawCursor, BRect* bounds) 358 { 359 if (_bitmap == NULL) 360 return B_BAD_VALUE; 361 362 BRect rect; 363 if (bounds != NULL) 364 rect = *bounds; 365 else 366 rect = Frame(); 367 368 BBitmap* bitmap = new (std::nothrow) BBitmap(rect, ColorSpace()); 369 if (bitmap == NULL) 370 return B_NO_MEMORY; 371 372 status_t status = bitmap->InitCheck(); 373 if (status == B_OK) 374 status = ReadBitmap(bitmap, drawCursor, &rect); 375 if (status != B_OK) { 376 delete bitmap; 377 return status; 378 } 379 380 *_bitmap = bitmap; 381 return B_OK; 382 } 383 384 385 status_t 386 BPrivateScreen::ReadBitmap(BBitmap* bitmap, bool drawCursor, BRect* bounds) 387 { 388 if (bitmap == NULL) 389 return B_BAD_VALUE; 390 391 BRect rect; 392 if (bounds != NULL) 393 rect = *bounds; 394 else 395 rect = Frame(); 396 397 BPrivate::AppServerLink link; 398 link.StartMessage(AS_READ_BITMAP); 399 link.Attach<int32>(bitmap->_ServerToken()); 400 link.Attach<bool>(drawCursor); 401 link.Attach<BRect>(rect); 402 403 status_t status = B_ERROR; 404 if (link.FlushWithReply(status) < B_OK || status != B_OK) 405 return status; 406 407 return B_OK; 408 } 409 410 411 rgb_color 412 BPrivateScreen::DesktopColor(uint32 workspace) 413 { 414 rgb_color color = { 51, 102, 152, 255 }; 415 BPrivate::AppServerLink link; 416 417 link.StartMessage(AS_GET_DESKTOP_COLOR); 418 link.Attach<uint32>(workspace); 419 420 int32 code; 421 if (link.FlushWithReply(code) == B_OK 422 && code == B_OK) 423 link.Read<rgb_color>(&color); 424 425 return color; 426 } 427 428 429 void 430 BPrivateScreen::SetDesktopColor(rgb_color color, uint32 workspace, 431 bool makeDefault) 432 { 433 BPrivate::AppServerLink link; 434 435 link.StartMessage(AS_SET_DESKTOP_COLOR); 436 link.Attach<rgb_color>(color); 437 link.Attach<uint32>(workspace); 438 link.Attach<bool>(makeDefault); 439 link.Flush(); 440 } 441 442 443 status_t 444 BPrivateScreen::ProposeMode(display_mode* target, 445 const display_mode* low, const display_mode* high) 446 { 447 // We can't return B_BAD_VALUE here, because it's used to indicate 448 // that the mode returned is supported, but it doesn't fall 449 // within the limit (see ProposeMode() documentation) 450 if (target == NULL || low == NULL || high == NULL) 451 return B_ERROR; 452 453 BPrivate::AppServerLink link; 454 link.StartMessage(AS_PROPOSE_MODE); 455 link.Attach<int32>(ID()); 456 link.Attach<display_mode>(*target); 457 link.Attach<display_mode>(*low); 458 link.Attach<display_mode>(*high); 459 460 status_t status = B_ERROR; 461 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 462 link.Read<display_mode>(target); 463 464 bool withinLimits; 465 link.Read<bool>(&withinLimits); 466 if (!withinLimits) 467 status = B_BAD_VALUE; 468 } 469 470 return status; 471 } 472 473 474 status_t 475 BPrivateScreen::GetModeList(display_mode** _modeList, uint32* _count) 476 { 477 if (_modeList == NULL || _count == NULL) 478 return B_BAD_VALUE; 479 480 BPrivate::AppServerLink link; 481 link.StartMessage(AS_GET_MODE_LIST); 482 link.Attach<int32>(ID()); 483 484 status_t status = B_ERROR; 485 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 486 uint32 count; 487 if (link.Read<uint32>(&count) < B_OK) 488 return B_ERROR; 489 490 // TODO: this could get too big for the link 491 int32 size = count * sizeof(display_mode); 492 display_mode* modeList = (display_mode *)malloc(size); 493 if (modeList == NULL) 494 return B_NO_MEMORY; 495 496 if (link.Read(modeList, size) < B_OK) { 497 free(modeList); 498 return B_ERROR; 499 } 500 501 *_modeList = modeList; 502 *_count = count; 503 } 504 505 return status; 506 } 507 508 509 status_t 510 BPrivateScreen::GetMode(uint32 workspace, display_mode *mode) 511 { 512 if (mode == NULL) 513 return B_BAD_VALUE; 514 515 BPrivate::AppServerLink link; 516 link.StartMessage(AS_SCREEN_GET_MODE); 517 link.Attach<int32>(ID()); 518 link.Attach<uint32>(workspace); 519 520 status_t status = B_ERROR; 521 if (link.FlushWithReply(status) != B_OK 522 || status != B_OK) 523 return status; 524 525 link.Read<display_mode>(mode); 526 return B_OK; 527 } 528 529 530 status_t 531 BPrivateScreen::SetMode(uint32 workspace, display_mode *mode, bool makeDefault) 532 { 533 if (mode == NULL) 534 return B_BAD_VALUE; 535 536 BPrivate::AppServerLink link; 537 link.StartMessage(AS_SCREEN_SET_MODE); 538 link.Attach<int32>(ID()); 539 link.Attach<uint32>(workspace); 540 link.Attach<display_mode>(*mode); 541 link.Attach<bool>(makeDefault); 542 543 status_t status = B_ERROR; 544 link.FlushWithReply(status); 545 546 return status; 547 } 548 549 550 status_t 551 BPrivateScreen::GetDeviceInfo(accelerant_device_info *info) 552 { 553 if (info == NULL) 554 return B_BAD_VALUE; 555 556 BPrivate::AppServerLink link; 557 link.StartMessage(AS_GET_ACCELERANT_INFO); 558 link.Attach<int32>(ID()); 559 560 status_t status = B_ERROR; 561 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 562 link.Read<accelerant_device_info>(info); 563 return B_OK; 564 } 565 566 return status; 567 } 568 569 570 status_t 571 BPrivateScreen::GetMonitorInfo(monitor_info* info) 572 { 573 if (info == NULL) 574 return B_BAD_VALUE; 575 576 BPrivate::AppServerLink link; 577 link.StartMessage(AS_GET_MONITOR_INFO); 578 link.Attach<int32>(ID()); 579 580 status_t status = B_ERROR; 581 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 582 link.Read<monitor_info>(info); 583 return B_OK; 584 } 585 586 return status; 587 } 588 589 590 status_t 591 BPrivateScreen::GetPixelClockLimits(display_mode *mode, uint32 *low, uint32 *high) 592 { 593 if (mode == NULL || low == NULL || high == NULL) 594 return B_BAD_VALUE; 595 596 BPrivate::AppServerLink link; 597 link.StartMessage(AS_GET_PIXEL_CLOCK_LIMITS); 598 link.Attach<int32>(ID()); 599 link.Attach<display_mode>(*mode); 600 601 status_t status; 602 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 603 link.Read<uint32>(low); 604 link.Read<uint32>(high); 605 return B_OK; 606 } 607 608 return status; 609 } 610 611 612 status_t 613 BPrivateScreen::GetTimingConstraints(display_timing_constraints *constraints) 614 { 615 if (constraints == NULL) 616 return B_BAD_VALUE; 617 618 BPrivate::AppServerLink link; 619 link.StartMessage(AS_GET_TIMING_CONSTRAINTS); 620 link.Attach<int32>(ID()); 621 622 status_t status = B_ERROR; 623 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 624 link.Read<display_timing_constraints>(constraints); 625 return B_OK; 626 } 627 628 return status; 629 } 630 631 632 status_t 633 BPrivateScreen::SetDPMS(uint32 dpmsState) 634 { 635 BPrivate::AppServerLink link; 636 link.StartMessage(AS_SET_DPMS); 637 link.Attach<int32>(ID()); 638 link.Attach<uint32>(dpmsState); 639 640 status_t status = B_ERROR; 641 link.FlushWithReply(status); 642 643 return status; 644 } 645 646 647 uint32 648 BPrivateScreen::DPMSState() 649 { 650 uint32 state = 0; 651 652 BPrivate::AppServerLink link; 653 link.StartMessage(AS_GET_DPMS_STATE); 654 link.Attach<int32>(ID()); 655 656 status_t status; 657 if (link.FlushWithReply(status) == B_OK && status == B_OK) 658 link.Read<uint32>(&state); 659 660 return state; 661 } 662 663 664 uint32 665 BPrivateScreen::DPMSCapabilites() 666 { 667 uint32 capabilities = 0; 668 669 BPrivate::AppServerLink link; 670 link.StartMessage(AS_GET_DPMS_CAPABILITIES); 671 link.Attach<int32>(ID()); 672 673 status_t status; 674 if (link.FlushWithReply(status) == B_OK && status == B_OK) 675 link.Read<uint32>(&capabilities); 676 677 return capabilities; 678 } 679 680 681 status_t 682 BPrivateScreen::GetBrightness(float* brightness) 683 { 684 if (brightness == NULL) 685 return B_BAD_VALUE; 686 687 BPrivate::AppServerLink link; 688 link.StartMessage(AS_SCREEN_GET_BRIGHTNESS); 689 link.Attach<int32>(ID()); 690 691 status_t status; 692 if (link.FlushWithReply(status) == B_OK && status == B_OK) 693 link.Read<float>(brightness); 694 695 return status; 696 } 697 698 699 status_t 700 BPrivateScreen::SetBrightness(float brightness) 701 { 702 BPrivate::AppServerLink link; 703 link.StartMessage(AS_SCREEN_SET_BRIGHTNESS); 704 link.Attach<int32>(ID()); 705 link.Attach<float>(brightness); 706 707 status_t status = B_ERROR; 708 link.FlushWithReply(status); 709 710 return status; 711 } 712 713 714 void * 715 BPrivateScreen::BaseAddress() 716 { 717 frame_buffer_config config; 718 if (_GetFrameBufferConfig(config) != B_OK) 719 return NULL; 720 721 return config.frame_buffer; 722 } 723 724 725 uint32 726 BPrivateScreen::BytesPerRow() 727 { 728 frame_buffer_config config; 729 if (_GetFrameBufferConfig(config) != B_OK) 730 return 0; 731 732 return config.bytes_per_row; 733 } 734 735 736 // #pragma mark - private methods 737 738 739 void 740 BPrivateScreen::_Acquire() 741 { 742 fReferenceCount++; 743 744 fLastUpdate = 0; 745 // force an update for the new BScreen object 746 } 747 748 749 bool 750 BPrivateScreen::_Release() 751 { 752 return --fReferenceCount == 0; 753 } 754 755 756 sem_id 757 BPrivateScreen::_RetraceSemaphore() 758 { 759 BPrivate::AppServerLink link; 760 link.StartMessage(AS_GET_RETRACE_SEMAPHORE); 761 link.Attach<int32>(ID()); 762 763 sem_id id = B_BAD_SEM_ID; 764 status_t status = B_ERROR; 765 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 766 link.Read<sem_id>(&id); 767 fRetraceSemValid = true; 768 } 769 770 return id; 771 } 772 773 774 status_t 775 BPrivateScreen::_GetFrameBufferConfig(frame_buffer_config& config) 776 { 777 BPrivate::AppServerLink link; 778 link.StartMessage(AS_GET_FRAME_BUFFER_CONFIG); 779 link.Attach<int32>(ID()); 780 781 status_t status = B_ERROR; 782 if (link.FlushWithReply(status) == B_OK && status == B_OK) { 783 link.Read<frame_buffer_config>(&config); 784 return B_OK; 785 } 786 787 return status; 788 } 789 790 791 BPrivateScreen::BPrivateScreen(int32 id) 792 : 793 fID(id), 794 fReferenceCount(0), 795 fColorMap(NULL), 796 fRetraceSem(-1), 797 fRetraceSemValid(false), 798 fOwnsColorMap(false), 799 fFrame(0, 0, 0, 0), 800 fLastUpdate(0) 801 { 802 } 803 804 805 BPrivateScreen::~BPrivateScreen() 806 { 807 if (fOwnsColorMap) 808 free(fColorMap); 809 } 810