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