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 36using namespace BPrivate; 37 38 39namespace { 40 41struct 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 69private: 70 static void _InitSingleton() 71 { 72 sDefaultInstance = new Screens; 73 } 74 75private: 76 BLocker fLock; 77 78 static pthread_once_t sDefaultInitOnce; 79 static Screens* sDefaultInstance; 80}; 81 82pthread_once_t Screens::sDefaultInitOnce = PTHREAD_ONCE_INIT; 83Screens* Screens::sDefaultInstance = NULL; 84 85} // unnamed namespace 86 87 88BPrivateScreen* 89BPrivateScreen::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 107BPrivateScreen* 108BPrivateScreen::Get(int32 id) 109{ 110 return _Get(id, true); 111} 112 113 114BPrivateScreen* 115BPrivateScreen::_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 152void 153BPrivateScreen::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 171BPrivateScreen* 172BPrivateScreen::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 191bool 192BPrivateScreen::_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 209color_space 210BPrivateScreen::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 220BRect 221BPrivateScreen::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 241bool 242BPrivateScreen::IsValid() const 243{ 244 return BPrivateScreen::_IsValid(ID()); 245} 246 247 248status_t 249BPrivateScreen::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 265status_t 266BPrivateScreen::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 287uint8 288BPrivateScreen::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 305rgb_color 306BPrivateScreen::ColorForIndex(const uint8 index) 307{ 308 if (ColorMap()) 309 return fColorMap->color_list[index]; 310 311 return rgb_color(); 312} 313 314 315uint8 316BPrivateScreen::InvertIndex(uint8 index) 317{ 318 if (ColorMap()) 319 return fColorMap->inversion_map[index]; 320 321 return 0; 322} 323 324 325const color_map* 326BPrivateScreen::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 356status_t 357BPrivateScreen::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 385status_t 386BPrivateScreen::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 411rgb_color 412BPrivateScreen::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 429void 430BPrivateScreen::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 443status_t 444BPrivateScreen::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 474status_t 475BPrivateScreen::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 509status_t 510BPrivateScreen::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 530status_t 531BPrivateScreen::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 550status_t 551BPrivateScreen::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 570status_t 571BPrivateScreen::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 590status_t 591BPrivateScreen::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 612status_t 613BPrivateScreen::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 632status_t 633BPrivateScreen::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 647uint32 648BPrivateScreen::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 664uint32 665BPrivateScreen::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 681status_t 682BPrivateScreen::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 699status_t 700BPrivateScreen::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 714void * 715BPrivateScreen::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 725uint32 726BPrivateScreen::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 739void 740BPrivateScreen::_Acquire() 741{ 742 fReferenceCount++; 743 744 fLastUpdate = 0; 745 // force an update for the new BScreen object 746} 747 748 749bool 750BPrivateScreen::_Release() 751{ 752 return --fReferenceCount == 0; 753} 754 755 756sem_id 757BPrivateScreen::_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 774status_t 775BPrivateScreen::_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 791BPrivateScreen::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 805BPrivateScreen::~BPrivateScreen() 806{ 807 if (fOwnsColorMap) 808 free(fColorMap); 809} 810