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