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