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