xref: /haiku/src/servers/app/drawing/HWInterface.cpp (revision b06a48ab8f30b45916a9c157b992827779182163)
1 /*
2  * Copyright 2005-2007, 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 "drawing_support.h"
13 
14 #include "RenderingBuffer.h"
15 #include "SystemPalette.h"
16 #include "UpdateQueue.h"
17 
18 #include <vesa/vesa_info.h>
19 
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 
24 
25 HWInterfaceListener::HWInterfaceListener()
26 {
27 }
28 
29 
30 HWInterfaceListener::~HWInterfaceListener()
31 {
32 }
33 
34 
35 // #pragma mark - HWInterface
36 
37 
38 HWInterface::HWInterface(bool doubleBuffered)
39 	: MultiLocker("hw interface lock"),
40 	  fCursorAreaBackup(NULL),
41 	  fFloatingOverlaysLock("floating overlays lock"),
42 	  fCursor(NULL),
43 	  fDragBitmap(NULL),
44 	  fDragBitmapOffset(0, 0),
45 	  fCursorAndDragBitmap(NULL),
46 	  fCursorVisible(false),
47 	  fCursorObscured(false),
48 	  fCursorLocation(0, 0),
49 	  fDoubleBuffered(doubleBuffered),
50 	  fVGADevice(-1),
51 //	  fUpdateExecutor(new UpdateQueue(this))
52 	  fUpdateExecutor(NULL),
53 	  fListeners(20)
54 {
55 }
56 
57 // destructor
58 HWInterface::~HWInterface()
59 {
60 	delete fCursorAreaBackup;
61 
62 	// The standard cursor doesn't belong us - the drag bitmap might
63 	if (fCursor != fCursorAndDragBitmap)
64 		delete fCursorAndDragBitmap;
65 
66 	delete fUpdateExecutor;
67 }
68 
69 // Initialize
70 status_t
71 HWInterface::Initialize()
72 {
73 	return MultiLocker::InitCheck();
74 }
75 
76 
77 status_t
78 HWInterface::GetAccelerantPath(BString &path)
79 {
80 	return B_ERROR;
81 }
82 
83 
84 status_t
85 HWInterface::GetDriverPath(BString &path)
86 {
87 	return B_ERROR;
88 }
89 
90 
91 status_t
92 HWInterface::GetPreferredMode(display_mode* mode)
93 {
94 	return B_NOT_SUPPORTED;
95 }
96 
97 
98 status_t
99 HWInterface::GetMonitorInfo(monitor_info* info)
100 {
101 	return B_NOT_SUPPORTED;
102 }
103 
104 
105 // #pragma mark -
106 
107 
108 void
109 HWInterface::SetCursor(ServerCursor* cursor)
110 {
111 	if (!fFloatingOverlaysLock.Lock())
112 		return;
113 
114 	// TODO: if a bitmap is being dragged, it could
115 	// be considered iritating to the user to change
116 	// cursor shapes while something is dragged.
117 	// The disabled code below would prevent this (except
118 	// for the minor annoyance that the cursor is not
119 	// updated when the drag is over)
120 //	if (fDragBitmap) {
121 //		// TODO: like a "+" or "-" sign when dragging some files to indicate
122 //		//		the current drag mode?
123 //		UnlockExclusiveAccess();
124 //		return;
125 //	}
126 	if (fCursor != cursor) {
127 		BRect oldFrame = _CursorFrame();
128 
129 		if (fCursorAndDragBitmap == fCursor) {
130 			// make sure _AdoptDragBitmap doesn't delete a real cursor
131 			fCursorAndDragBitmap = NULL;
132 		}
133 
134 		if (fCursor)
135 			fCursor->Release();
136 
137 		fCursor = cursor;
138 
139 		if (fCursor)
140 			fCursor->Acquire();
141 
142 		Invalidate(oldFrame);
143 
144 		_AdoptDragBitmap(fDragBitmap, fDragBitmapOffset);
145 		Invalidate(_CursorFrame());
146 	}
147 	fFloatingOverlaysLock.Unlock();
148 }
149 
150 // Cursor
151 ServerCursorReference
152 HWInterface::Cursor() const
153 {
154 	if (!fFloatingOverlaysLock.Lock())
155 		return ServerCursorReference(NULL);
156 	ServerCursorReference reference(fCursor);
157 	fFloatingOverlaysLock.Unlock();
158 	return reference;
159 }
160 
161 // SetCursorVisible
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 // IsCursorVisible
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 // ObscureCursor
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 // MoveCursorTo
217 void
218 HWInterface::MoveCursorTo(const float& x, const 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) {
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 		_AdoptDragBitmap(bitmap, offsetFromCursor);
272 		fFloatingOverlaysLock.Unlock();
273 	}
274 }
275 
276 
277 // #pragma mark -
278 
279 
280 RenderingBuffer*
281 HWInterface::DrawingBuffer() const
282 {
283 	if (IsDoubleBuffered())
284 		return BackBuffer();
285 	return FrontBuffer();
286 }
287 
288 
289 bool
290 HWInterface::IsDoubleBuffered() const
291 {
292 	return fDoubleBuffered;
293 }
294 
295 
296 // * the object needs to be already locked!
297 status_t
298 HWInterface::Invalidate(const BRect& frame)
299 {
300 	if (IsDoubleBuffered()) {
301 		return CopyBackToFront(frame);
302 
303 // TODO: the remaining problem is the immediate wake up of the
304 // thread carrying out the updates, when I enable it, there
305 // seems to be a deadlock, but I didn't figure it out yet.
306 // Maybe the same bug is there without the wakeup, only, triggered
307 // less often.... scarry, huh?
308 /*		if (frame.IsValid()) {
309 			fUpdateExecutor->AddRect(frame);
310 			return B_OK;
311 		}
312 		return B_BAD_VALUE;*/
313 	} else {
314 //		_DrawCursor(frame);
315 	}
316 	return B_OK;
317 }
318 
319 
320 // * the object must already be locked!
321 status_t
322 HWInterface::CopyBackToFront(const BRect& frame)
323 {
324 	RenderingBuffer* frontBuffer = FrontBuffer();
325 	RenderingBuffer* backBuffer = BackBuffer();
326 
327 	if (!backBuffer || !frontBuffer)
328 		return B_NO_INIT;
329 
330 	// we need to mess with the area, but it is const
331 	IntRect area(frame);
332 	IntRect bufferClip(backBuffer->Bounds());
333 
334 	if (area.IsValid() && area.Intersects(bufferClip)) {
335 
336 		// make sure we don't copy out of bounds
337 		area = bufferClip & area;
338 
339 		BRegion region((BRect)area);
340 		if (IsDoubleBuffered())
341 			region.Exclude((clipping_rect)_CursorFrame());
342 
343 		CopyBackToFront(region);
344 
345 		_DrawCursor(area);
346 
347 		return B_OK;
348 	}
349 	return B_BAD_VALUE;
350 }
351 
352 
353 void
354 HWInterface::CopyBackToFront(/*const*/ BRegion& region)
355 {
356 	RenderingBuffer* backBuffer = BackBuffer();
357 
358 	uint32 srcBPR = backBuffer->BytesPerRow();
359 	uint8* src = (uint8*)backBuffer->Bits();
360 
361 	int32 count = region.CountRects();
362 	for (int32 i = 0; i < count; i++) {
363 		clipping_rect r = region.RectAtInt(i);
364 		// offset to left top pixel in source buffer (always B_RGBA32)
365 		uint8* srcOffset = src + r.top * srcBPR + r.left * 4;
366 		_CopyToFront(srcOffset, srcBPR, r.left, r.top, r.right, r.bottom);
367 	}
368 }
369 
370 
371 // #pragma mark -
372 
373 
374 overlay_token
375 HWInterface::AcquireOverlayChannel()
376 {
377 	return NULL;
378 }
379 
380 
381 void
382 HWInterface::ReleaseOverlayChannel(overlay_token token)
383 {
384 }
385 
386 
387 status_t
388 HWInterface::GetOverlayRestrictions(const Overlay* overlay,
389 	overlay_restrictions* restrictions)
390 {
391 	return B_NOT_SUPPORTED;
392 }
393 
394 
395 bool
396 HWInterface::CheckOverlayRestrictions(int32 width, int32 height,
397 	color_space colorSpace)
398 {
399 	return false;
400 }
401 
402 
403 const overlay_buffer*
404 HWInterface::AllocateOverlayBuffer(int32 width, int32 height, color_space space)
405 {
406 	return NULL;
407 }
408 
409 
410 void
411 HWInterface::FreeOverlayBuffer(const overlay_buffer* buffer)
412 {
413 }
414 
415 
416 void
417 HWInterface::ConfigureOverlay(Overlay* overlay)
418 {
419 }
420 
421 
422 void
423 HWInterface::HideOverlay(Overlay* overlay)
424 {
425 }
426 
427 
428 // #pragma mark -
429 
430 
431 bool
432 HWInterface::HideFloatingOverlays(const BRect& area)
433 {
434 	if (IsDoubleBuffered())
435 		return false;
436 	if (!fFloatingOverlaysLock.Lock())
437 		return false;
438 	if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) {
439 		BRect backupArea(fCursorAreaBackup->left,
440 						 fCursorAreaBackup->top,
441 						 fCursorAreaBackup->right,
442 						 fCursorAreaBackup->bottom);
443 		if (area.Intersects(backupArea)) {
444 			_RestoreCursorArea();
445 			// do not unlock the cursor lock
446 			return true;
447 		}
448 	}
449 	fFloatingOverlaysLock.Unlock();
450 	return false;
451 }
452 
453 
454 bool
455 HWInterface::HideFloatingOverlays()
456 {
457 	if (IsDoubleBuffered())
458 		return false;
459 	if (!fFloatingOverlaysLock.Lock())
460 		return false;
461 
462 	_RestoreCursorArea();
463 	return true;
464 }
465 
466 
467 void
468 HWInterface::ShowFloatingOverlays()
469 {
470 	if (fCursorAreaBackup && fCursorAreaBackup->cursor_hidden) {
471 		_DrawCursor(_CursorFrame());
472 	}
473 	fFloatingOverlaysLock.Unlock();
474 }
475 
476 
477 // #pragma mark -
478 
479 
480 bool
481 HWInterface::AddListener(HWInterfaceListener* listener)
482 {
483 	if (listener && !fListeners.HasItem(listener))
484 		return fListeners.AddItem(listener);
485 	return false;
486 }
487 
488 
489 void
490 HWInterface::RemoveListener(HWInterfaceListener* listener)
491 {
492 	fListeners.RemoveItem(listener);
493 }
494 
495 
496 // #pragma mark -
497 
498 
499 // * default implementation, can be used as fallback or for
500 //   software cursor
501 // * area is where we potentially draw the cursor, the cursor
502 //   might be somewhere else, in which case this function does nothing
503 void
504 HWInterface::_DrawCursor(IntRect area) const
505 {
506 	RenderingBuffer* backBuffer = DrawingBuffer();
507 	if (!backBuffer || !area.IsValid())
508 		return;
509 
510 	IntRect cf = _CursorFrame();
511 
512 	// make sure we don't copy out of bounds
513 	area = backBuffer->Bounds() & area;
514 
515 	if (cf.IsValid() && area.Intersects(cf)) {
516 
517 		// clip to common area
518 		area = area & cf;
519 
520 		int32 width = area.right - area.left + 1;
521 		int32 height = area.bottom - area.top + 1;
522 
523 		// make a bitmap from the backbuffer
524 		// that has the cursor blended on top of it
525 
526 		// blending buffer
527 		uint8* buffer = new uint8[width * height * 4];
528 			// TODO: cache this buffer
529 
530 		// offset into back buffer
531 		uint8* src = (uint8*)backBuffer->Bits();
532 		uint32 srcBPR = backBuffer->BytesPerRow();
533 		src += area.top * srcBPR + area.left * 4;
534 
535 		// offset into cursor bitmap
536 		uint8* crs = (uint8*)fCursorAndDragBitmap->Bits();
537 		uint32 crsBPR = fCursorAndDragBitmap->BytesPerRow();
538 		// since area is clipped to cf,
539 		// the diff between area.top and cf.top is always positive,
540 		// same for diff between area.left and cf.left
541 		crs += (area.top - (int32)floorf(cf.top)) * crsBPR
542 				+ (area.left - (int32)floorf(cf.left)) * 4;
543 
544 		uint8* dst = buffer;
545 
546 		if (fCursorAreaBackup && fCursorAreaBackup->buffer
547 			&& fFloatingOverlaysLock.Lock()) {
548 			fCursorAreaBackup->cursor_hidden = false;
549 			// remember which area the backup contains
550 			fCursorAreaBackup->left = area.left;
551 			fCursorAreaBackup->top = area.top;
552 			fCursorAreaBackup->right = area.right;
553 			fCursorAreaBackup->bottom = area.bottom;
554 			uint8* bup = fCursorAreaBackup->buffer;
555 			uint32 bupBPR = fCursorAreaBackup->bpr;
556 
557 			// blending and backup of drawing buffer
558 			for (int32 y = area.top; y <= area.bottom; y++) {
559 				uint8* s = src;
560 				uint8* c = crs;
561 				uint8* d = dst;
562 				uint8* b = bup;
563 
564 				for (int32 x = area.left; x <= area.right; x++) {
565 					*(uint32*)b = *(uint32*)s;
566 					// assumes backbuffer alpha = 255
567 					// assuming pre-multiplied cursor bitmap
568 					int a = 255 - c[3];
569 					d[0] = ((int)(b[0] * a + 255) >> 8) + c[0];
570 					d[1] = ((int)(b[1] * a + 255) >> 8) + c[1];
571 					d[2] = ((int)(b[2] * a + 255) >> 8) + c[2];
572 
573 					s += 4;
574 					c += 4;
575 					d += 4;
576 					b += 4;
577 				}
578 				crs += crsBPR;
579 				src += srcBPR;
580 				dst += width * 4;
581 				bup += bupBPR;
582 			}
583 			fFloatingOverlaysLock.Unlock();
584 		} else {
585 			// blending
586 			for (int32 y = area.top; y <= area.bottom; y++) {
587 				uint8* s = src;
588 				uint8* c = crs;
589 				uint8* d = dst;
590 				for (int32 x = area.left; x <= area.right; x++) {
591 					// assumes backbuffer alpha = 255
592 					// assuming pre-multiplied cursor bitmap
593 					uint8 a = 255 - c[3];
594 					d[0] = ((s[0] * a + 255) >> 8) + c[0];
595 					d[1] = ((s[1] * a + 255) >> 8) + c[1];
596 					d[2] = ((s[2] * a + 255) >> 8) + c[2];
597 
598 					s += 4;
599 					c += 4;
600 					d += 4;
601 				}
602 				crs += crsBPR;
603 				src += srcBPR;
604 				dst += width * 4;
605 			}
606 		}
607 		// copy result to front buffer
608 		_CopyToFront(buffer, width * 4, area.left, area.top, area.right,
609 			area.bottom);
610 
611 		delete[] buffer;
612 	}
613 }
614 
615 
616 // * source is assumed to be already at the right offset
617 // * source is assumed to be in B_RGBA32 format
618 // * location in front buffer is calculated
619 // * conversion from B_RGBA32 to format of front buffer is taken care of
620 void
621 HWInterface::_CopyToFront(uint8* src, uint32 srcBPR, int32 x, int32 y,
622 	int32 right, int32 bottom) const
623 {
624 	RenderingBuffer* frontBuffer = FrontBuffer();
625 
626 	uint8* dst = (uint8*)frontBuffer->Bits();
627 	uint32 dstBPR = frontBuffer->BytesPerRow();
628 
629 	// transfer, handle colorspace conversion
630 	switch (frontBuffer->ColorSpace()) {
631 		case B_RGB32:
632 		case B_RGBA32: {
633 			int32 bytes = (right - x + 1) * 4;
634 
635 			if (bytes > 0) {
636 				// offset to left top pixel in dest buffer
637 				dst += y * dstBPR + x * 4;
638 				// copy
639 				for (; y <= bottom; y++) {
640 					// bytes is guaranteed to be multiple of 4
641 					gfxcpy32(dst, src, bytes);
642 					dst += dstBPR;
643 					src += srcBPR;
644 				}
645 			}
646 			break;
647 		}
648 		// NOTE: on R5, B_RGB24 bitmaps are not supported by DrawBitmap()
649 		case B_RGB24: {
650 			// offset to left top pixel in dest buffer
651 			dst += y * dstBPR + x * 3;
652 			int32 left = x;
653 			// copy
654 			for (; y <= bottom; y++) {
655 				uint8* srcHandle = src;
656 				uint8* dstHandle = dst;
657 				for (x = left; x <= right; x++) {
658 					dstHandle[0] = srcHandle[0];
659 					dstHandle[1] = srcHandle[1];
660 					dstHandle[2] = srcHandle[2];
661 					dstHandle += 3;
662 					srcHandle += 4;
663 				}
664 				dst += dstBPR;
665 				src += srcBPR;
666 			}
667 			break;
668 		}
669 		case B_RGB16: {
670 			// offset to left top pixel in dest buffer
671 			dst += y * dstBPR + x * 2;
672 			int32 left = x;
673 			// copy
674 			// TODO: assumes BGR order, does this work on big endian as well?
675 			for (; y <= bottom; y++) {
676 				uint8* srcHandle = src;
677 				uint16* dstHandle = (uint16*)dst;
678 				for (x = left; x <= right; x++) {
679 					*dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 8) |
680 										  ((srcHandle[1] & 0xfc) << 3) |
681 										  (srcHandle[0] >> 3));
682 					dstHandle ++;
683 					srcHandle += 4;
684 				}
685 				dst += dstBPR;
686 				src += srcBPR;
687 			}
688 			break;
689 		}
690 		case B_RGB15:
691 		case B_RGBA15: {
692 			// offset to left top pixel in dest buffer
693 			dst += y * dstBPR + x * 2;
694 			int32 left = x;
695 			// copy
696 			// TODO: assumes BGR order, does this work on big endian as well?
697 			for (; y <= bottom; y++) {
698 				uint8* srcHandle = src;
699 				uint16* dstHandle = (uint16*)dst;
700 				for (x = left; x <= right; x++) {
701 					*dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 7) |
702 										  ((srcHandle[1] & 0xf8) << 2) |
703 										  (srcHandle[0] >> 3));
704 					dstHandle ++;
705 					srcHandle += 4;
706 				}
707 				dst += dstBPR;
708 				src += srcBPR;
709 			}
710 			break;
711 		}
712 		case B_CMAP8: {
713 			const color_map *colorMap = SystemColorMap();
714 			// offset to left top pixel in dest buffer
715 			dst += y * dstBPR + x;
716 			int32 left = x;
717 			uint16 index;
718 			// copy
719 			// TODO: assumes BGR order again
720 			for (; y <= bottom; y++) {
721 				uint8* srcHandle = src;
722 				uint8* dstHandle = dst;
723 				for (x = left; x <= right; x++) {
724 					index = ((srcHandle[2] & 0xf8) << 7) | ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3);
725 					*dstHandle = colorMap->index_map[index];
726 					dstHandle ++;
727 					srcHandle += 4;
728 				}
729 				dst += dstBPR;
730 				src += srcBPR;
731 			}
732 
733 			break;
734 		}
735 		case B_GRAY8:
736 			if (frontBuffer->Width() > dstBPR) {
737 				// VGA 16 color grayscale planar mode
738 				if (fVGADevice >= 0) {
739 					vga_planar_blit_args args;
740 					args.source = src;
741 					args.source_bytes_per_row = srcBPR;
742 					args.left = x;
743 					args.top = y;
744 					args.right = right;
745 					args.bottom = bottom;
746 					if (ioctl(fVGADevice, VGA_PLANAR_BLIT, &args, sizeof(args)) == 0)
747 						break;
748 				}
749 
750 				// Since we cannot set the plane, we do monochrome output
751 				dst += y * dstBPR + x / 8;
752 				int32 left = x;
753 
754 				// TODO: this is awfully slow...
755 				// TODO: assumes BGR order
756 				for (; y <= bottom; y++) {
757 					uint8* srcHandle = src;
758 					uint8* dstHandle = dst;
759 					uint8 current8 = dstHandle[0];
760 						// we store 8 pixels before writing them back
761 
762 					for (x = left; x <= right; x++) {
763 						uint8 pixel = (308 * srcHandle[2] + 600 * srcHandle[1]
764 							+ 116 * srcHandle[0]) / 1024;
765 						srcHandle += 4;
766 
767 						if (pixel > 128)
768 							current8 |= 0x80 >> (x & 7);
769 						else
770 							current8 &= ~(0x80 >> (x & 7));
771 
772 						if ((x & 7) == 7) {
773 							// last pixel in 8 pixel group
774 							dstHandle[0] = current8;
775 							dstHandle++;
776 							current8 = dstHandle[0];
777 						}
778 					}
779 
780 					if (x & 7) {
781 						// last pixel has not been written yet
782 						dstHandle[0] = current8;
783 					}
784 					dst += dstBPR;
785 					src += srcBPR;
786 				}
787 			} else {
788 				// offset to left top pixel in dest buffer
789 				dst += y * dstBPR + x;
790 				int32 left = x;
791 				// copy
792 				// TODO: assumes BGR order, does this work on big endian as well?
793 				for (; y <= bottom; y++) {
794 					uint8* srcHandle = src;
795 					uint8* dstHandle = dst;
796 					for (x = left; x <= right; x++) {
797 						*dstHandle = (308 * srcHandle[2] + 600 * srcHandle[1] + 116 * srcHandle[0]) / 1024;
798 						dstHandle ++;
799 						srcHandle += 4;
800 					}
801 					dst += dstBPR;
802 					src += srcBPR;
803 				}
804 			}
805 			break;
806 
807 		default:
808 			fprintf(stderr, "HWInterface::CopyBackToFront() - unsupported front "
809 				"buffer format! (0x%x)\n", frontBuffer->ColorSpace());
810 			break;
811 	}
812 }
813 
814 
815 // PRE: the object must be locked
816 IntRect
817 HWInterface::_CursorFrame() const
818 {
819 	IntRect frame(0, 0, -1, -1);
820 	if (fCursorAndDragBitmap && fCursorVisible) {
821 		frame = fCursorAndDragBitmap->Bounds();
822 		frame.OffsetTo(fCursorLocation - fCursorAndDragBitmap->GetHotSpot());
823 	}
824 	return frame;
825 }
826 
827 
828 void
829 HWInterface::_RestoreCursorArea() const
830 {
831 	if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) {
832 		_CopyToFront(fCursorAreaBackup->buffer,
833 					 fCursorAreaBackup->bpr,
834 					 fCursorAreaBackup->left,
835 					 fCursorAreaBackup->top,
836 					 fCursorAreaBackup->right,
837 					 fCursorAreaBackup->bottom);
838 
839 		fCursorAreaBackup->cursor_hidden = true;
840 	}
841 }
842 
843 // _AdoptDragBitmap
844 void
845 HWInterface::_AdoptDragBitmap(const ServerBitmap* bitmap, const BPoint& offset)
846 {
847 	// TODO: support other colorspaces/convert bitmap
848 	if (bitmap && !(bitmap->ColorSpace() == B_RGB32
849 		|| bitmap->ColorSpace() == B_RGBA32)) {
850 		fprintf(stderr, "HWInterface::_AdoptDragBitmap() - bitmap has yet unsupported colorspace\n");
851 		return;
852 	}
853 
854 	_RestoreCursorArea();
855 	BRect cursorFrame = _CursorFrame();
856 
857 	if (fCursorAndDragBitmap && fCursorAndDragBitmap != fCursor) {
858 		delete fCursorAndDragBitmap;
859 		fCursorAndDragBitmap = NULL;
860 	}
861 
862 	if (bitmap) {
863 		BRect bitmapFrame = bitmap->Bounds();
864 		if (fCursor) {
865 			// put bitmap frame and cursor frame into the same
866 			// coordinate space (the cursor location is the origin)
867 			bitmapFrame.OffsetTo(BPoint(-offset.x, -offset.y));
868 
869 			BRect cursorFrame(fCursor->Bounds());
870 			BPoint hotspot(fCursor->GetHotSpot());
871 				// the hotspot is at the origin
872 			cursorFrame.OffsetTo(-hotspot.x, -hotspot.y);
873 
874 			BRect combindedBounds = bitmapFrame | cursorFrame;
875 
876 			BPoint shift;
877 			shift.x = -combindedBounds.left;
878 			shift.y = -combindedBounds.top;
879 
880 			combindedBounds.OffsetBy(shift);
881 			cursorFrame.OffsetBy(shift);
882 			bitmapFrame.OffsetBy(shift);
883 
884 			fCursorAndDragBitmap = new ServerCursor(combindedBounds,
885 													bitmap->ColorSpace(), 0,
886 													hotspot + shift);
887 
888 			// clear the combined buffer
889 			uint8* dst = (uint8*)fCursorAndDragBitmap->Bits();
890 			uint32 dstBPR = fCursorAndDragBitmap->BytesPerRow();
891 
892 			memset(dst, 0, fCursorAndDragBitmap->BitsLength());
893 
894 			// put drag bitmap into combined buffer
895 			uint8* src = (uint8*)bitmap->Bits();
896 			uint32 srcBPR = bitmap->BytesPerRow();
897 
898 			dst += (int32)bitmapFrame.top * dstBPR + (int32)bitmapFrame.left * 4;
899 
900 			uint32 width = bitmapFrame.IntegerWidth() + 1;
901 			uint32 height = bitmapFrame.IntegerHeight() + 1;
902 
903 			for (uint32 y = 0; y < height; y++) {
904 				memcpy(dst, src, srcBPR);
905 				dst += dstBPR;
906 				src += srcBPR;
907 			}
908 
909 			// compose cursor into combined buffer
910 			dst = (uint8*)fCursorAndDragBitmap->Bits();
911 			dst += (int32)cursorFrame.top * dstBPR + (int32)cursorFrame.left * 4;
912 
913 			src = (uint8*)fCursor->Bits();
914 			srcBPR = fCursor->BytesPerRow();
915 
916 			width = cursorFrame.IntegerWidth() + 1;
917 			height = cursorFrame.IntegerHeight() + 1;
918 
919 			for (uint32 y = 0; y < height; y++) {
920 				uint8* d = dst;
921 				uint8* s = src;
922 				for (uint32 x = 0; x < width; x++) {
923 					// takes two semi-transparent pixels
924 					// with unassociated alpha (not pre-multiplied)
925 					// and stays within non-premultiplied color space
926 					if (s[3] > 0) {
927 						if (s[3] == 255) {
928 							d[0] = s[0];
929 							d[1] = s[1];
930 							d[2] = s[2];
931 							d[3] = 255;
932 						} else {
933 							uint8 alphaRest = 255 - s[3];
934 							uint32 alphaTemp = (65025 - alphaRest * (255 - d[3]));
935 							uint32 alphaDest = d[3] * alphaRest;
936 							uint32 alphaSrc = 255 * s[3];
937 							d[0] = (d[0] * alphaDest + s[0] * alphaSrc) / alphaTemp;
938 							d[1] = (d[1] * alphaDest + s[1] * alphaSrc) / alphaTemp;
939 							d[2] = (d[2] * alphaDest + s[2] * alphaSrc) / alphaTemp;
940 							d[3] = alphaTemp / 255;
941 						}
942 					}
943 					// TODO: make sure the alpha is always upside down,
944 					// then it doesn't need to be done when drawing the cursor
945 					// (see _DrawCursor())
946 //					d[3] = 255 - d[3];
947 					d += 4;
948 					s += 4;
949 				}
950 				dst += dstBPR;
951 				src += srcBPR;
952 			}
953 
954 			// handle pre-multiplication with alpha
955 			// for faster compositing during cursor drawing
956 			width = combindedBounds.IntegerWidth() + 1;
957 			height = combindedBounds.IntegerHeight() + 1;
958 
959 			dst = (uint8*)fCursorAndDragBitmap->Bits();
960 
961 			for (uint32 y = 0; y < height; y++) {
962 				uint8* d = dst;
963 				for (uint32 x = 0; x < width; x++) {
964 					d[0] = (d[0] * d[3]) >> 8;
965 					d[1] = (d[1] * d[3]) >> 8;
966 					d[2] = (d[2] * d[3]) >> 8;
967 					d += 4;
968 				}
969 				dst += dstBPR;
970 			}
971 		} else {
972 			fCursorAndDragBitmap = new ServerCursor(bitmap->Bits(),
973 													bitmapFrame.IntegerWidth() + 1,
974 													bitmapFrame.IntegerHeight() + 1,
975 													bitmap->ColorSpace());
976 			fCursorAndDragBitmap->SetHotSpot(BPoint(-offset.x, -offset.y));
977 		}
978 	} else {
979 		fCursorAndDragBitmap = fCursor;
980 	}
981 
982 	Invalidate(cursorFrame);
983 
984 // NOTE: the EventDispatcher does the reference counting stuff for us
985 // TODO: You can not simply call Release() on a ServerBitmap like you
986 // can for a ServerCursor... it could be changed, but there are linking
987 // troubles with the test environment that need to be solved than.
988 //	if (fDragBitmap)
989 //		fDragBitmap->Release();
990 	fDragBitmap = bitmap;
991 	fDragBitmapOffset = offset;
992 //	if (fDragBitmap)
993 //		fDragBitmap->Acquire();
994 
995 	delete fCursorAreaBackup;
996 	fCursorAreaBackup = NULL;
997 
998 	if (!fCursorAndDragBitmap)
999 		return;
1000 
1001 	if (fCursorAndDragBitmap && !IsDoubleBuffered()) {
1002 		BRect cursorBounds = fCursorAndDragBitmap->Bounds();
1003 		fCursorAreaBackup = new buffer_clip(cursorBounds.IntegerWidth() + 1,
1004 											cursorBounds.IntegerHeight() + 1);
1005 	}
1006  	_DrawCursor(_CursorFrame());
1007 }
1008 
1009 
1010 void
1011 HWInterface::_NotifyFrameBufferChanged()
1012 {
1013 	BList listeners(fListeners);
1014 	int32 count = listeners.CountItems();
1015 	for (int32 i = 0; i < count; i++) {
1016 		HWInterfaceListener* listener
1017 			= (HWInterfaceListener*)listeners.ItemAtFast(i);
1018 		listener->FrameBufferChanged();
1019 	}
1020 }
1021 
1022 
1023 /*static*/ bool
1024 HWInterface::_IsValidMode(const display_mode& mode)
1025 {
1026 	// TODO: more of those!
1027 	if (mode.virtual_width < 320
1028 		|| mode.virtual_height < 200)
1029 		return false;
1030 
1031 	return true;
1032 }
1033 
1034