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