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