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