1 /* 2 * Copyright 2005-2007, 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 "drawing_support.h" 13 14 #include "RenderingBuffer.h" 15 #include "SystemPalette.h" 16 #include "UpdateQueue.h" 17 18 #include <vesa/vesa_info.h> 19 20 #include <stdio.h> 21 #include <string.h> 22 #include <unistd.h> 23 24 25 HWInterfaceListener::HWInterfaceListener() 26 { 27 } 28 29 30 HWInterfaceListener::~HWInterfaceListener() 31 { 32 } 33 34 35 // #pragma mark - HWInterface 36 37 38 HWInterface::HWInterface(bool doubleBuffered) 39 : MultiLocker("hw interface lock"), 40 fCursorAreaBackup(NULL), 41 fFloatingOverlaysLock("floating overlays lock"), 42 fCursor(NULL), 43 fDragBitmap(NULL), 44 fDragBitmapOffset(0, 0), 45 fCursorAndDragBitmap(NULL), 46 fCursorVisible(false), 47 fCursorObscured(false), 48 fCursorLocation(0, 0), 49 fDoubleBuffered(doubleBuffered), 50 fVGADevice(-1), 51 // fUpdateExecutor(new UpdateQueue(this)) 52 fUpdateExecutor(NULL), 53 fListeners(20) 54 { 55 } 56 57 // destructor 58 HWInterface::~HWInterface() 59 { 60 delete fCursorAreaBackup; 61 62 // The standard cursor doesn't belong us - the drag bitmap might 63 if (fCursor != fCursorAndDragBitmap) 64 delete fCursorAndDragBitmap; 65 66 delete fUpdateExecutor; 67 } 68 69 // Initialize 70 status_t 71 HWInterface::Initialize() 72 { 73 return MultiLocker::InitCheck(); 74 } 75 76 77 status_t 78 HWInterface::GetAccelerantPath(BString &path) 79 { 80 return B_ERROR; 81 } 82 83 84 status_t 85 HWInterface::GetDriverPath(BString &path) 86 { 87 return B_ERROR; 88 } 89 90 91 status_t 92 HWInterface::GetPreferredMode(display_mode* mode) 93 { 94 return B_NOT_SUPPORTED; 95 } 96 97 98 status_t 99 HWInterface::GetMonitorInfo(monitor_info* info) 100 { 101 return B_NOT_SUPPORTED; 102 } 103 104 105 // #pragma mark - 106 107 108 void 109 HWInterface::SetCursor(ServerCursor* cursor) 110 { 111 if (!fFloatingOverlaysLock.Lock()) 112 return; 113 114 // TODO: if a bitmap is being dragged, it could 115 // be considered iritating to the user to change 116 // cursor shapes while something is dragged. 117 // The disabled code below would prevent this (except 118 // for the minor annoyance that the cursor is not 119 // updated when the drag is over) 120 // if (fDragBitmap) { 121 // // TODO: like a "+" or "-" sign when dragging some files to indicate 122 // // the current drag mode? 123 // UnlockExclusiveAccess(); 124 // return; 125 // } 126 if (fCursor != cursor) { 127 BRect oldFrame = _CursorFrame(); 128 129 if (fCursorAndDragBitmap == fCursor) { 130 // make sure _AdoptDragBitmap doesn't delete a real cursor 131 fCursorAndDragBitmap = NULL; 132 } 133 134 if (fCursor) 135 fCursor->Release(); 136 137 fCursor = cursor; 138 139 if (fCursor) 140 fCursor->Acquire(); 141 142 Invalidate(oldFrame); 143 144 _AdoptDragBitmap(fDragBitmap, fDragBitmapOffset); 145 Invalidate(_CursorFrame()); 146 } 147 fFloatingOverlaysLock.Unlock(); 148 } 149 150 // Cursor 151 ServerCursorReference 152 HWInterface::Cursor() const 153 { 154 if (!fFloatingOverlaysLock.Lock()) 155 return ServerCursorReference(NULL); 156 ServerCursorReference reference(fCursor); 157 fFloatingOverlaysLock.Unlock(); 158 return reference; 159 } 160 161 // SetCursorVisible 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 // IsCursorVisible 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 // ObscureCursor 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 // MoveCursorTo 217 void 218 HWInterface::MoveCursorTo(const float& x, const 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) { 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 _AdoptDragBitmap(bitmap, offsetFromCursor); 272 fFloatingOverlaysLock.Unlock(); 273 } 274 } 275 276 277 // #pragma mark - 278 279 280 RenderingBuffer* 281 HWInterface::DrawingBuffer() const 282 { 283 if (IsDoubleBuffered()) 284 return BackBuffer(); 285 return FrontBuffer(); 286 } 287 288 289 bool 290 HWInterface::IsDoubleBuffered() const 291 { 292 return fDoubleBuffered; 293 } 294 295 296 // * the object needs to be already locked! 297 status_t 298 HWInterface::Invalidate(const BRect& frame) 299 { 300 if (IsDoubleBuffered()) { 301 return CopyBackToFront(frame); 302 303 // TODO: the remaining problem is the immediate wake up of the 304 // thread carrying out the updates, when I enable it, there 305 // seems to be a deadlock, but I didn't figure it out yet. 306 // Maybe the same bug is there without the wakeup, only, triggered 307 // less often.... scarry, huh? 308 /* if (frame.IsValid()) { 309 fUpdateExecutor->AddRect(frame); 310 return B_OK; 311 } 312 return B_BAD_VALUE;*/ 313 } else { 314 // _DrawCursor(frame); 315 } 316 return B_OK; 317 } 318 319 320 // * the object must already be locked! 321 status_t 322 HWInterface::CopyBackToFront(const BRect& frame) 323 { 324 RenderingBuffer* frontBuffer = FrontBuffer(); 325 RenderingBuffer* backBuffer = BackBuffer(); 326 327 if (!backBuffer || !frontBuffer) 328 return B_NO_INIT; 329 330 // we need to mess with the area, but it is const 331 IntRect area(frame); 332 IntRect bufferClip(backBuffer->Bounds()); 333 334 if (area.IsValid() && area.Intersects(bufferClip)) { 335 336 // make sure we don't copy out of bounds 337 area = bufferClip & area; 338 339 BRegion region((BRect)area); 340 if (IsDoubleBuffered()) 341 region.Exclude((clipping_rect)_CursorFrame()); 342 343 CopyBackToFront(region); 344 345 _DrawCursor(area); 346 347 return B_OK; 348 } 349 return B_BAD_VALUE; 350 } 351 352 353 void 354 HWInterface::CopyBackToFront(/*const*/ BRegion& region) 355 { 356 RenderingBuffer* backBuffer = BackBuffer(); 357 358 uint32 srcBPR = backBuffer->BytesPerRow(); 359 uint8* src = (uint8*)backBuffer->Bits(); 360 361 int32 count = region.CountRects(); 362 for (int32 i = 0; i < count; i++) { 363 clipping_rect r = region.RectAtInt(i); 364 // offset to left top pixel in source buffer (always B_RGBA32) 365 uint8* srcOffset = src + r.top * srcBPR + r.left * 4; 366 _CopyToFront(srcOffset, srcBPR, r.left, r.top, r.right, r.bottom); 367 } 368 } 369 370 371 // #pragma mark - 372 373 374 overlay_token 375 HWInterface::AcquireOverlayChannel() 376 { 377 return NULL; 378 } 379 380 381 void 382 HWInterface::ReleaseOverlayChannel(overlay_token token) 383 { 384 } 385 386 387 status_t 388 HWInterface::GetOverlayRestrictions(const Overlay* overlay, 389 overlay_restrictions* restrictions) 390 { 391 return B_NOT_SUPPORTED; 392 } 393 394 395 bool 396 HWInterface::CheckOverlayRestrictions(int32 width, int32 height, 397 color_space colorSpace) 398 { 399 return false; 400 } 401 402 403 const overlay_buffer* 404 HWInterface::AllocateOverlayBuffer(int32 width, int32 height, color_space space) 405 { 406 return NULL; 407 } 408 409 410 void 411 HWInterface::FreeOverlayBuffer(const overlay_buffer* buffer) 412 { 413 } 414 415 416 void 417 HWInterface::ConfigureOverlay(Overlay* overlay) 418 { 419 } 420 421 422 void 423 HWInterface::HideOverlay(Overlay* overlay) 424 { 425 } 426 427 428 // #pragma mark - 429 430 431 bool 432 HWInterface::HideFloatingOverlays(const BRect& area) 433 { 434 if (IsDoubleBuffered()) 435 return false; 436 if (!fFloatingOverlaysLock.Lock()) 437 return false; 438 if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) { 439 BRect backupArea(fCursorAreaBackup->left, 440 fCursorAreaBackup->top, 441 fCursorAreaBackup->right, 442 fCursorAreaBackup->bottom); 443 if (area.Intersects(backupArea)) { 444 _RestoreCursorArea(); 445 // do not unlock the cursor lock 446 return true; 447 } 448 } 449 fFloatingOverlaysLock.Unlock(); 450 return false; 451 } 452 453 454 bool 455 HWInterface::HideFloatingOverlays() 456 { 457 if (IsDoubleBuffered()) 458 return false; 459 if (!fFloatingOverlaysLock.Lock()) 460 return false; 461 462 _RestoreCursorArea(); 463 return true; 464 } 465 466 467 void 468 HWInterface::ShowFloatingOverlays() 469 { 470 if (fCursorAreaBackup && fCursorAreaBackup->cursor_hidden) { 471 _DrawCursor(_CursorFrame()); 472 } 473 fFloatingOverlaysLock.Unlock(); 474 } 475 476 477 // #pragma mark - 478 479 480 bool 481 HWInterface::AddListener(HWInterfaceListener* listener) 482 { 483 if (listener && !fListeners.HasItem(listener)) 484 return fListeners.AddItem(listener); 485 return false; 486 } 487 488 489 void 490 HWInterface::RemoveListener(HWInterfaceListener* listener) 491 { 492 fListeners.RemoveItem(listener); 493 } 494 495 496 // #pragma mark - 497 498 499 // * default implementation, can be used as fallback or for 500 // software cursor 501 // * area is where we potentially draw the cursor, the cursor 502 // might be somewhere else, in which case this function does nothing 503 void 504 HWInterface::_DrawCursor(IntRect area) const 505 { 506 RenderingBuffer* backBuffer = DrawingBuffer(); 507 if (!backBuffer || !area.IsValid()) 508 return; 509 510 IntRect cf = _CursorFrame(); 511 512 // make sure we don't copy out of bounds 513 area = backBuffer->Bounds() & area; 514 515 if (cf.IsValid() && area.Intersects(cf)) { 516 517 // clip to common area 518 area = area & cf; 519 520 int32 width = area.right - area.left + 1; 521 int32 height = area.bottom - area.top + 1; 522 523 // make a bitmap from the backbuffer 524 // that has the cursor blended on top of it 525 526 // blending buffer 527 uint8* buffer = new uint8[width * height * 4]; 528 // TODO: cache this buffer 529 530 // offset into back buffer 531 uint8* src = (uint8*)backBuffer->Bits(); 532 uint32 srcBPR = backBuffer->BytesPerRow(); 533 src += area.top * srcBPR + area.left * 4; 534 535 // offset into cursor bitmap 536 uint8* crs = (uint8*)fCursorAndDragBitmap->Bits(); 537 uint32 crsBPR = fCursorAndDragBitmap->BytesPerRow(); 538 // since area is clipped to cf, 539 // the diff between area.top and cf.top is always positive, 540 // same for diff between area.left and cf.left 541 crs += (area.top - (int32)floorf(cf.top)) * crsBPR 542 + (area.left - (int32)floorf(cf.left)) * 4; 543 544 uint8* dst = buffer; 545 546 if (fCursorAreaBackup && fCursorAreaBackup->buffer 547 && fFloatingOverlaysLock.Lock()) { 548 fCursorAreaBackup->cursor_hidden = false; 549 // remember which area the backup contains 550 fCursorAreaBackup->left = area.left; 551 fCursorAreaBackup->top = area.top; 552 fCursorAreaBackup->right = area.right; 553 fCursorAreaBackup->bottom = area.bottom; 554 uint8* bup = fCursorAreaBackup->buffer; 555 uint32 bupBPR = fCursorAreaBackup->bpr; 556 557 // blending and backup of drawing buffer 558 for (int32 y = area.top; y <= area.bottom; y++) { 559 uint8* s = src; 560 uint8* c = crs; 561 uint8* d = dst; 562 uint8* b = bup; 563 564 for (int32 x = area.left; x <= area.right; x++) { 565 *(uint32*)b = *(uint32*)s; 566 // assumes backbuffer alpha = 255 567 // assuming pre-multiplied cursor bitmap 568 int a = 255 - c[3]; 569 d[0] = ((int)(b[0] * a + 255) >> 8) + c[0]; 570 d[1] = ((int)(b[1] * a + 255) >> 8) + c[1]; 571 d[2] = ((int)(b[2] * a + 255) >> 8) + c[2]; 572 573 s += 4; 574 c += 4; 575 d += 4; 576 b += 4; 577 } 578 crs += crsBPR; 579 src += srcBPR; 580 dst += width * 4; 581 bup += bupBPR; 582 } 583 fFloatingOverlaysLock.Unlock(); 584 } else { 585 // blending 586 for (int32 y = area.top; y <= area.bottom; y++) { 587 uint8* s = src; 588 uint8* c = crs; 589 uint8* d = dst; 590 for (int32 x = area.left; x <= area.right; x++) { 591 // assumes backbuffer alpha = 255 592 // assuming pre-multiplied cursor bitmap 593 uint8 a = 255 - c[3]; 594 d[0] = ((s[0] * a + 255) >> 8) + c[0]; 595 d[1] = ((s[1] * a + 255) >> 8) + c[1]; 596 d[2] = ((s[2] * a + 255) >> 8) + c[2]; 597 598 s += 4; 599 c += 4; 600 d += 4; 601 } 602 crs += crsBPR; 603 src += srcBPR; 604 dst += width * 4; 605 } 606 } 607 // copy result to front buffer 608 _CopyToFront(buffer, width * 4, area.left, area.top, area.right, 609 area.bottom); 610 611 delete[] buffer; 612 } 613 } 614 615 616 // * source is assumed to be already at the right offset 617 // * source is assumed to be in B_RGBA32 format 618 // * location in front buffer is calculated 619 // * conversion from B_RGBA32 to format of front buffer is taken care of 620 void 621 HWInterface::_CopyToFront(uint8* src, uint32 srcBPR, int32 x, int32 y, 622 int32 right, int32 bottom) const 623 { 624 RenderingBuffer* frontBuffer = FrontBuffer(); 625 626 uint8* dst = (uint8*)frontBuffer->Bits(); 627 uint32 dstBPR = frontBuffer->BytesPerRow(); 628 629 // transfer, handle colorspace conversion 630 switch (frontBuffer->ColorSpace()) { 631 case B_RGB32: 632 case B_RGBA32: { 633 int32 bytes = (right - x + 1) * 4; 634 635 if (bytes > 0) { 636 // offset to left top pixel in dest buffer 637 dst += y * dstBPR + x * 4; 638 // copy 639 for (; y <= bottom; y++) { 640 // bytes is guaranteed to be multiple of 4 641 gfxcpy32(dst, src, bytes); 642 dst += dstBPR; 643 src += srcBPR; 644 } 645 } 646 break; 647 } 648 // NOTE: on R5, B_RGB24 bitmaps are not supported by DrawBitmap() 649 case B_RGB24: { 650 // offset to left top pixel in dest buffer 651 dst += y * dstBPR + x * 3; 652 int32 left = x; 653 // copy 654 for (; y <= bottom; y++) { 655 uint8* srcHandle = src; 656 uint8* dstHandle = dst; 657 for (x = left; x <= right; x++) { 658 dstHandle[0] = srcHandle[0]; 659 dstHandle[1] = srcHandle[1]; 660 dstHandle[2] = srcHandle[2]; 661 dstHandle += 3; 662 srcHandle += 4; 663 } 664 dst += dstBPR; 665 src += srcBPR; 666 } 667 break; 668 } 669 case B_RGB16: { 670 // offset to left top pixel in dest buffer 671 dst += y * dstBPR + x * 2; 672 int32 left = x; 673 // copy 674 // TODO: assumes BGR order, does this work on big endian as well? 675 for (; y <= bottom; y++) { 676 uint8* srcHandle = src; 677 uint16* dstHandle = (uint16*)dst; 678 for (x = left; x <= right; x++) { 679 *dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 8) | 680 ((srcHandle[1] & 0xfc) << 3) | 681 (srcHandle[0] >> 3)); 682 dstHandle ++; 683 srcHandle += 4; 684 } 685 dst += dstBPR; 686 src += srcBPR; 687 } 688 break; 689 } 690 case B_RGB15: 691 case B_RGBA15: { 692 // offset to left top pixel in dest buffer 693 dst += y * dstBPR + x * 2; 694 int32 left = x; 695 // copy 696 // TODO: assumes BGR order, does this work on big endian as well? 697 for (; y <= bottom; y++) { 698 uint8* srcHandle = src; 699 uint16* dstHandle = (uint16*)dst; 700 for (x = left; x <= right; x++) { 701 *dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 7) | 702 ((srcHandle[1] & 0xf8) << 2) | 703 (srcHandle[0] >> 3)); 704 dstHandle ++; 705 srcHandle += 4; 706 } 707 dst += dstBPR; 708 src += srcBPR; 709 } 710 break; 711 } 712 case B_CMAP8: { 713 const color_map *colorMap = SystemColorMap(); 714 // offset to left top pixel in dest buffer 715 dst += y * dstBPR + x; 716 int32 left = x; 717 uint16 index; 718 // copy 719 // TODO: assumes BGR order again 720 for (; y <= bottom; y++) { 721 uint8* srcHandle = src; 722 uint8* dstHandle = dst; 723 for (x = left; x <= right; x++) { 724 index = ((srcHandle[2] & 0xf8) << 7) | ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3); 725 *dstHandle = colorMap->index_map[index]; 726 dstHandle ++; 727 srcHandle += 4; 728 } 729 dst += dstBPR; 730 src += srcBPR; 731 } 732 733 break; 734 } 735 case B_GRAY8: 736 if (frontBuffer->Width() > dstBPR) { 737 // VGA 16 color grayscale planar mode 738 if (fVGADevice >= 0) { 739 vga_planar_blit_args args; 740 args.source = src; 741 args.source_bytes_per_row = srcBPR; 742 args.left = x; 743 args.top = y; 744 args.right = right; 745 args.bottom = bottom; 746 if (ioctl(fVGADevice, VGA_PLANAR_BLIT, &args, sizeof(args)) == 0) 747 break; 748 } 749 750 // Since we cannot set the plane, we do monochrome output 751 dst += y * dstBPR + x / 8; 752 int32 left = x; 753 754 // TODO: this is awfully slow... 755 // TODO: assumes BGR order 756 for (; y <= bottom; y++) { 757 uint8* srcHandle = src; 758 uint8* dstHandle = dst; 759 uint8 current8 = dstHandle[0]; 760 // we store 8 pixels before writing them back 761 762 for (x = left; x <= right; x++) { 763 uint8 pixel = (308 * srcHandle[2] + 600 * srcHandle[1] 764 + 116 * srcHandle[0]) / 1024; 765 srcHandle += 4; 766 767 if (pixel > 128) 768 current8 |= 0x80 >> (x & 7); 769 else 770 current8 &= ~(0x80 >> (x & 7)); 771 772 if ((x & 7) == 7) { 773 // last pixel in 8 pixel group 774 dstHandle[0] = current8; 775 dstHandle++; 776 current8 = dstHandle[0]; 777 } 778 } 779 780 if (x & 7) { 781 // last pixel has not been written yet 782 dstHandle[0] = current8; 783 } 784 dst += dstBPR; 785 src += srcBPR; 786 } 787 } else { 788 // offset to left top pixel in dest buffer 789 dst += y * dstBPR + x; 790 int32 left = x; 791 // copy 792 // TODO: assumes BGR order, does this work on big endian as well? 793 for (; y <= bottom; y++) { 794 uint8* srcHandle = src; 795 uint8* dstHandle = dst; 796 for (x = left; x <= right; x++) { 797 *dstHandle = (308 * srcHandle[2] + 600 * srcHandle[1] + 116 * srcHandle[0]) / 1024; 798 dstHandle ++; 799 srcHandle += 4; 800 } 801 dst += dstBPR; 802 src += srcBPR; 803 } 804 } 805 break; 806 807 default: 808 fprintf(stderr, "HWInterface::CopyBackToFront() - unsupported front " 809 "buffer format! (0x%x)\n", frontBuffer->ColorSpace()); 810 break; 811 } 812 } 813 814 815 // PRE: the object must be locked 816 IntRect 817 HWInterface::_CursorFrame() const 818 { 819 IntRect frame(0, 0, -1, -1); 820 if (fCursorAndDragBitmap && fCursorVisible) { 821 frame = fCursorAndDragBitmap->Bounds(); 822 frame.OffsetTo(fCursorLocation - fCursorAndDragBitmap->GetHotSpot()); 823 } 824 return frame; 825 } 826 827 828 void 829 HWInterface::_RestoreCursorArea() const 830 { 831 if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) { 832 _CopyToFront(fCursorAreaBackup->buffer, 833 fCursorAreaBackup->bpr, 834 fCursorAreaBackup->left, 835 fCursorAreaBackup->top, 836 fCursorAreaBackup->right, 837 fCursorAreaBackup->bottom); 838 839 fCursorAreaBackup->cursor_hidden = true; 840 } 841 } 842 843 // _AdoptDragBitmap 844 void 845 HWInterface::_AdoptDragBitmap(const ServerBitmap* bitmap, const BPoint& offset) 846 { 847 // TODO: support other colorspaces/convert bitmap 848 if (bitmap && !(bitmap->ColorSpace() == B_RGB32 849 || bitmap->ColorSpace() == B_RGBA32)) { 850 fprintf(stderr, "HWInterface::_AdoptDragBitmap() - bitmap has yet unsupported colorspace\n"); 851 return; 852 } 853 854 _RestoreCursorArea(); 855 BRect cursorFrame = _CursorFrame(); 856 857 if (fCursorAndDragBitmap && fCursorAndDragBitmap != fCursor) { 858 delete fCursorAndDragBitmap; 859 fCursorAndDragBitmap = NULL; 860 } 861 862 if (bitmap) { 863 BRect bitmapFrame = bitmap->Bounds(); 864 if (fCursor) { 865 // put bitmap frame and cursor frame into the same 866 // coordinate space (the cursor location is the origin) 867 bitmapFrame.OffsetTo(BPoint(-offset.x, -offset.y)); 868 869 BRect cursorFrame(fCursor->Bounds()); 870 BPoint hotspot(fCursor->GetHotSpot()); 871 // the hotspot is at the origin 872 cursorFrame.OffsetTo(-hotspot.x, -hotspot.y); 873 874 BRect combindedBounds = bitmapFrame | cursorFrame; 875 876 BPoint shift; 877 shift.x = -combindedBounds.left; 878 shift.y = -combindedBounds.top; 879 880 combindedBounds.OffsetBy(shift); 881 cursorFrame.OffsetBy(shift); 882 bitmapFrame.OffsetBy(shift); 883 884 fCursorAndDragBitmap = new ServerCursor(combindedBounds, 885 bitmap->ColorSpace(), 0, 886 hotspot + shift); 887 888 // clear the combined buffer 889 uint8* dst = (uint8*)fCursorAndDragBitmap->Bits(); 890 uint32 dstBPR = fCursorAndDragBitmap->BytesPerRow(); 891 892 memset(dst, 0, fCursorAndDragBitmap->BitsLength()); 893 894 // put drag bitmap into combined buffer 895 uint8* src = (uint8*)bitmap->Bits(); 896 uint32 srcBPR = bitmap->BytesPerRow(); 897 898 dst += (int32)bitmapFrame.top * dstBPR + (int32)bitmapFrame.left * 4; 899 900 uint32 width = bitmapFrame.IntegerWidth() + 1; 901 uint32 height = bitmapFrame.IntegerHeight() + 1; 902 903 for (uint32 y = 0; y < height; y++) { 904 memcpy(dst, src, srcBPR); 905 dst += dstBPR; 906 src += srcBPR; 907 } 908 909 // compose cursor into combined buffer 910 dst = (uint8*)fCursorAndDragBitmap->Bits(); 911 dst += (int32)cursorFrame.top * dstBPR + (int32)cursorFrame.left * 4; 912 913 src = (uint8*)fCursor->Bits(); 914 srcBPR = fCursor->BytesPerRow(); 915 916 width = cursorFrame.IntegerWidth() + 1; 917 height = cursorFrame.IntegerHeight() + 1; 918 919 for (uint32 y = 0; y < height; y++) { 920 uint8* d = dst; 921 uint8* s = src; 922 for (uint32 x = 0; x < width; x++) { 923 // takes two semi-transparent pixels 924 // with unassociated alpha (not pre-multiplied) 925 // and stays within non-premultiplied color space 926 if (s[3] > 0) { 927 if (s[3] == 255) { 928 d[0] = s[0]; 929 d[1] = s[1]; 930 d[2] = s[2]; 931 d[3] = 255; 932 } else { 933 uint8 alphaRest = 255 - s[3]; 934 uint32 alphaTemp = (65025 - alphaRest * (255 - d[3])); 935 uint32 alphaDest = d[3] * alphaRest; 936 uint32 alphaSrc = 255 * s[3]; 937 d[0] = (d[0] * alphaDest + s[0] * alphaSrc) / alphaTemp; 938 d[1] = (d[1] * alphaDest + s[1] * alphaSrc) / alphaTemp; 939 d[2] = (d[2] * alphaDest + s[2] * alphaSrc) / alphaTemp; 940 d[3] = alphaTemp / 255; 941 } 942 } 943 // TODO: make sure the alpha is always upside down, 944 // then it doesn't need to be done when drawing the cursor 945 // (see _DrawCursor()) 946 // d[3] = 255 - d[3]; 947 d += 4; 948 s += 4; 949 } 950 dst += dstBPR; 951 src += srcBPR; 952 } 953 954 // handle pre-multiplication with alpha 955 // for faster compositing during cursor drawing 956 width = combindedBounds.IntegerWidth() + 1; 957 height = combindedBounds.IntegerHeight() + 1; 958 959 dst = (uint8*)fCursorAndDragBitmap->Bits(); 960 961 for (uint32 y = 0; y < height; y++) { 962 uint8* d = dst; 963 for (uint32 x = 0; x < width; x++) { 964 d[0] = (d[0] * d[3]) >> 8; 965 d[1] = (d[1] * d[3]) >> 8; 966 d[2] = (d[2] * d[3]) >> 8; 967 d += 4; 968 } 969 dst += dstBPR; 970 } 971 } else { 972 fCursorAndDragBitmap = new ServerCursor(bitmap->Bits(), 973 bitmapFrame.IntegerWidth() + 1, 974 bitmapFrame.IntegerHeight() + 1, 975 bitmap->ColorSpace()); 976 fCursorAndDragBitmap->SetHotSpot(BPoint(-offset.x, -offset.y)); 977 } 978 } else { 979 fCursorAndDragBitmap = fCursor; 980 } 981 982 Invalidate(cursorFrame); 983 984 // NOTE: the EventDispatcher does the reference counting stuff for us 985 // TODO: You can not simply call Release() on a ServerBitmap like you 986 // can for a ServerCursor... it could be changed, but there are linking 987 // troubles with the test environment that need to be solved than. 988 // if (fDragBitmap) 989 // fDragBitmap->Release(); 990 fDragBitmap = bitmap; 991 fDragBitmapOffset = offset; 992 // if (fDragBitmap) 993 // fDragBitmap->Acquire(); 994 995 delete fCursorAreaBackup; 996 fCursorAreaBackup = NULL; 997 998 if (!fCursorAndDragBitmap) 999 return; 1000 1001 if (fCursorAndDragBitmap && !IsDoubleBuffered()) { 1002 BRect cursorBounds = fCursorAndDragBitmap->Bounds(); 1003 fCursorAreaBackup = new buffer_clip(cursorBounds.IntegerWidth() + 1, 1004 cursorBounds.IntegerHeight() + 1); 1005 } 1006 _DrawCursor(_CursorFrame()); 1007 } 1008 1009 1010 void 1011 HWInterface::_NotifyFrameBufferChanged() 1012 { 1013 BList listeners(fListeners); 1014 int32 count = listeners.CountItems(); 1015 for (int32 i = 0; i < count; i++) { 1016 HWInterfaceListener* listener 1017 = (HWInterfaceListener*)listeners.ItemAtFast(i); 1018 listener->FrameBufferChanged(); 1019 } 1020 } 1021 1022 1023 /*static*/ bool 1024 HWInterface::_IsValidMode(const display_mode& mode) 1025 { 1026 // TODO: more of those! 1027 if (mode.virtual_width < 320 1028 || mode.virtual_height < 200) 1029 return false; 1030 1031 return true; 1032 } 1033 1034