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
27using std::nothrow;
28
29
30HWInterfaceListener::HWInterfaceListener()
31{
32}
33
34
35HWInterfaceListener::~HWInterfaceListener()
36{
37}
38
39
40// #pragma mark - HWInterface
41
42
43HWInterface::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
62HWInterface::~HWInterface()
63{
64	SetAsyncDoubleBuffered(false);
65}
66
67
68status_t
69HWInterface::Initialize()
70{
71	return MultiLocker::InitCheck();
72}
73
74
75DrawingEngine*
76HWInterface::CreateDrawingEngine()
77{
78	return new(std::nothrow) DrawingEngine(this);
79}
80
81
82EventStream*
83HWInterface::CreateEventStream()
84{
85	return NULL;
86}
87
88
89status_t
90HWInterface::GetAccelerantPath(BString &path)
91{
92	return B_ERROR;
93}
94
95
96status_t
97HWInterface::GetDriverPath(BString &path)
98{
99	return B_ERROR;
100}
101
102
103status_t
104HWInterface::GetPreferredMode(display_mode* mode)
105{
106	return B_NOT_SUPPORTED;
107}
108
109
110status_t
111HWInterface::GetMonitorInfo(monitor_info* info)
112{
113	return B_NOT_SUPPORTED;
114}
115
116
117// #pragma mark -
118
119
120void
121HWInterface::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
140ServerCursorReference
141HWInterface::Cursor() const
142{
143	if (!fFloatingOverlaysLock.Lock())
144		return ServerCursorReference(NULL);
145
146	fFloatingOverlaysLock.Unlock();
147	return fCursor;
148}
149
150
151ServerCursorReference
152HWInterface::CursorAndDragBitmap() const
153{
154	if (!fFloatingOverlaysLock.Lock())
155		return ServerCursorReference(NULL);
156
157	fFloatingOverlaysLock.Unlock();
158	return fCursorAndDragBitmap;
159}
160
161
162void
163HWInterface::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
191bool
192HWInterface::IsCursorVisible()
193{
194	bool visible = true;
195	if (fFloatingOverlaysLock.Lock()) {
196		visible = fCursorVisible;
197		fFloatingOverlaysLock.Unlock();
198	}
199	return visible;
200}
201
202
203void
204HWInterface::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
217void
218HWInterface::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
254BPoint
255HWInterface::CursorPosition()
256{
257	BPoint location;
258	if (fFloatingOverlaysLock.Lock()) {
259		location = fCursorLocation;
260		fFloatingOverlaysLock.Unlock();
261	}
262	return location;
263}
264
265
266void
267HWInterface::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
282RenderingBuffer*
283HWInterface::DrawingBuffer() const
284{
285	if (IsDoubleBuffered())
286		return BackBuffer();
287	return FrontBuffer();
288}
289
290
291void
292HWInterface::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*/
310status_t
311HWInterface::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*/
326status_t
327HWInterface::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*/
352status_t
353HWInterface::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
389void
390HWInterface::_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
410overlay_token
411HWInterface::AcquireOverlayChannel()
412{
413	return NULL;
414}
415
416
417void
418HWInterface::ReleaseOverlayChannel(overlay_token token)
419{
420}
421
422
423status_t
424HWInterface::GetOverlayRestrictions(const Overlay* overlay,
425	overlay_restrictions* restrictions)
426{
427	return B_NOT_SUPPORTED;
428}
429
430
431bool
432HWInterface::CheckOverlayRestrictions(int32 width, int32 height,
433	color_space colorSpace)
434{
435	return false;
436}
437
438
439const overlay_buffer*
440HWInterface::AllocateOverlayBuffer(int32 width, int32 height, color_space space)
441{
442	return NULL;
443}
444
445
446void
447HWInterface::FreeOverlayBuffer(const overlay_buffer* buffer)
448{
449}
450
451
452void
453HWInterface::ConfigureOverlay(Overlay* overlay)
454{
455}
456
457
458void
459HWInterface::HideOverlay(Overlay* overlay)
460{
461}
462
463
464// #pragma mark -
465
466
467bool
468HWInterface::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
488bool
489HWInterface::HideFloatingOverlays()
490{
491	if (IsDoubleBuffered())
492		return false;
493	if (!fFloatingOverlaysLock.Lock())
494		return false;
495
496	_RestoreCursorArea();
497	return true;
498}
499
500
501void
502HWInterface::ShowFloatingOverlays()
503{
504	if (fCursorAreaBackup.IsSet() && fCursorAreaBackup->cursor_hidden)
505		_DrawCursor(_CursorFrame());
506
507	fFloatingOverlaysLock.Unlock();
508}
509
510
511// #pragma mark -
512
513
514bool
515HWInterface::AddListener(HWInterfaceListener* listener)
516{
517	if (listener && !fListeners.HasItem(listener))
518		return fListeners.AddItem(listener);
519	return false;
520}
521
522
523void
524HWInterface::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*/
537void
538HWInterface::_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*/
657void
658HWInterface::_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*/
864IntRect
865HWInterface::_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
876void
877HWInterface::_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
889void
890HWInterface::_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
1051void
1052HWInterface::_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
1064void
1065HWInterface::_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
1078HWInterface::_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