* Copyright 2005-2012, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "HWInterface.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <vesa/vesa_info.h>
#include "drawing_support.h"
#include "DrawingEngine.h"
#include "RenderingBuffer.h"
#include "SystemPalette.h"
using std::nothrow;
HWInterfaceListener::HWInterfaceListener()
{
}
HWInterfaceListener::~HWInterfaceListener()
{
}
HWInterface::HWInterface()
:
MultiLocker("hw interface lock"),
fFloatingOverlaysLock("floating overlays lock"),
fCursor(NULL),
fDragBitmap(NULL),
fDragBitmapOffset(0, 0),
fCursorAndDragBitmap(NULL),
fCursorVisible(false),
fCursorObscured(false),
fHardwareCursorEnabled(false),
fCursorLocation(0, 0),
fVGADevice(-1),
fListeners(20)
{
}
HWInterface::~HWInterface()
{
}
status_t
HWInterface::Initialize()
{
return MultiLocker::InitCheck();
}
DrawingEngine*
HWInterface::CreateDrawingEngine()
{
return new(std::nothrow) DrawingEngine(this);
}
EventStream*
HWInterface::CreateEventStream()
{
return NULL;
}
status_t
HWInterface::GetAccelerantPath(BString &path)
{
return B_ERROR;
}
status_t
HWInterface::GetDriverPath(BString &path)
{
return B_ERROR;
}
status_t
HWInterface::GetPreferredMode(display_mode* mode)
{
return B_NOT_SUPPORTED;
}
status_t
HWInterface::GetMonitorInfo(monitor_info* info)
{
return B_NOT_SUPPORTED;
}
void
HWInterface::SetCursor(ServerCursor* cursor)
{
if (!fFloatingOverlaysLock.Lock())
return;
if (fCursor.Get() != cursor) {
BRect oldFrame = _CursorFrame();
fCursor.SetTo(cursor);
Invalidate(oldFrame);
_AdoptDragBitmap();
Invalidate(_CursorFrame());
}
fFloatingOverlaysLock.Unlock();
}
ServerCursorReference
HWInterface::Cursor() const
{
if (!fFloatingOverlaysLock.Lock())
return ServerCursorReference(NULL);
fFloatingOverlaysLock.Unlock();
return fCursor;
}
ServerCursorReference
HWInterface::CursorAndDragBitmap() const
{
if (!fFloatingOverlaysLock.Lock())
return ServerCursorReference(NULL);
fFloatingOverlaysLock.Unlock();
return fCursorAndDragBitmap;
}
void
HWInterface::SetCursorVisible(bool visible)
{
if (!fFloatingOverlaysLock.Lock())
return;
if (fCursorVisible != visible) {
if (visible) {
fCursorVisible = visible;
fCursorObscured = false;
IntRect r = _CursorFrame();
_DrawCursor(r);
Invalidate(r);
} else {
IntRect r = _CursorFrame();
fCursorVisible = visible;
_RestoreCursorArea();
Invalidate(r);
}
}
fFloatingOverlaysLock.Unlock();
}
bool
HWInterface::IsCursorVisible()
{
bool visible = true;
if (fFloatingOverlaysLock.Lock()) {
visible = fCursorVisible;
fFloatingOverlaysLock.Unlock();
}
return visible;
}
void
HWInterface::ObscureCursor()
{
if (!fFloatingOverlaysLock.Lock())
return;
if (!fCursorObscured) {
SetCursorVisible(false);
fCursorObscured = true;
}
fFloatingOverlaysLock.Unlock();
}
void
HWInterface::MoveCursorTo(float x, float y)
{
if (!fFloatingOverlaysLock.Lock())
return;
BPoint p(x, y);
if (p != fCursorLocation) {
if (fCursorObscured) {
SetCursorVisible(true);
}
IntRect oldFrame = _CursorFrame();
fCursorLocation = p;
if (fCursorVisible) {
if (fCursorAreaBackup.IsSet()) {
_RestoreCursorArea();
_DrawCursor(_CursorFrame());
}
IntRect newFrame = _CursorFrame();
if (newFrame.Intersects(oldFrame))
Invalidate(oldFrame | newFrame);
else {
Invalidate(oldFrame);
Invalidate(newFrame);
}
}
}
fFloatingOverlaysLock.Unlock();
}
BPoint
HWInterface::CursorPosition()
{
BPoint location;
if (fFloatingOverlaysLock.Lock()) {
location = fCursorLocation;
fFloatingOverlaysLock.Unlock();
}
return location;
}
void
HWInterface::SetDragBitmap(const ServerBitmap* bitmap,
const BPoint& offsetFromCursor)
{
if (fFloatingOverlaysLock.Lock()) {
fDragBitmap.SetTo((ServerBitmap*)bitmap, false);
fDragBitmapOffset = offsetFromCursor;
_AdoptDragBitmap();
fFloatingOverlaysLock.Unlock();
}
}
RenderingBuffer*
HWInterface::DrawingBuffer() const
{
if (IsDoubleBuffered())
return BackBuffer();
return FrontBuffer();
}
*/
status_t
HWInterface::InvalidateRegion(const BRegion& region)
{
int32 count = region.CountRects();
for (int32 i = 0; i < count; i++) {
status_t result = Invalidate(region.RectAt(i));
if (result != B_OK)
return result;
}
return B_OK;
}
*/
status_t
HWInterface::Invalidate(const BRect& frame)
{
if (IsDoubleBuffered())
return CopyBackToFront(frame);
return B_OK;
}
*/
status_t
HWInterface::CopyBackToFront(const BRect& frame)
{
RenderingBuffer* frontBuffer = FrontBuffer();
RenderingBuffer* backBuffer = BackBuffer();
if (!backBuffer || !frontBuffer)
return B_NO_INIT;
IntRect area(frame);
IntRect bufferClip(backBuffer->Bounds());
if (area.IsValid() && area.Intersects(bufferClip)) {
area = bufferClip & area;
bool cursorLocked = fFloatingOverlaysLock.Lock();
BRegion region((BRect)area);
if (IsDoubleBuffered())
region.Exclude((clipping_rect)_CursorFrame());
_CopyBackToFront(region);
_DrawCursor(area);
if (cursorLocked)
fFloatingOverlaysLock.Unlock();
return B_OK;
}
return B_BAD_VALUE;
}
void
HWInterface::_CopyBackToFront( BRegion& region)
{
RenderingBuffer* backBuffer = BackBuffer();
uint32 srcBPR = backBuffer->BytesPerRow();
uint8* src = (uint8*)backBuffer->Bits();
int32 count = region.CountRects();
for (int32 i = 0; i < count; i++) {
clipping_rect r = region.RectAtInt(i);
uint8* srcOffset = src + r.top * srcBPR + r.left * 4;
_CopyToFront(srcOffset, srcBPR, r.left, r.top, r.right, r.bottom);
}
}
overlay_token
HWInterface::AcquireOverlayChannel()
{
return NULL;
}
void
HWInterface::ReleaseOverlayChannel(overlay_token token)
{
}
status_t
HWInterface::GetOverlayRestrictions(const Overlay* overlay,
overlay_restrictions* restrictions)
{
return B_NOT_SUPPORTED;
}
bool
HWInterface::CheckOverlayRestrictions(int32 width, int32 height,
color_space colorSpace)
{
return false;
}
const overlay_buffer*
HWInterface::AllocateOverlayBuffer(int32 width, int32 height, color_space space)
{
return NULL;
}
void
HWInterface::FreeOverlayBuffer(const overlay_buffer* buffer)
{
}
void
HWInterface::ConfigureOverlay(Overlay* overlay)
{
}
void
HWInterface::HideOverlay(Overlay* overlay)
{
}
bool
HWInterface::HideFloatingOverlays(const BRect& area)
{
if (IsDoubleBuffered())
return false;
if (!fFloatingOverlaysLock.Lock())
return false;
if (fCursorAreaBackup.IsSet() && !fCursorAreaBackup->cursor_hidden) {
BRect backupArea(fCursorAreaBackup->left, fCursorAreaBackup->top,
fCursorAreaBackup->right, fCursorAreaBackup->bottom);
if (area.Intersects(backupArea)) {
_RestoreCursorArea();
return true;
}
}
fFloatingOverlaysLock.Unlock();
return false;
}
bool
HWInterface::HideFloatingOverlays()
{
if (IsDoubleBuffered())
return false;
if (!fFloatingOverlaysLock.Lock())
return false;
_RestoreCursorArea();
return true;
}
void
HWInterface::ShowFloatingOverlays()
{
if (fCursorAreaBackup.IsSet() && fCursorAreaBackup->cursor_hidden)
_DrawCursor(_CursorFrame());
fFloatingOverlaysLock.Unlock();
}
bool
HWInterface::AddListener(HWInterfaceListener* listener)
{
if (listener && !fListeners.HasItem(listener))
return fListeners.AddItem(listener);
return false;
}
void
HWInterface::RemoveListener(HWInterfaceListener* listener)
{
fListeners.RemoveItem(listener);
}
\param area is where we potentially draw the cursor, the cursor
might be somewhere else, in which case this function does nothing
*/
void
HWInterface::_DrawCursor(IntRect area) const
{
RenderingBuffer* backBuffer = DrawingBuffer();
if (!backBuffer || !area.IsValid())
return;
IntRect cf = _CursorFrame();
area = backBuffer->Bounds() & area;
if (cf.IsValid() && area.Intersects(cf)) {
area = area & cf;
int32 width = area.right - area.left + 1;
int32 height = area.bottom - area.top + 1;
uint8* buffer = new(std::nothrow) uint8[width * height * 4];
if (buffer == NULL)
return;
uint8* src = (uint8*)backBuffer->Bits();
uint32 srcBPR = backBuffer->BytesPerRow();
src += area.top * srcBPR + area.left * 4;
uint8* crs = (uint8*)fCursorAndDragBitmap->Bits();
uint32 crsBPR = fCursorAndDragBitmap->BytesPerRow();
crs += (area.top - (int32)floorf(cf.top)) * crsBPR
+ (area.left - (int32)floorf(cf.left)) * 4;
uint8* dst = buffer;
if (fCursorAreaBackup.IsSet() && fCursorAreaBackup->buffer
&& fFloatingOverlaysLock.Lock()) {
fCursorAreaBackup->cursor_hidden = false;
fCursorAreaBackup->left = area.left;
fCursorAreaBackup->top = area.top;
fCursorAreaBackup->right = area.right;
fCursorAreaBackup->bottom = area.bottom;
uint8* bup = fCursorAreaBackup->buffer;
uint32 bupBPR = fCursorAreaBackup->bpr;
for (int32 y = area.top; y <= area.bottom; y++) {
uint8* s = src;
uint8* c = crs;
uint8* d = dst;
uint8* b = bup;
for (int32 x = area.left; x <= area.right; x++) {
*(uint32*)b = *(uint32*)s;
int a = 255 - c[3];
d[0] = ((int)(b[0] * a + 255) >> 8) + c[0];
d[1] = ((int)(b[1] * a + 255) >> 8) + c[1];
d[2] = ((int)(b[2] * a + 255) >> 8) + c[2];
s += 4;
c += 4;
d += 4;
b += 4;
}
crs += crsBPR;
src += srcBPR;
dst += width * 4;
bup += bupBPR;
}
fFloatingOverlaysLock.Unlock();
} else {
for (int32 y = area.top; y <= area.bottom; y++) {
uint8* s = src;
uint8* c = crs;
uint8* d = dst;
for (int32 x = area.left; x <= area.right; x++) {
uint8 a = 255 - c[3];
d[0] = ((s[0] * a + 255) >> 8) + c[0];
d[1] = ((s[1] * a + 255) >> 8) + c[1];
d[2] = ((s[2] * a + 255) >> 8) + c[2];
s += 4;
c += 4;
d += 4;
}
crs += crsBPR;
src += srcBPR;
dst += width * 4;
}
}
_CopyToFront(buffer, width * 4, area.left, area.top, area.right,
area.bottom);
delete[] buffer;
}
}
- source is assumed to be in B_RGBA32 format
- location in front buffer is calculated
- conversion from B_RGBA32 to format of front buffer is taken care of
*/
void
HWInterface::_CopyToFront(uint8* src, uint32 srcBPR, int32 x, int32 y,
int32 right, int32 bottom) const
{
RenderingBuffer* frontBuffer = FrontBuffer();
uint8* dst = (uint8*)frontBuffer->Bits();
uint32 dstBPR = frontBuffer->BytesPerRow();
switch (frontBuffer->ColorSpace()) {
case B_RGB32:
case B_RGBA32:
{
int32 bytes = (right - x + 1) * 4;
if (bytes > 0) {
dst += y * dstBPR + x * 4;
for (; y <= bottom; y++) {
memcpy(dst, src, bytes);
dst += dstBPR;
src += srcBPR;
}
}
break;
}
case B_RGB24:
{
dst += y * dstBPR + x * 3;
int32 left = x;
for (; y <= bottom; y++) {
uint8* srcHandle = src;
uint8* dstHandle = dst;
for (x = left; x <= right; x++) {
dstHandle[0] = srcHandle[0];
dstHandle[1] = srcHandle[1];
dstHandle[2] = srcHandle[2];
dstHandle += 3;
srcHandle += 4;
}
dst += dstBPR;
src += srcBPR;
}
break;
}
case B_RGB16:
{
dst += y * dstBPR + x * 2;
int32 left = x;
for (; y <= bottom; y++) {
uint8* srcHandle = src;
uint16* dstHandle = (uint16*)dst;
for (x = left; x <= right; x++) {
*dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 8)
| ((srcHandle[1] & 0xfc) << 3) | (srcHandle[0] >> 3));
dstHandle ++;
srcHandle += 4;
}
dst += dstBPR;
src += srcBPR;
}
break;
}
case B_RGB15:
case B_RGBA15:
{
dst += y * dstBPR + x * 2;
int32 left = x;
for (; y <= bottom; y++) {
uint8* srcHandle = src;
uint16* dstHandle = (uint16*)dst;
for (x = left; x <= right; x++) {
*dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 7)
| ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3));
dstHandle ++;
srcHandle += 4;
}
dst += dstBPR;
src += srcBPR;
}
break;
}
case B_CMAP8:
{
const color_map *colorMap = SystemColorMap();
dst += y * dstBPR + x;
int32 left = x;
uint16 index;
for (; y <= bottom; y++) {
uint8* srcHandle = src;
uint8* dstHandle = dst;
for (x = left; x <= right; x++) {
index = ((srcHandle[2] & 0xf8) << 7)
| ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3);
*dstHandle = colorMap->index_map[index];
dstHandle ++;
srcHandle += 4;
}
dst += dstBPR;
src += srcBPR;
}
break;
}
case B_GRAY8:
if (frontBuffer->Width() > dstBPR) {
if (fVGADevice >= 0) {
vga_planar_blit_args args;
args.source = src;
args.source_bytes_per_row = srcBPR;
args.left = x;
args.top = y;
args.right = right;
args.bottom = bottom;
if (ioctl(fVGADevice, VGA_PLANAR_BLIT, &args, sizeof(args))
== 0)
break;
}
dst += y * dstBPR + x / 8;
int32 left = x;
for (; y <= bottom; y++) {
uint8* srcHandle = src;
uint8* dstHandle = dst;
uint8 current8 = dstHandle[0];
for (x = left; x <= right; x++) {
uint8 pixel = (308 * srcHandle[2] + 600 * srcHandle[1]
+ 116 * srcHandle[0]) / 1024;
srcHandle += 4;
if (pixel > 128)
current8 |= 0x80 >> (x & 7);
else
current8 &= ~(0x80 >> (x & 7));
if ((x & 7) == 7) {
dstHandle[0] = current8;
dstHandle++;
current8 = dstHandle[0];
}
}
if (x & 7) {
dstHandle[0] = current8;
}
dst += dstBPR;
src += srcBPR;
}
} else {
dst += y * dstBPR + x;
int32 left = x;
for (; y <= bottom; y++) {
uint8* srcHandle = src;
uint8* dstHandle = dst;
for (x = left; x <= right; x++) {
*dstHandle = (308 * srcHandle[2] + 600 * srcHandle[1]
+ 116 * srcHandle[0]) / 1024;
dstHandle ++;
srcHandle += 4;
}
dst += dstBPR;
src += srcBPR;
}
}
break;
default:
fprintf(stderr, "HWInterface::CopyBackToFront() - unsupported "
"front buffer format! (0x%x)\n", frontBuffer->ColorSpace());
break;
}
}
*/
IntRect
HWInterface::_CursorFrame() const
{
IntRect frame(0, 0, -1, -1);
if (fCursorAndDragBitmap && fCursorVisible && !fHardwareCursorEnabled) {
frame = fCursorAndDragBitmap->Bounds();
frame.OffsetTo(fCursorLocation - fCursorAndDragBitmap->GetHotSpot());
}
return frame;
}
void
HWInterface::_RestoreCursorArea() const
{
if (fCursorAreaBackup.IsSet() && !fCursorAreaBackup->cursor_hidden) {
_CopyToFront(fCursorAreaBackup->buffer, fCursorAreaBackup->bpr,
fCursorAreaBackup->left, fCursorAreaBackup->top,
fCursorAreaBackup->right, fCursorAreaBackup->bottom);
fCursorAreaBackup->cursor_hidden = true;
}
}
void
HWInterface::_AdoptDragBitmap()
{
if (fDragBitmap && !(fDragBitmap->ColorSpace() == B_RGB32
|| fDragBitmap->ColorSpace() == B_RGBA32)) {
fprintf(stderr, "HWInterface::_AdoptDragBitmap() - bitmap has yet "
"unsupported colorspace\n");
return;
}
_RestoreCursorArea();
BRect oldCursorFrame = _CursorFrame();
if (fDragBitmap != NULL && fDragBitmap->Bounds().Width() > 0 && fDragBitmap->Bounds().Height() > 0) {
BRect bitmapFrame = fDragBitmap->Bounds();
if (fCursor) {
bitmapFrame.OffsetTo(BPoint(-fDragBitmapOffset.x, -fDragBitmapOffset.y));
BRect cursorFrame(fCursor->Bounds());
BPoint hotspot(fCursor->GetHotSpot());
cursorFrame.OffsetTo(-hotspot.x, -hotspot.y);
BRect combindedBounds = bitmapFrame | cursorFrame;
BPoint shift;
shift.x = -combindedBounds.left;
shift.y = -combindedBounds.top;
combindedBounds.OffsetBy(shift);
cursorFrame.OffsetBy(shift);
bitmapFrame.OffsetBy(shift);
fCursorAndDragBitmap.SetTo(new(std::nothrow) ServerCursor(combindedBounds,
fDragBitmap->ColorSpace(), 0, shift), true);
uint8* dst = fCursorAndDragBitmap ? (uint8*)fCursorAndDragBitmap->Bits() : NULL;
if (dst == NULL) {
fCursorAndDragBitmap = fCursor;
} else {
uint32 dstBPR = fCursorAndDragBitmap->BytesPerRow();
memset(dst, 0, fCursorAndDragBitmap->BitsLength());
uint8* src = (uint8*)fDragBitmap->Bits();
uint32 srcBPR = fDragBitmap->BytesPerRow();
dst += (int32)bitmapFrame.top * dstBPR
+ (int32)bitmapFrame.left * 4;
uint32 width = bitmapFrame.IntegerWidth() + 1;
uint32 height = bitmapFrame.IntegerHeight() + 1;
for (uint32 y = 0; y < height; y++) {
memcpy(dst, src, srcBPR);
dst += dstBPR;
src += srcBPR;
}
dst = (uint8*)fCursorAndDragBitmap->Bits();
dst += (int32)cursorFrame.top * dstBPR
+ (int32)cursorFrame.left * 4;
src = (uint8*)fCursor->Bits();
srcBPR = fCursor->BytesPerRow();
width = cursorFrame.IntegerWidth() + 1;
height = cursorFrame.IntegerHeight() + 1;
for (uint32 y = 0; y < height; y++) {
uint8* d = dst;
uint8* s = src;
for (uint32 x = 0; x < width; x++) {
if (s[3] > 0) {
if (s[3] == 255) {
d[0] = s[0];
d[1] = s[1];
d[2] = s[2];
d[3] = 255;
} else {
uint8 alphaRest = 255 - s[3];
uint32 alphaTemp
= (65025 - alphaRest * (255 - d[3]));
uint32 alphaDest = d[3] * alphaRest;
uint32 alphaSrc = 255 * s[3];
d[0] = (d[0] * alphaDest + s[0] * alphaSrc)
/ alphaTemp;
d[1] = (d[1] * alphaDest + s[1] * alphaSrc)
/ alphaTemp;
d[2] = (d[2] * alphaDest + s[2] * alphaSrc)
/ alphaTemp;
d[3] = alphaTemp / 255;
}
}
d += 4;
s += 4;
}
dst += dstBPR;
src += srcBPR;
}
width = combindedBounds.IntegerWidth() + 1;
height = combindedBounds.IntegerHeight() + 1;
dst = (uint8*)fCursorAndDragBitmap->Bits();
for (uint32 y = 0; y < height; y++) {
uint8* d = dst;
for (uint32 x = 0; x < width; x++) {
d[0] = (d[0] * d[3]) >> 8;
d[1] = (d[1] * d[3]) >> 8;
d[2] = (d[2] * d[3]) >> 8;
d += 4;
}
dst += dstBPR;
}
}
} else {
fCursorAndDragBitmap.SetTo(new ServerCursor(fDragBitmap->Bits(),
bitmapFrame.IntegerWidth() + 1, bitmapFrame.IntegerHeight() + 1,
fDragBitmap->ColorSpace()), true);
fCursorAndDragBitmap->SetHotSpot(BPoint(-fDragBitmapOffset.x, -fDragBitmapOffset.y));
}
} else {
fCursorAndDragBitmap = fCursor;
}
Invalidate(oldCursorFrame);
fCursorAreaBackup.Unset();
if (!fCursorAndDragBitmap)
return;
if (fCursorAndDragBitmap && !IsDoubleBuffered()) {
BRect cursorBounds = fCursorAndDragBitmap->Bounds();
fCursorAreaBackup.SetTo(new buffer_clip(cursorBounds.IntegerWidth() + 1,
cursorBounds.IntegerHeight() + 1));
if (fCursorAreaBackup->buffer == NULL)
fCursorAreaBackup.Unset();
}
_DrawCursor(_CursorFrame());
}
void
HWInterface::_NotifyFrameBufferChanged()
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
HWInterfaceListener* listener
= (HWInterfaceListener*)listeners.ItemAtFast(i);
listener->FrameBufferChanged();
}
}
void
HWInterface::_NotifyScreenChanged()
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
HWInterfaceListener* listener
= (HWInterfaceListener*)listeners.ItemAtFast(i);
listener->ScreenChanged(this);
}
}
bool
HWInterface::_IsValidMode(const display_mode& mode)
{
if (mode.virtual_width < 320
|| mode.virtual_height < 200)
return false;
return true;
}