1 /* 2 * Copyright 2005-2012, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 10 #include "HWInterface.h" 11 12 #include <new> 13 #include <stdio.h> 14 #include <string.h> 15 #include <unistd.h> 16 17 #include <vesa/vesa_info.h> 18 19 #include "drawing_support.h" 20 21 #include "DrawingEngine.h" 22 #include "RenderingBuffer.h" 23 #include "SystemPalette.h" 24 #include "UpdateQueue.h" 25 26 27 using std::nothrow; 28 29 30 HWInterfaceListener::HWInterfaceListener() 31 { 32 } 33 34 35 HWInterfaceListener::~HWInterfaceListener() 36 { 37 } 38 39 40 // #pragma mark - HWInterface 41 42 43 HWInterface::HWInterface() 44 : 45 MultiLocker("hw interface lock"), 46 fFloatingOverlaysLock("floating overlays lock"), 47 fCursor(NULL), 48 fDragBitmap(NULL), 49 fDragBitmapOffset(0, 0), 50 fCursorAndDragBitmap(NULL), 51 fCursorVisible(false), 52 fCursorObscured(false), 53 fHardwareCursorEnabled(false), 54 fCursorLocation(0, 0), 55 fVGADevice(-1), 56 fUpdateExecutor(NULL), 57 fListeners(20) 58 { 59 } 60 61 62 HWInterface::~HWInterface() 63 { 64 SetAsyncDoubleBuffered(false); 65 } 66 67 68 status_t 69 HWInterface::Initialize() 70 { 71 return MultiLocker::InitCheck(); 72 } 73 74 75 DrawingEngine* 76 HWInterface::CreateDrawingEngine() 77 { 78 return new(std::nothrow) DrawingEngine(this); 79 } 80 81 82 EventStream* 83 HWInterface::CreateEventStream() 84 { 85 return NULL; 86 } 87 88 89 status_t 90 HWInterface::GetAccelerantPath(BString &path) 91 { 92 return B_ERROR; 93 } 94 95 96 status_t 97 HWInterface::GetDriverPath(BString &path) 98 { 99 return B_ERROR; 100 } 101 102 103 status_t 104 HWInterface::GetPreferredMode(display_mode* mode) 105 { 106 return B_NOT_SUPPORTED; 107 } 108 109 110 status_t 111 HWInterface::GetMonitorInfo(monitor_info* info) 112 { 113 return B_NOT_SUPPORTED; 114 } 115 116 117 // #pragma mark - 118 119 120 void 121 HWInterface::SetCursor(ServerCursor* cursor) 122 { 123 if (!fFloatingOverlaysLock.Lock()) 124 return; 125 126 if (fCursor.Get() != cursor) { 127 BRect oldFrame = _CursorFrame(); 128 129 fCursor.SetTo(cursor); 130 131 Invalidate(oldFrame); 132 133 _AdoptDragBitmap(); 134 Invalidate(_CursorFrame()); 135 } 136 fFloatingOverlaysLock.Unlock(); 137 } 138 139 140 ServerCursorReference 141 HWInterface::Cursor() const 142 { 143 if (!fFloatingOverlaysLock.Lock()) 144 return ServerCursorReference(NULL); 145 146 fFloatingOverlaysLock.Unlock(); 147 return fCursor; 148 } 149 150 151 ServerCursorReference 152 HWInterface::CursorAndDragBitmap() const 153 { 154 if (!fFloatingOverlaysLock.Lock()) 155 return ServerCursorReference(NULL); 156 157 fFloatingOverlaysLock.Unlock(); 158 return fCursorAndDragBitmap; 159 } 160 161 162 void 163 HWInterface::SetCursorVisible(bool visible) 164 { 165 if (!fFloatingOverlaysLock.Lock()) 166 return; 167 168 if (fCursorVisible != visible) { 169 // NOTE: _CursorFrame() will 170 // return an invalid rect if 171 // fCursorVisible == false! 172 if (visible) { 173 fCursorVisible = visible; 174 fCursorObscured = false; 175 IntRect r = _CursorFrame(); 176 177 _DrawCursor(r); 178 Invalidate(r); 179 } else { 180 IntRect r = _CursorFrame(); 181 fCursorVisible = visible; 182 183 _RestoreCursorArea(); 184 Invalidate(r); 185 } 186 } 187 fFloatingOverlaysLock.Unlock(); 188 } 189 190 191 bool 192 HWInterface::IsCursorVisible() 193 { 194 bool visible = true; 195 if (fFloatingOverlaysLock.Lock()) { 196 visible = fCursorVisible; 197 fFloatingOverlaysLock.Unlock(); 198 } 199 return visible; 200 } 201 202 203 void 204 HWInterface::ObscureCursor() 205 { 206 if (!fFloatingOverlaysLock.Lock()) 207 return; 208 209 if (!fCursorObscured) { 210 SetCursorVisible(false); 211 fCursorObscured = true; 212 } 213 fFloatingOverlaysLock.Unlock(); 214 } 215 216 217 void 218 HWInterface::MoveCursorTo(float x, float y) 219 { 220 if (!fFloatingOverlaysLock.Lock()) 221 return; 222 223 BPoint p(x, y); 224 if (p != fCursorLocation) { 225 // unhide cursor if it is obscured only 226 if (fCursorObscured) { 227 SetCursorVisible(true); 228 } 229 IntRect oldFrame = _CursorFrame(); 230 fCursorLocation = p; 231 if (fCursorVisible) { 232 // Invalidate and _DrawCursor would not draw 233 // anything if the cursor is hidden 234 // (invalid cursor frame), but explicitly 235 // testing for it here saves us some cycles 236 if (fCursorAreaBackup.IsSet()) { 237 // means we have a software cursor which we need to draw 238 _RestoreCursorArea(); 239 _DrawCursor(_CursorFrame()); 240 } 241 IntRect newFrame = _CursorFrame(); 242 if (newFrame.Intersects(oldFrame)) 243 Invalidate(oldFrame | newFrame); 244 else { 245 Invalidate(oldFrame); 246 Invalidate(newFrame); 247 } 248 } 249 } 250 fFloatingOverlaysLock.Unlock(); 251 } 252 253 254 BPoint 255 HWInterface::CursorPosition() 256 { 257 BPoint location; 258 if (fFloatingOverlaysLock.Lock()) { 259 location = fCursorLocation; 260 fFloatingOverlaysLock.Unlock(); 261 } 262 return location; 263 } 264 265 266 void 267 HWInterface::SetDragBitmap(const ServerBitmap* bitmap, 268 const BPoint& offsetFromCursor) 269 { 270 if (fFloatingOverlaysLock.Lock()) { 271 fDragBitmap.SetTo((ServerBitmap*)bitmap, false); 272 fDragBitmapOffset = offsetFromCursor; 273 _AdoptDragBitmap(); 274 fFloatingOverlaysLock.Unlock(); 275 } 276 } 277 278 279 // #pragma mark - 280 281 282 RenderingBuffer* 283 HWInterface::DrawingBuffer() const 284 { 285 if (IsDoubleBuffered()) 286 return BackBuffer(); 287 return FrontBuffer(); 288 } 289 290 291 void 292 HWInterface::SetAsyncDoubleBuffered(bool doubleBuffered) 293 { 294 if (doubleBuffered) { 295 if (fUpdateExecutor.IsSet()) 296 return; 297 fUpdateExecutor.SetTo(new (nothrow) UpdateQueue(this)); 298 AddListener(fUpdateExecutor.Get()); 299 } else { 300 if (!fUpdateExecutor.IsSet()) 301 return; 302 RemoveListener(fUpdateExecutor.Get()); 303 fUpdateExecutor.Unset(); 304 } 305 } 306 307 308 /*! The object needs to be already locked! 309 */ 310 status_t 311 HWInterface::InvalidateRegion(const BRegion& region) 312 { 313 int32 count = region.CountRects(); 314 for (int32 i = 0; i < count; i++) { 315 status_t result = Invalidate(region.RectAt(i)); 316 if (result != B_OK) 317 return result; 318 } 319 320 return B_OK; 321 } 322 323 324 /*! The object needs to be already locked! 325 */ 326 status_t 327 HWInterface::Invalidate(const BRect& frame) 328 { 329 if (IsDoubleBuffered()) { 330 #if 0 331 // NOTE: The UpdateQueue works perfectly fine, but it screws the 332 // flicker-free-ness of the double buffered rendering. The problem being the 333 // asynchronous nature. The UpdateQueue will transfer regions of the screen 334 // which have been clean at the time we are in this function, but which have 335 // been damaged meanwhile by drawing into them again. All in all, the 336 // UpdateQueue is good for reducing the number of times that the transfer 337 // is performed, and makes it happen during refresh only, but until there 338 // is a smarter way to synchronize this all better, I've disabled it. 339 if (fUpdateExecutor != NULL) { 340 fUpdateExecutor->AddRect(frame); 341 return B_OK; 342 } 343 #endif 344 return CopyBackToFront(frame); 345 } 346 return B_OK; 347 } 348 349 350 /*! The object must already be locked! 351 */ 352 status_t 353 HWInterface::CopyBackToFront(const BRect& frame) 354 { 355 RenderingBuffer* frontBuffer = FrontBuffer(); 356 RenderingBuffer* backBuffer = BackBuffer(); 357 358 if (!backBuffer || !frontBuffer) 359 return B_NO_INIT; 360 361 // we need to mess with the area, but it is const 362 IntRect area(frame); 363 IntRect bufferClip(backBuffer->Bounds()); 364 365 if (area.IsValid() && area.Intersects(bufferClip)) { 366 367 // make sure we don't copy out of bounds 368 area = bufferClip & area; 369 370 bool cursorLocked = fFloatingOverlaysLock.Lock(); 371 372 BRegion region((BRect)area); 373 if (IsDoubleBuffered()) 374 region.Exclude((clipping_rect)_CursorFrame()); 375 376 _CopyBackToFront(region); 377 378 _DrawCursor(area); 379 380 if (cursorLocked) 381 fFloatingOverlaysLock.Unlock(); 382 383 return B_OK; 384 } 385 return B_BAD_VALUE; 386 } 387 388 389 void 390 HWInterface::_CopyBackToFront(/*const*/ BRegion& region) 391 { 392 RenderingBuffer* backBuffer = BackBuffer(); 393 394 uint32 srcBPR = backBuffer->BytesPerRow(); 395 uint8* src = (uint8*)backBuffer->Bits(); 396 397 int32 count = region.CountRects(); 398 for (int32 i = 0; i < count; i++) { 399 clipping_rect r = region.RectAtInt(i); 400 // offset to left top pixel in source buffer (always B_RGBA32) 401 uint8* srcOffset = src + r.top * srcBPR + r.left * 4; 402 _CopyToFront(srcOffset, srcBPR, r.left, r.top, r.right, r.bottom); 403 } 404 } 405 406 407 // #pragma mark - 408 409 410 overlay_token 411 HWInterface::AcquireOverlayChannel() 412 { 413 return NULL; 414 } 415 416 417 void 418 HWInterface::ReleaseOverlayChannel(overlay_token token) 419 { 420 } 421 422 423 status_t 424 HWInterface::GetOverlayRestrictions(const Overlay* overlay, 425 overlay_restrictions* restrictions) 426 { 427 return B_NOT_SUPPORTED; 428 } 429 430 431 bool 432 HWInterface::CheckOverlayRestrictions(int32 width, int32 height, 433 color_space colorSpace) 434 { 435 return false; 436 } 437 438 439 const overlay_buffer* 440 HWInterface::AllocateOverlayBuffer(int32 width, int32 height, color_space space) 441 { 442 return NULL; 443 } 444 445 446 void 447 HWInterface::FreeOverlayBuffer(const overlay_buffer* buffer) 448 { 449 } 450 451 452 void 453 HWInterface::ConfigureOverlay(Overlay* overlay) 454 { 455 } 456 457 458 void 459 HWInterface::HideOverlay(Overlay* overlay) 460 { 461 } 462 463 464 // #pragma mark - 465 466 467 bool 468 HWInterface::HideFloatingOverlays(const BRect& area) 469 { 470 if (IsDoubleBuffered()) 471 return false; 472 if (!fFloatingOverlaysLock.Lock()) 473 return false; 474 if (fCursorAreaBackup.IsSet() && !fCursorAreaBackup->cursor_hidden) { 475 BRect backupArea(fCursorAreaBackup->left, fCursorAreaBackup->top, 476 fCursorAreaBackup->right, fCursorAreaBackup->bottom); 477 if (area.Intersects(backupArea)) { 478 _RestoreCursorArea(); 479 // do not unlock the cursor lock 480 return true; 481 } 482 } 483 fFloatingOverlaysLock.Unlock(); 484 return false; 485 } 486 487 488 bool 489 HWInterface::HideFloatingOverlays() 490 { 491 if (IsDoubleBuffered()) 492 return false; 493 if (!fFloatingOverlaysLock.Lock()) 494 return false; 495 496 _RestoreCursorArea(); 497 return true; 498 } 499 500 501 void 502 HWInterface::ShowFloatingOverlays() 503 { 504 if (fCursorAreaBackup.IsSet() && fCursorAreaBackup->cursor_hidden) 505 _DrawCursor(_CursorFrame()); 506 507 fFloatingOverlaysLock.Unlock(); 508 } 509 510 511 // #pragma mark - 512 513 514 bool 515 HWInterface::AddListener(HWInterfaceListener* listener) 516 { 517 if (listener && !fListeners.HasItem(listener)) 518 return fListeners.AddItem(listener); 519 return false; 520 } 521 522 523 void 524 HWInterface::RemoveListener(HWInterfaceListener* listener) 525 { 526 fListeners.RemoveItem(listener); 527 } 528 529 530 // #pragma mark - 531 532 533 /*! Default implementation, can be used as fallback or for software cursor. 534 \param area is where we potentially draw the cursor, the cursor 535 might be somewhere else, in which case this function does nothing 536 */ 537 void 538 HWInterface::_DrawCursor(IntRect area) const 539 { 540 RenderingBuffer* backBuffer = DrawingBuffer(); 541 if (!backBuffer || !area.IsValid()) 542 return; 543 544 IntRect cf = _CursorFrame(); 545 546 // make sure we don't copy out of bounds 547 area = backBuffer->Bounds() & area; 548 549 if (cf.IsValid() && area.Intersects(cf)) { 550 551 // clip to common area 552 area = area & cf; 553 554 int32 width = area.right - area.left + 1; 555 int32 height = area.bottom - area.top + 1; 556 557 // make a bitmap from the backbuffer 558 // that has the cursor blended on top of it 559 560 // blending buffer 561 uint8* buffer = new(std::nothrow) uint8[width * height * 4]; 562 // TODO: cache this buffer 563 if (buffer == NULL) 564 return; 565 566 // offset into back buffer 567 uint8* src = (uint8*)backBuffer->Bits(); 568 uint32 srcBPR = backBuffer->BytesPerRow(); 569 src += area.top * srcBPR + area.left * 4; 570 571 // offset into cursor bitmap 572 uint8* crs = (uint8*)fCursorAndDragBitmap->Bits(); 573 uint32 crsBPR = fCursorAndDragBitmap->BytesPerRow(); 574 // since area is clipped to cf, 575 // the diff between area.top and cf.top is always positive, 576 // same for diff between area.left and cf.left 577 crs += (area.top - (int32)floorf(cf.top)) * crsBPR 578 + (area.left - (int32)floorf(cf.left)) * 4; 579 580 uint8* dst = buffer; 581 582 if (fCursorAreaBackup.IsSet() && fCursorAreaBackup->buffer 583 && fFloatingOverlaysLock.Lock()) { 584 fCursorAreaBackup->cursor_hidden = false; 585 // remember which area the backup contains 586 fCursorAreaBackup->left = area.left; 587 fCursorAreaBackup->top = area.top; 588 fCursorAreaBackup->right = area.right; 589 fCursorAreaBackup->bottom = area.bottom; 590 uint8* bup = fCursorAreaBackup->buffer; 591 uint32 bupBPR = fCursorAreaBackup->bpr; 592 593 // blending and backup of drawing buffer 594 for (int32 y = area.top; y <= area.bottom; y++) { 595 uint8* s = src; 596 uint8* c = crs; 597 uint8* d = dst; 598 uint8* b = bup; 599 600 for (int32 x = area.left; x <= area.right; x++) { 601 *(uint32*)b = *(uint32*)s; 602 // assumes backbuffer alpha = 255 603 // assuming pre-multiplied cursor bitmap 604 int a = 255 - c[3]; 605 d[0] = ((int)(b[0] * a + 255) >> 8) + c[0]; 606 d[1] = ((int)(b[1] * a + 255) >> 8) + c[1]; 607 d[2] = ((int)(b[2] * a + 255) >> 8) + c[2]; 608 609 s += 4; 610 c += 4; 611 d += 4; 612 b += 4; 613 } 614 crs += crsBPR; 615 src += srcBPR; 616 dst += width * 4; 617 bup += bupBPR; 618 } 619 fFloatingOverlaysLock.Unlock(); 620 } else { 621 // blending 622 for (int32 y = area.top; y <= area.bottom; y++) { 623 uint8* s = src; 624 uint8* c = crs; 625 uint8* d = dst; 626 for (int32 x = area.left; x <= area.right; x++) { 627 // assumes backbuffer alpha = 255 628 // assuming pre-multiplied cursor bitmap 629 uint8 a = 255 - c[3]; 630 d[0] = ((s[0] * a + 255) >> 8) + c[0]; 631 d[1] = ((s[1] * a + 255) >> 8) + c[1]; 632 d[2] = ((s[2] * a + 255) >> 8) + c[2]; 633 634 s += 4; 635 c += 4; 636 d += 4; 637 } 638 crs += crsBPR; 639 src += srcBPR; 640 dst += width * 4; 641 } 642 } 643 // copy result to front buffer 644 _CopyToFront(buffer, width * 4, area.left, area.top, area.right, 645 area.bottom); 646 647 delete[] buffer; 648 } 649 } 650 651 652 /*! - source is assumed to be already at the right offset 653 - source is assumed to be in B_RGBA32 format 654 - location in front buffer is calculated 655 - conversion from B_RGBA32 to format of front buffer is taken care of 656 */ 657 void 658 HWInterface::_CopyToFront(uint8* src, uint32 srcBPR, int32 x, int32 y, 659 int32 right, int32 bottom) const 660 { 661 RenderingBuffer* frontBuffer = FrontBuffer(); 662 663 uint8* dst = (uint8*)frontBuffer->Bits(); 664 uint32 dstBPR = frontBuffer->BytesPerRow(); 665 666 // transfer, handle colorspace conversion 667 switch (frontBuffer->ColorSpace()) { 668 case B_RGB32: 669 case B_RGBA32: 670 { 671 int32 bytes = (right - x + 1) * 4; 672 673 if (bytes > 0) { 674 // offset to left top pixel in dest buffer 675 dst += y * dstBPR + x * 4; 676 // copy 677 for (; y <= bottom; y++) { 678 // bytes is guaranteed to be multiple of 4 679 memcpy(dst, src, bytes); 680 dst += dstBPR; 681 src += srcBPR; 682 } 683 } 684 break; 685 } 686 687 case B_RGB24: 688 { 689 // offset to left top pixel in dest buffer 690 dst += y * dstBPR + x * 3; 691 int32 left = x; 692 // copy 693 for (; y <= bottom; y++) { 694 uint8* srcHandle = src; 695 uint8* dstHandle = dst; 696 for (x = left; x <= right; x++) { 697 dstHandle[0] = srcHandle[0]; 698 dstHandle[1] = srcHandle[1]; 699 dstHandle[2] = srcHandle[2]; 700 dstHandle += 3; 701 srcHandle += 4; 702 } 703 dst += dstBPR; 704 src += srcBPR; 705 } 706 break; 707 } 708 709 case B_RGB16: 710 { 711 // offset to left top pixel in dest buffer 712 dst += y * dstBPR + x * 2; 713 int32 left = x; 714 // copy 715 // TODO: assumes BGR order, does this work on big endian as well? 716 for (; y <= bottom; y++) { 717 uint8* srcHandle = src; 718 uint16* dstHandle = (uint16*)dst; 719 for (x = left; x <= right; x++) { 720 *dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 8) 721 | ((srcHandle[1] & 0xfc) << 3) | (srcHandle[0] >> 3)); 722 dstHandle ++; 723 srcHandle += 4; 724 } 725 dst += dstBPR; 726 src += srcBPR; 727 } 728 break; 729 } 730 731 case B_RGB15: 732 case B_RGBA15: 733 { 734 // offset to left top pixel in dest buffer 735 dst += y * dstBPR + x * 2; 736 int32 left = x; 737 // copy 738 // TODO: assumes BGR order, does this work on big endian as well? 739 for (; y <= bottom; y++) { 740 uint8* srcHandle = src; 741 uint16* dstHandle = (uint16*)dst; 742 for (x = left; x <= right; x++) { 743 *dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 7) 744 | ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3)); 745 dstHandle ++; 746 srcHandle += 4; 747 } 748 dst += dstBPR; 749 src += srcBPR; 750 } 751 break; 752 } 753 754 case B_CMAP8: 755 { 756 const color_map *colorMap = SystemColorMap(); 757 // offset to left top pixel in dest buffer 758 dst += y * dstBPR + x; 759 int32 left = x; 760 uint16 index; 761 // copy 762 // TODO: assumes BGR order again 763 for (; y <= bottom; y++) { 764 uint8* srcHandle = src; 765 uint8* dstHandle = dst; 766 for (x = left; x <= right; x++) { 767 index = ((srcHandle[2] & 0xf8) << 7) 768 | ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3); 769 *dstHandle = colorMap->index_map[index]; 770 dstHandle ++; 771 srcHandle += 4; 772 } 773 dst += dstBPR; 774 src += srcBPR; 775 } 776 777 break; 778 } 779 780 case B_GRAY8: 781 if (frontBuffer->Width() > dstBPR) { 782 // VGA 16 color grayscale planar mode 783 if (fVGADevice >= 0) { 784 vga_planar_blit_args args; 785 args.source = src; 786 args.source_bytes_per_row = srcBPR; 787 args.left = x; 788 args.top = y; 789 args.right = right; 790 args.bottom = bottom; 791 if (ioctl(fVGADevice, VGA_PLANAR_BLIT, &args, sizeof(args)) 792 == 0) 793 break; 794 } 795 796 // Since we cannot set the plane, we do monochrome output 797 dst += y * dstBPR + x / 8; 798 int32 left = x; 799 800 // TODO: this is awfully slow... 801 // TODO: assumes BGR order 802 for (; y <= bottom; y++) { 803 uint8* srcHandle = src; 804 uint8* dstHandle = dst; 805 uint8 current8 = dstHandle[0]; 806 // we store 8 pixels before writing them back 807 808 for (x = left; x <= right; x++) { 809 uint8 pixel = (308 * srcHandle[2] + 600 * srcHandle[1] 810 + 116 * srcHandle[0]) / 1024; 811 srcHandle += 4; 812 813 if (pixel > 128) 814 current8 |= 0x80 >> (x & 7); 815 else 816 current8 &= ~(0x80 >> (x & 7)); 817 818 if ((x & 7) == 7) { 819 // last pixel in 8 pixel group 820 dstHandle[0] = current8; 821 dstHandle++; 822 current8 = dstHandle[0]; 823 } 824 } 825 826 if (x & 7) { 827 // last pixel has not been written yet 828 dstHandle[0] = current8; 829 } 830 dst += dstBPR; 831 src += srcBPR; 832 } 833 } else { 834 // offset to left top pixel in dest buffer 835 dst += y * dstBPR + x; 836 int32 left = x; 837 // copy 838 // TODO: assumes BGR order, does this work on big endian as well? 839 for (; y <= bottom; y++) { 840 uint8* srcHandle = src; 841 uint8* dstHandle = dst; 842 for (x = left; x <= right; x++) { 843 *dstHandle = (308 * srcHandle[2] + 600 * srcHandle[1] 844 + 116 * srcHandle[0]) / 1024; 845 dstHandle ++; 846 srcHandle += 4; 847 } 848 dst += dstBPR; 849 src += srcBPR; 850 } 851 } 852 break; 853 854 default: 855 fprintf(stderr, "HWInterface::CopyBackToFront() - unsupported " 856 "front buffer format! (0x%x)\n", frontBuffer->ColorSpace()); 857 break; 858 } 859 } 860 861 862 /*! The object must be locked 863 */ 864 IntRect 865 HWInterface::_CursorFrame() const 866 { 867 IntRect frame(0, 0, -1, -1); 868 if (fCursorAndDragBitmap && fCursorVisible && !fHardwareCursorEnabled) { 869 frame = fCursorAndDragBitmap->Bounds(); 870 frame.OffsetTo(fCursorLocation - fCursorAndDragBitmap->GetHotSpot()); 871 } 872 return frame; 873 } 874 875 876 void 877 HWInterface::_RestoreCursorArea() const 878 { 879 if (fCursorAreaBackup.IsSet() && !fCursorAreaBackup->cursor_hidden) { 880 _CopyToFront(fCursorAreaBackup->buffer, fCursorAreaBackup->bpr, 881 fCursorAreaBackup->left, fCursorAreaBackup->top, 882 fCursorAreaBackup->right, fCursorAreaBackup->bottom); 883 884 fCursorAreaBackup->cursor_hidden = true; 885 } 886 } 887 888 889 void 890 HWInterface::_AdoptDragBitmap() 891 { 892 // TODO: support other colorspaces/convert bitmap 893 if (fDragBitmap && !(fDragBitmap->ColorSpace() == B_RGB32 894 || fDragBitmap->ColorSpace() == B_RGBA32)) { 895 fprintf(stderr, "HWInterface::_AdoptDragBitmap() - bitmap has yet " 896 "unsupported colorspace\n"); 897 return; 898 } 899 900 _RestoreCursorArea(); 901 BRect oldCursorFrame = _CursorFrame(); 902 903 if (fDragBitmap != NULL && fDragBitmap->Bounds().Width() > 0 && fDragBitmap->Bounds().Height() > 0) { 904 BRect bitmapFrame = fDragBitmap->Bounds(); 905 if (fCursor) { 906 // put bitmap frame and cursor frame into the same 907 // coordinate space (the cursor location is the origin) 908 bitmapFrame.OffsetTo(BPoint(-fDragBitmapOffset.x, -fDragBitmapOffset.y)); 909 910 BRect cursorFrame(fCursor->Bounds()); 911 BPoint hotspot(fCursor->GetHotSpot()); 912 // the hotspot is at the origin 913 cursorFrame.OffsetTo(-hotspot.x, -hotspot.y); 914 915 BRect combindedBounds = bitmapFrame | cursorFrame; 916 917 BPoint shift; 918 shift.x = -combindedBounds.left; 919 shift.y = -combindedBounds.top; 920 921 combindedBounds.OffsetBy(shift); 922 cursorFrame.OffsetBy(shift); 923 bitmapFrame.OffsetBy(shift); 924 925 fCursorAndDragBitmap.SetTo(new(std::nothrow) ServerCursor(combindedBounds, 926 fDragBitmap->ColorSpace(), 0, shift), true); 927 928 uint8* dst = fCursorAndDragBitmap ? (uint8*)fCursorAndDragBitmap->Bits() : NULL; 929 if (dst == NULL) { 930 // Oops, we could not allocate memory for the drag bitmap. 931 // Let's show the cursor only. 932 fCursorAndDragBitmap = fCursor; 933 } else { 934 // clear the combined buffer 935 uint32 dstBPR = fCursorAndDragBitmap->BytesPerRow(); 936 937 memset(dst, 0, fCursorAndDragBitmap->BitsLength()); 938 939 // put drag bitmap into combined buffer 940 uint8* src = (uint8*)fDragBitmap->Bits(); 941 uint32 srcBPR = fDragBitmap->BytesPerRow(); 942 943 dst += (int32)bitmapFrame.top * dstBPR 944 + (int32)bitmapFrame.left * 4; 945 946 uint32 width = bitmapFrame.IntegerWidth() + 1; 947 uint32 height = bitmapFrame.IntegerHeight() + 1; 948 949 for (uint32 y = 0; y < height; y++) { 950 memcpy(dst, src, srcBPR); 951 dst += dstBPR; 952 src += srcBPR; 953 } 954 955 // compose cursor into combined buffer 956 dst = (uint8*)fCursorAndDragBitmap->Bits(); 957 dst += (int32)cursorFrame.top * dstBPR 958 + (int32)cursorFrame.left * 4; 959 960 src = (uint8*)fCursor->Bits(); 961 srcBPR = fCursor->BytesPerRow(); 962 963 width = cursorFrame.IntegerWidth() + 1; 964 height = cursorFrame.IntegerHeight() + 1; 965 966 for (uint32 y = 0; y < height; y++) { 967 uint8* d = dst; 968 uint8* s = src; 969 for (uint32 x = 0; x < width; x++) { 970 // takes two semi-transparent pixels 971 // with unassociated alpha (not pre-multiplied) 972 // and stays within non-premultiplied color space 973 if (s[3] > 0) { 974 if (s[3] == 255) { 975 d[0] = s[0]; 976 d[1] = s[1]; 977 d[2] = s[2]; 978 d[3] = 255; 979 } else { 980 uint8 alphaRest = 255 - s[3]; 981 uint32 alphaTemp 982 = (65025 - alphaRest * (255 - d[3])); 983 uint32 alphaDest = d[3] * alphaRest; 984 uint32 alphaSrc = 255 * s[3]; 985 d[0] = (d[0] * alphaDest + s[0] * alphaSrc) 986 / alphaTemp; 987 d[1] = (d[1] * alphaDest + s[1] * alphaSrc) 988 / alphaTemp; 989 d[2] = (d[2] * alphaDest + s[2] * alphaSrc) 990 / alphaTemp; 991 d[3] = alphaTemp / 255; 992 } 993 } 994 // TODO: make sure the alpha is always upside down, 995 // then it doesn't need to be done when drawing the cursor 996 // (see _DrawCursor()) 997 // d[3] = 255 - d[3]; 998 d += 4; 999 s += 4; 1000 } 1001 dst += dstBPR; 1002 src += srcBPR; 1003 } 1004 1005 // handle pre-multiplication with alpha 1006 // for faster compositing during cursor drawing 1007 width = combindedBounds.IntegerWidth() + 1; 1008 height = combindedBounds.IntegerHeight() + 1; 1009 1010 dst = (uint8*)fCursorAndDragBitmap->Bits(); 1011 1012 for (uint32 y = 0; y < height; y++) { 1013 uint8* d = dst; 1014 for (uint32 x = 0; x < width; x++) { 1015 d[0] = (d[0] * d[3]) >> 8; 1016 d[1] = (d[1] * d[3]) >> 8; 1017 d[2] = (d[2] * d[3]) >> 8; 1018 d += 4; 1019 } 1020 dst += dstBPR; 1021 } 1022 } 1023 } else { 1024 fCursorAndDragBitmap.SetTo(new ServerCursor(fDragBitmap->Bits(), 1025 bitmapFrame.IntegerWidth() + 1, bitmapFrame.IntegerHeight() + 1, 1026 fDragBitmap->ColorSpace()), true); 1027 fCursorAndDragBitmap->SetHotSpot(BPoint(-fDragBitmapOffset.x, -fDragBitmapOffset.y)); 1028 } 1029 } else { 1030 fCursorAndDragBitmap = fCursor; 1031 } 1032 1033 Invalidate(oldCursorFrame); 1034 1035 fCursorAreaBackup.Unset(); 1036 1037 if (!fCursorAndDragBitmap) 1038 return; 1039 1040 if (fCursorAndDragBitmap && !IsDoubleBuffered()) { 1041 BRect cursorBounds = fCursorAndDragBitmap->Bounds(); 1042 fCursorAreaBackup.SetTo(new buffer_clip(cursorBounds.IntegerWidth() + 1, 1043 cursorBounds.IntegerHeight() + 1)); 1044 if (fCursorAreaBackup->buffer == NULL) 1045 fCursorAreaBackup.Unset(); 1046 } 1047 _DrawCursor(_CursorFrame()); 1048 } 1049 1050 1051 void 1052 HWInterface::_NotifyFrameBufferChanged() 1053 { 1054 BList listeners(fListeners); 1055 int32 count = listeners.CountItems(); 1056 for (int32 i = 0; i < count; i++) { 1057 HWInterfaceListener* listener 1058 = (HWInterfaceListener*)listeners.ItemAtFast(i); 1059 listener->FrameBufferChanged(); 1060 } 1061 } 1062 1063 1064 void 1065 HWInterface::_NotifyScreenChanged() 1066 { 1067 BList listeners(fListeners); 1068 int32 count = listeners.CountItems(); 1069 for (int32 i = 0; i < count; i++) { 1070 HWInterfaceListener* listener 1071 = (HWInterfaceListener*)listeners.ItemAtFast(i); 1072 listener->ScreenChanged(this); 1073 } 1074 } 1075 1076 1077 /*static*/ bool 1078 HWInterface::_IsValidMode(const display_mode& mode) 1079 { 1080 // TODO: more of those! 1081 if (mode.virtual_width < 320 1082 || mode.virtual_height < 200) 1083 return false; 1084 1085 return true; 1086 } 1087 1088