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