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