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