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