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