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