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