xref: /haiku/src/servers/app/drawing/HWInterface.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 uint8[width * height * 4];
590 			// TODO: cache this buffer
591 
592 		// offset into back buffer
593 		uint8* src = (uint8*)backBuffer->Bits();
594 		uint32 srcBPR = backBuffer->BytesPerRow();
595 		src += area.top * srcBPR + area.left * 4;
596 
597 		// offset into cursor bitmap
598 		uint8* crs = (uint8*)fCursorAndDragBitmap->Bits();
599 		uint32 crsBPR = fCursorAndDragBitmap->BytesPerRow();
600 		// since area is clipped to cf,
601 		// the diff between area.top and cf.top is always positive,
602 		// same for diff between area.left and cf.left
603 		crs += (area.top - (int32)floorf(cf.top)) * crsBPR
604 				+ (area.left - (int32)floorf(cf.left)) * 4;
605 
606 		uint8* dst = buffer;
607 
608 		if (fCursorAreaBackup && fCursorAreaBackup->buffer
609 			&& fFloatingOverlaysLock.Lock()) {
610 			fCursorAreaBackup->cursor_hidden = false;
611 			// remember which area the backup contains
612 			fCursorAreaBackup->left = area.left;
613 			fCursorAreaBackup->top = area.top;
614 			fCursorAreaBackup->right = area.right;
615 			fCursorAreaBackup->bottom = area.bottom;
616 			uint8* bup = fCursorAreaBackup->buffer;
617 			uint32 bupBPR = fCursorAreaBackup->bpr;
618 
619 			// blending and backup of drawing buffer
620 			for (int32 y = area.top; y <= area.bottom; y++) {
621 				uint8* s = src;
622 				uint8* c = crs;
623 				uint8* d = dst;
624 				uint8* b = bup;
625 
626 				for (int32 x = area.left; x <= area.right; x++) {
627 					*(uint32*)b = *(uint32*)s;
628 					// assumes backbuffer alpha = 255
629 					// assuming pre-multiplied cursor bitmap
630 					int a = 255 - c[3];
631 					d[0] = ((int)(b[0] * a + 255) >> 8) + c[0];
632 					d[1] = ((int)(b[1] * a + 255) >> 8) + c[1];
633 					d[2] = ((int)(b[2] * a + 255) >> 8) + c[2];
634 
635 					s += 4;
636 					c += 4;
637 					d += 4;
638 					b += 4;
639 				}
640 				crs += crsBPR;
641 				src += srcBPR;
642 				dst += width * 4;
643 				bup += bupBPR;
644 			}
645 			fFloatingOverlaysLock.Unlock();
646 		} else {
647 			// blending
648 			for (int32 y = area.top; y <= area.bottom; y++) {
649 				uint8* s = src;
650 				uint8* c = crs;
651 				uint8* d = dst;
652 				for (int32 x = area.left; x <= area.right; x++) {
653 					// assumes backbuffer alpha = 255
654 					// assuming pre-multiplied cursor bitmap
655 					uint8 a = 255 - c[3];
656 					d[0] = ((s[0] * a + 255) >> 8) + c[0];
657 					d[1] = ((s[1] * a + 255) >> 8) + c[1];
658 					d[2] = ((s[2] * a + 255) >> 8) + c[2];
659 
660 					s += 4;
661 					c += 4;
662 					d += 4;
663 				}
664 				crs += crsBPR;
665 				src += srcBPR;
666 				dst += width * 4;
667 			}
668 		}
669 		// copy result to front buffer
670 		_CopyToFront(buffer, width * 4, area.left, area.top, area.right,
671 			area.bottom);
672 
673 		delete[] buffer;
674 	}
675 }
676 
677 
678 /*!	- source is assumed to be already at the right offset
679 	- source is assumed to be in B_RGBA32 format
680 	- location in front buffer is calculated
681 	- conversion from B_RGBA32 to format of front buffer is taken care of
682 */
683 void
684 HWInterface::_CopyToFront(uint8* src, uint32 srcBPR, int32 x, int32 y,
685 	int32 right, int32 bottom) const
686 {
687 	RenderingBuffer* frontBuffer = FrontBuffer();
688 
689 	uint8* dst = (uint8*)frontBuffer->Bits();
690 	uint32 dstBPR = frontBuffer->BytesPerRow();
691 
692 	// transfer, handle colorspace conversion
693 	switch (frontBuffer->ColorSpace()) {
694 		case B_RGB32:
695 		case B_RGBA32:
696 		{
697 			int32 bytes = (right - x + 1) * 4;
698 
699 			if (bytes > 0) {
700 				// offset to left top pixel in dest buffer
701 				dst += y * dstBPR + x * 4;
702 				// copy
703 				for (; y <= bottom; y++) {
704 					// bytes is guaranteed to be multiple of 4
705 					gfxcpy32(dst, src, bytes);
706 					dst += dstBPR;
707 					src += srcBPR;
708 				}
709 			}
710 			break;
711 		}
712 
713 		case B_RGB24:
714 		{
715 			// offset to left top pixel in dest buffer
716 			dst += y * dstBPR + x * 3;
717 			int32 left = x;
718 			// copy
719 			for (; y <= bottom; y++) {
720 				uint8* srcHandle = src;
721 				uint8* dstHandle = dst;
722 				for (x = left; x <= right; x++) {
723 					dstHandle[0] = srcHandle[0];
724 					dstHandle[1] = srcHandle[1];
725 					dstHandle[2] = srcHandle[2];
726 					dstHandle += 3;
727 					srcHandle += 4;
728 				}
729 				dst += dstBPR;
730 				src += srcBPR;
731 			}
732 			break;
733 		}
734 
735 		case B_RGB16:
736 		{
737 			// offset to left top pixel in dest buffer
738 			dst += y * dstBPR + x * 2;
739 			int32 left = x;
740 			// copy
741 			// TODO: assumes BGR order, does this work on big endian as well?
742 			for (; y <= bottom; y++) {
743 				uint8* srcHandle = src;
744 				uint16* dstHandle = (uint16*)dst;
745 				for (x = left; x <= right; x++) {
746 					*dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 8)
747 						| ((srcHandle[1] & 0xfc) << 3) | (srcHandle[0] >> 3));
748 					dstHandle ++;
749 					srcHandle += 4;
750 				}
751 				dst += dstBPR;
752 				src += srcBPR;
753 			}
754 			break;
755 		}
756 
757 		case B_RGB15:
758 		case B_RGBA15:
759 		{
760 			// offset to left top pixel in dest buffer
761 			dst += y * dstBPR + x * 2;
762 			int32 left = x;
763 			// copy
764 			// TODO: assumes BGR order, does this work on big endian as well?
765 			for (; y <= bottom; y++) {
766 				uint8* srcHandle = src;
767 				uint16* dstHandle = (uint16*)dst;
768 				for (x = left; x <= right; x++) {
769 					*dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 7)
770 						| ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3));
771 					dstHandle ++;
772 					srcHandle += 4;
773 				}
774 				dst += dstBPR;
775 				src += srcBPR;
776 			}
777 			break;
778 		}
779 
780 		case B_CMAP8:
781 		{
782 			const color_map *colorMap = SystemColorMap();
783 			// offset to left top pixel in dest buffer
784 			dst += y * dstBPR + x;
785 			int32 left = x;
786 			uint16 index;
787 			// copy
788 			// TODO: assumes BGR order again
789 			for (; y <= bottom; y++) {
790 				uint8* srcHandle = src;
791 				uint8* dstHandle = dst;
792 				for (x = left; x <= right; x++) {
793 					index = ((srcHandle[2] & 0xf8) << 7)
794 						| ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3);
795 					*dstHandle = colorMap->index_map[index];
796 					dstHandle ++;
797 					srcHandle += 4;
798 				}
799 				dst += dstBPR;
800 				src += srcBPR;
801 			}
802 
803 			break;
804 		}
805 
806 		case B_GRAY8:
807 			if (frontBuffer->Width() > dstBPR) {
808 				// VGA 16 color grayscale planar mode
809 				if (fVGADevice >= 0) {
810 					vga_planar_blit_args args;
811 					args.source = src;
812 					args.source_bytes_per_row = srcBPR;
813 					args.left = x;
814 					args.top = y;
815 					args.right = right;
816 					args.bottom = bottom;
817 					if (ioctl(fVGADevice, VGA_PLANAR_BLIT, &args, sizeof(args))
818 							== 0)
819 						break;
820 				}
821 
822 				// Since we cannot set the plane, we do monochrome output
823 				dst += y * dstBPR + x / 8;
824 				int32 left = x;
825 
826 				// TODO: this is awfully slow...
827 				// TODO: assumes BGR order
828 				for (; y <= bottom; y++) {
829 					uint8* srcHandle = src;
830 					uint8* dstHandle = dst;
831 					uint8 current8 = dstHandle[0];
832 						// we store 8 pixels before writing them back
833 
834 					for (x = left; x <= right; x++) {
835 						uint8 pixel = (308 * srcHandle[2] + 600 * srcHandle[1]
836 							+ 116 * srcHandle[0]) / 1024;
837 						srcHandle += 4;
838 
839 						if (pixel > 128)
840 							current8 |= 0x80 >> (x & 7);
841 						else
842 							current8 &= ~(0x80 >> (x & 7));
843 
844 						if ((x & 7) == 7) {
845 							// last pixel in 8 pixel group
846 							dstHandle[0] = current8;
847 							dstHandle++;
848 							current8 = dstHandle[0];
849 						}
850 					}
851 
852 					if (x & 7) {
853 						// last pixel has not been written yet
854 						dstHandle[0] = current8;
855 					}
856 					dst += dstBPR;
857 					src += srcBPR;
858 				}
859 			} else {
860 				// offset to left top pixel in dest buffer
861 				dst += y * dstBPR + x;
862 				int32 left = x;
863 				// copy
864 				// TODO: assumes BGR order, does this work on big endian as well?
865 				for (; y <= bottom; y++) {
866 					uint8* srcHandle = src;
867 					uint8* dstHandle = dst;
868 					for (x = left; x <= right; x++) {
869 						*dstHandle = (308 * srcHandle[2] + 600 * srcHandle[1]
870 							+ 116 * srcHandle[0]) / 1024;
871 						dstHandle ++;
872 						srcHandle += 4;
873 					}
874 					dst += dstBPR;
875 					src += srcBPR;
876 				}
877 			}
878 			break;
879 
880 		default:
881 			fprintf(stderr, "HWInterface::CopyBackToFront() - unsupported "
882 				"front buffer format! (0x%x)\n", frontBuffer->ColorSpace());
883 			break;
884 	}
885 }
886 
887 
888 /*!	The object must be locked
889 */
890 IntRect
891 HWInterface::_CursorFrame() const
892 {
893 	IntRect frame(0, 0, -1, -1);
894 	if (fCursorAndDragBitmap && fCursorVisible && !fHardwareCursorEnabled) {
895 		frame = fCursorAndDragBitmap->Bounds();
896 		frame.OffsetTo(fCursorLocation - fCursorAndDragBitmap->GetHotSpot());
897 	}
898 	return frame;
899 }
900 
901 
902 void
903 HWInterface::_RestoreCursorArea() const
904 {
905 	if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) {
906 		_CopyToFront(fCursorAreaBackup->buffer, fCursorAreaBackup->bpr,
907 			fCursorAreaBackup->left, fCursorAreaBackup->top,
908 			fCursorAreaBackup->right, fCursorAreaBackup->bottom);
909 
910 		fCursorAreaBackup->cursor_hidden = true;
911 	}
912 }
913 
914 
915 void
916 HWInterface::_AdoptDragBitmap(const ServerBitmap* bitmap, const BPoint& offset)
917 {
918 	// TODO: support other colorspaces/convert bitmap
919 	if (bitmap && !(bitmap->ColorSpace() == B_RGB32
920 		|| bitmap->ColorSpace() == B_RGBA32)) {
921 		fprintf(stderr, "HWInterface::_AdoptDragBitmap() - bitmap has yet "
922 			"unsupported colorspace\n");
923 		return;
924 	}
925 
926 	_RestoreCursorArea();
927 	BRect cursorFrame = _CursorFrame();
928 
929 	if (fCursorAndDragBitmap && fCursorAndDragBitmap != fCursor) {
930 		delete fCursorAndDragBitmap;
931 		fCursorAndDragBitmap = NULL;
932 	}
933 
934 	if (bitmap != NULL) {
935 		BRect bitmapFrame = bitmap->Bounds();
936 		if (fCursor) {
937 			// put bitmap frame and cursor frame into the same
938 			// coordinate space (the cursor location is the origin)
939 			bitmapFrame.OffsetTo(BPoint(-offset.x, -offset.y));
940 
941 			BRect cursorFrame(fCursor->Bounds());
942 			BPoint hotspot(fCursor->GetHotSpot());
943 				// the hotspot is at the origin
944 			cursorFrame.OffsetTo(-hotspot.x, -hotspot.y);
945 
946 			BRect combindedBounds = bitmapFrame | cursorFrame;
947 
948 			BPoint shift;
949 			shift.x = -combindedBounds.left;
950 			shift.y = -combindedBounds.top;
951 
952 			combindedBounds.OffsetBy(shift);
953 			cursorFrame.OffsetBy(shift);
954 			bitmapFrame.OffsetBy(shift);
955 
956 			fCursorAndDragBitmap = new ServerCursor(combindedBounds,
957 				bitmap->ColorSpace(), 0, shift);
958 
959 			// clear the combined buffer
960 			uint8* dst = (uint8*)fCursorAndDragBitmap->Bits();
961 			uint32 dstBPR = fCursorAndDragBitmap->BytesPerRow();
962 
963 			memset(dst, 0, fCursorAndDragBitmap->BitsLength());
964 
965 			// put drag bitmap into combined buffer
966 			uint8* src = (uint8*)bitmap->Bits();
967 			uint32 srcBPR = bitmap->BytesPerRow();
968 
969 			dst += (int32)bitmapFrame.top * dstBPR
970 				+ (int32)bitmapFrame.left * 4;
971 
972 			uint32 width = bitmapFrame.IntegerWidth() + 1;
973 			uint32 height = bitmapFrame.IntegerHeight() + 1;
974 
975 			for (uint32 y = 0; y < height; y++) {
976 				memcpy(dst, src, srcBPR);
977 				dst += dstBPR;
978 				src += srcBPR;
979 			}
980 
981 			// compose cursor into combined buffer
982 			dst = (uint8*)fCursorAndDragBitmap->Bits();
983 			dst += (int32)cursorFrame.top * dstBPR
984 				+ (int32)cursorFrame.left * 4;
985 
986 			src = (uint8*)fCursor->Bits();
987 			srcBPR = fCursor->BytesPerRow();
988 
989 			width = cursorFrame.IntegerWidth() + 1;
990 			height = cursorFrame.IntegerHeight() + 1;
991 
992 			for (uint32 y = 0; y < height; y++) {
993 				uint8* d = dst;
994 				uint8* s = src;
995 				for (uint32 x = 0; x < width; x++) {
996 					// takes two semi-transparent pixels
997 					// with unassociated alpha (not pre-multiplied)
998 					// and stays within non-premultiplied color space
999 					if (s[3] > 0) {
1000 						if (s[3] == 255) {
1001 							d[0] = s[0];
1002 							d[1] = s[1];
1003 							d[2] = s[2];
1004 							d[3] = 255;
1005 						} else {
1006 							uint8 alphaRest = 255 - s[3];
1007 							uint32 alphaTemp
1008 								= (65025 - alphaRest * (255 - d[3]));
1009 							uint32 alphaDest = d[3] * alphaRest;
1010 							uint32 alphaSrc = 255 * s[3];
1011 							d[0] = (d[0] * alphaDest + s[0] * alphaSrc)
1012 								/ alphaTemp;
1013 							d[1] = (d[1] * alphaDest + s[1] * alphaSrc)
1014 								/ alphaTemp;
1015 							d[2] = (d[2] * alphaDest + s[2] * alphaSrc)
1016 								/ alphaTemp;
1017 							d[3] = alphaTemp / 255;
1018 						}
1019 					}
1020 					// TODO: make sure the alpha is always upside down,
1021 					// then it doesn't need to be done when drawing the cursor
1022 					// (see _DrawCursor())
1023 //					d[3] = 255 - d[3];
1024 					d += 4;
1025 					s += 4;
1026 				}
1027 				dst += dstBPR;
1028 				src += srcBPR;
1029 			}
1030 
1031 			// handle pre-multiplication with alpha
1032 			// for faster compositing during cursor drawing
1033 			width = combindedBounds.IntegerWidth() + 1;
1034 			height = combindedBounds.IntegerHeight() + 1;
1035 
1036 			dst = (uint8*)fCursorAndDragBitmap->Bits();
1037 
1038 			for (uint32 y = 0; y < height; y++) {
1039 				uint8* d = dst;
1040 				for (uint32 x = 0; x < width; x++) {
1041 					d[0] = (d[0] * d[3]) >> 8;
1042 					d[1] = (d[1] * d[3]) >> 8;
1043 					d[2] = (d[2] * d[3]) >> 8;
1044 					d += 4;
1045 				}
1046 				dst += dstBPR;
1047 			}
1048 		} else {
1049 			fCursorAndDragBitmap = new ServerCursor(bitmap->Bits(),
1050 				bitmapFrame.IntegerWidth() + 1, bitmapFrame.IntegerHeight() + 1,
1051 				bitmap->ColorSpace());
1052 			fCursorAndDragBitmap->SetHotSpot(BPoint(-offset.x, -offset.y));
1053 		}
1054 	} else {
1055 		fCursorAndDragBitmap = fCursor;
1056 	}
1057 
1058 	Invalidate(cursorFrame);
1059 
1060 // NOTE: the EventDispatcher does the reference counting stuff for us
1061 // TODO: You can not simply call Release() on a ServerBitmap like you
1062 // can for a ServerCursor... it could be changed, but there are linking
1063 // troubles with the test environment that need to be solved than.
1064 //	if (fDragBitmap)
1065 //		fDragBitmap->Release();
1066 	fDragBitmap = bitmap;
1067 	fDragBitmapOffset = offset;
1068 //	if (fDragBitmap)
1069 //		fDragBitmap->Acquire();
1070 
1071 	delete fCursorAreaBackup;
1072 	fCursorAreaBackup = NULL;
1073 
1074 	if (!fCursorAndDragBitmap)
1075 		return;
1076 
1077 	if (fCursorAndDragBitmap && !IsDoubleBuffered()) {
1078 		BRect cursorBounds = fCursorAndDragBitmap->Bounds();
1079 		fCursorAreaBackup = new buffer_clip(cursorBounds.IntegerWidth() + 1,
1080 			cursorBounds.IntegerHeight() + 1);
1081 	}
1082  	_DrawCursor(_CursorFrame());
1083 }
1084 
1085 
1086 void
1087 HWInterface::_NotifyFrameBufferChanged()
1088 {
1089 	BList listeners(fListeners);
1090 	int32 count = listeners.CountItems();
1091 	for (int32 i = 0; i < count; i++) {
1092 		HWInterfaceListener* listener
1093 			= (HWInterfaceListener*)listeners.ItemAtFast(i);
1094 		listener->FrameBufferChanged();
1095 	}
1096 }
1097 
1098 
1099 /*static*/ bool
1100 HWInterface::_IsValidMode(const display_mode& mode)
1101 {
1102 	// TODO: more of those!
1103 	if (mode.virtual_width < 320
1104 		|| mode.virtual_height < 200)
1105 		return false;
1106 
1107 	return true;
1108 }
1109 
1110