* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
*/
#include "RemoteHWInterface.h"
#include "RemoteDrawingEngine.h"
#include "RemoteEventStream.h"
#include "RemoteMessage.h"
#include "NetReceiver.h"
#include "NetSender.h"
#include "StreamingRingBuffer.h"
#include "SystemPalette.h"
#include <Autolock.h>
#include <NetEndpoint.h>
#include <new>
#include <string.h>
#define TRACE(x...)
#define TRACE_ALWAYS(x...) debug_printf("RemoteHWInterface: " x)
#define TRACE_ERROR(x...) debug_printf("RemoteHWInterface: " x)
struct callback_info {
uint32 token;
RemoteHWInterface::CallbackFunction callback;
void* cookie;
};
RemoteHWInterface::RemoteHWInterface(const char* target)
:
HWInterface(),
fTarget(target),
fIsConnected(false),
fProtocolVersion(100),
fConnectionSpeed(0),
fListenPort(10901),
fListenEndpoint(NULL),
fSendBuffer(NULL),
fReceiveBuffer(NULL),
fSender(NULL),
fReceiver(NULL),
fEventThread(-1),
fEventStream(NULL),
fCallbackLocker("callback locker")
{
memset(&fFallbackMode, 0, sizeof(fFallbackMode));
fFallbackMode.virtual_width = 640;
fFallbackMode.virtual_height = 480;
fFallbackMode.space = B_RGB32;
_FillDisplayModeTiming(fFallbackMode);
fCurrentMode = fClientMode = fFallbackMode;
if (sscanf(fTarget, "%" B_SCNu16, &fListenPort) != 1) {
fInitStatus = B_BAD_VALUE;
return;
}
fListenEndpoint.SetTo(new(std::nothrow) BNetEndpoint());
if (!fListenEndpoint.IsSet()) {
fInitStatus = B_NO_MEMORY;
return;
}
fInitStatus = fListenEndpoint->Bind(fListenPort);
if (fInitStatus != B_OK)
return;
fSendBuffer.SetTo(new(std::nothrow) StreamingRingBuffer(16 * 1024));
if (!fSendBuffer.IsSet()) {
fInitStatus = B_NO_MEMORY;
return;
}
fInitStatus = fSendBuffer->InitCheck();
if (fInitStatus != B_OK)
return;
fReceiveBuffer.SetTo(new(std::nothrow) StreamingRingBuffer(16 * 1024));
if (!fReceiveBuffer.IsSet()) {
fInitStatus = B_NO_MEMORY;
return;
}
fInitStatus = fReceiveBuffer->InitCheck();
if (fInitStatus != B_OK)
return;
fReceiver.SetTo(new(std::nothrow) NetReceiver(fListenEndpoint.Get(), fReceiveBuffer.Get(),
_NewConnectionCallback, this));
if (!fReceiver.IsSet()) {
fInitStatus = B_NO_MEMORY;
return;
}
fEventStream.SetTo(new(std::nothrow) RemoteEventStream());
if (!fEventStream.IsSet()) {
fInitStatus = B_NO_MEMORY;
return;
}
fEventThread = spawn_thread(_EventThreadEntry, "remote event thread",
B_NORMAL_PRIORITY, this);
if (fEventThread < 0) {
fInitStatus = fEventThread;
return;
}
resume_thread(fEventThread);
}
RemoteHWInterface::~RemoteHWInterface()
{
fReceiver.Unset();
fReceiveBuffer.Unset();
fSendBuffer.Unset();
fSender.Unset();
fListenEndpoint.Unset();
fEventStream.Unset();
}
status_t
RemoteHWInterface::Initialize()
{
return fInitStatus;
}
status_t
RemoteHWInterface::Shutdown()
{
_Disconnect();
return B_OK;
}
DrawingEngine*
RemoteHWInterface::CreateDrawingEngine()
{
return new(std::nothrow) RemoteDrawingEngine(this);
}
EventStream*
RemoteHWInterface::CreateEventStream()
{
return fEventStream.Get();
}
status_t
RemoteHWInterface::AddCallback(uint32 token, CallbackFunction callback,
void* cookie)
{
BAutolock lock(fCallbackLocker);
int32 index = fCallbacks.BinarySearchIndexByKey(token, &_CallbackCompare);
if (index >= 0)
return B_NAME_IN_USE;
callback_info* info = new(std::nothrow) callback_info;
if (info == NULL)
return B_NO_MEMORY;
info->token = token;
info->callback = callback;
info->cookie = cookie;
fCallbacks.AddItem(info, -index - 1);
return B_OK;
}
bool
RemoteHWInterface::RemoveCallback(uint32 token)
{
BAutolock lock(fCallbackLocker);
int32 index = fCallbacks.BinarySearchIndexByKey(token, &_CallbackCompare);
if (index < 0)
return false;
delete fCallbacks.RemoveItemAt(index);
return true;
}
callback_info*
RemoteHWInterface::_FindCallback(uint32 token)
{
BAutolock lock(fCallbackLocker);
return fCallbacks.BinarySearchByKey(token, &_CallbackCompare);
}
int
RemoteHWInterface::_CallbackCompare(const uint32* key,
const callback_info* info)
{
if (info->token == *key)
return 0;
if (info->token < *key)
return -1;
return 1;
}
int32
RemoteHWInterface::_EventThreadEntry(void* data)
{
return ((RemoteHWInterface*)data)->_EventThread();
}
status_t
RemoteHWInterface::_EventThread()
{
RemoteMessage message(fReceiveBuffer.Get(), NULL);
while (true) {
uint16 code;
status_t result = message.NextMessage(code);
if (result != B_OK) {
TRACE_ERROR("failed to read message from receiver: %s\n",
strerror(result));
return result;
}
TRACE("got message code %" B_PRIu16 " with %" B_PRIu32 " bytes\n", code,
message.DataLeft());
if (code >= RP_MOUSE_MOVED && code <= RP_MODIFIERS_CHANGED) {
if (fEventStream->EventReceived(message))
continue;
}
switch (code) {
case RP_INIT_CONNECTION:
{
RemoteMessage reply(NULL, fSendBuffer.Get());
reply.Start(RP_INIT_CONNECTION);
status_t result = reply.Flush();
(void)result;
TRACE("init connection result: %s\n", strerror(result));
reply.Start(RP_SET_CURSOR);
reply.AddCursor(CursorAndDragBitmap().Get());
result = reply.Flush();
TRACE("init connection set cursor result: %s\n", strerror(result));
reply.Start(RP_SET_CURSOR_VISIBLE);
reply.Add(fCursorVisible);
result = reply.Flush();
TRACE("init connection set cursor visible result: %s\n", strerror(result));
BPoint position = CursorPosition();
reply.Start(RP_MOVE_CURSOR_TO);
reply.Add(position.x);
reply.Add(position.y);
result = reply.Flush();
TRACE("init connection set cursor position result: %s\n", strerror(result));
break;
}
case RP_UPDATE_DISPLAY_MODE:
{
int32 width, height;
message.Read(width);
result = message.Read(height);
if (result != B_OK) {
TRACE_ERROR("failed to read display mode\n");
break;
}
fIsConnected = true;
fClientMode.virtual_width = width;
fClientMode.virtual_height = height;
_FillDisplayModeTiming(fClientMode);
_NotifyScreenChanged();
break;
}
case RP_GET_SYSTEM_PALETTE:
{
RemoteMessage reply(NULL, fSendBuffer.Get());
reply.Start(RP_GET_SYSTEM_PALETTE_RESULT);
const color_map *map = SystemColorMap();
uint32 count = (uint32)B_COUNT_OF(map->color_list);
reply.Add(count);
for (size_t i = 0; i < count; i++) {
const rgb_color &color = map->color_list[i];
reply.Add(color.red);
reply.Add(color.green);
reply.Add(color.blue);
reply.Add(color.alpha);
}
break;
}
default:
{
uint32 token;
if (message.Read(token) == B_OK) {
callback_info* info = _FindCallback(token);
if (info != NULL && info->callback(info->cookie, message))
break;
}
TRACE_ERROR("unhandled remote event code %u\n", code);
break;
}
}
}
}
status_t
RemoteHWInterface::_NewConnectionCallback(void *cookie, BNetEndpoint &endpoint)
{
return ((RemoteHWInterface *)cookie)->_NewConnection(endpoint);
}
status_t
RemoteHWInterface::_NewConnection(BNetEndpoint &endpoint)
{
fSender.Unset();
fSendBuffer->MakeEmpty();
BNetEndpoint *sendEndpoint = new(std::nothrow) BNetEndpoint(endpoint);
if (sendEndpoint == NULL)
return B_NO_MEMORY;
fSender.SetTo(new(std::nothrow) NetSender(sendEndpoint, fSendBuffer.Get()));
if (!fSender.IsSet()) {
delete sendEndpoint;
return B_NO_MEMORY;
}
return B_OK;
}
void
RemoteHWInterface::_Disconnect()
{
if (fIsConnected) {
RemoteMessage message(NULL, fSendBuffer.Get());
message.Start(RP_CLOSE_CONNECTION);
message.Flush();
fIsConnected = false;
}
if (fListenEndpoint.IsSet())
fListenEndpoint->Close();
}
status_t
RemoteHWInterface::SetMode(const display_mode& mode)
{
TRACE("set mode: %" B_PRIu16 " %" B_PRIu16 "\n", mode.virtual_width,
mode.virtual_height);
fCurrentMode = mode;
return B_OK;
}
void
RemoteHWInterface::GetMode(display_mode* mode)
{
if (mode == NULL || !ReadLock())
return;
*mode = fCurrentMode;
ReadUnlock();
TRACE("get mode: %" B_PRIu16 " %" B_PRIu16 "\n", mode->virtual_width,
mode->virtual_height);
}
status_t
RemoteHWInterface::GetPreferredMode(display_mode* mode)
{
*mode = fClientMode;
return B_OK;
}
status_t
RemoteHWInterface::GetDeviceInfo(accelerant_device_info* info)
{
if (!ReadLock())
return B_ERROR;
info->version = fProtocolVersion;
info->dac_speed = fConnectionSpeed;
info->memory = 33554432;
strlcpy(info->name, "Haiku, Inc. RemoteHWInterface", sizeof(info->name));
strlcpy(info->chipset, "Haiku, Inc. Chipset", sizeof(info->chipset));
strlcpy(info->serial_no, fTarget, sizeof(info->serial_no));
ReadUnlock();
return B_OK;
}
status_t
RemoteHWInterface::GetFrameBufferConfig(frame_buffer_config& config)
{
return B_UNSUPPORTED;
}
status_t
RemoteHWInterface::GetModeList(display_mode** _modes, uint32* _count)
{
AutoReadLocker _(this);
display_mode* modes = new(std::nothrow) display_mode[2];
if (modes == NULL)
return B_NO_MEMORY;
modes[0] = fFallbackMode;
modes[1] = fClientMode;
*_modes = modes;
*_count = 2;
return B_OK;
}
status_t
RemoteHWInterface::GetPixelClockLimits(display_mode* mode, uint32* low,
uint32* high)
{
TRACE("get pixel clock limits unsupported\n");
return B_UNSUPPORTED;
}
status_t
RemoteHWInterface::GetTimingConstraints(display_timing_constraints* constraints)
{
TRACE("get timing constraints unsupported\n");
return B_UNSUPPORTED;
}
status_t
RemoteHWInterface::ProposeMode(display_mode* candidate, const display_mode* low,
const display_mode* high)
{
TRACE("propose mode: %" B_PRIu16 " %" B_PRIu16 "\n",
candidate->virtual_width, candidate->virtual_height);
return B_OK;
}
status_t
RemoteHWInterface::SetDPMSMode(uint32 state)
{
return B_UNSUPPORTED;
}
uint32
RemoteHWInterface::DPMSMode()
{
return B_UNSUPPORTED;
}
uint32
RemoteHWInterface::DPMSCapabilities()
{
return 0;
}
status_t
RemoteHWInterface::SetBrightness(float)
{
return B_UNSUPPORTED;
}
status_t
RemoteHWInterface::GetBrightness(float*)
{
return B_UNSUPPORTED;
}
sem_id
RemoteHWInterface::RetraceSemaphore()
{
return -1;
}
status_t
RemoteHWInterface::WaitForRetrace(bigtime_t timeout)
{
return B_UNSUPPORTED;
}
void
RemoteHWInterface::SetCursor(ServerCursor* cursor)
{
HWInterface::SetCursor(cursor);
RemoteMessage message(NULL, fSendBuffer.Get());
message.Start(RP_SET_CURSOR);
message.AddCursor(CursorAndDragBitmap().Get());
}
void
RemoteHWInterface::SetCursorVisible(bool visible)
{
HWInterface::SetCursorVisible(visible);
RemoteMessage message(NULL, fSendBuffer.Get());
message.Start(RP_SET_CURSOR_VISIBLE);
message.Add(visible);
}
void
RemoteHWInterface::MoveCursorTo(float x, float y)
{
HWInterface::MoveCursorTo(x, y);
RemoteMessage message(NULL, fSendBuffer.Get());
message.Start(RP_MOVE_CURSOR_TO);
message.Add(x);
message.Add(y);
}
void
RemoteHWInterface::SetDragBitmap(const ServerBitmap* bitmap,
const BPoint& offsetFromCursor)
{
HWInterface::SetDragBitmap(bitmap, offsetFromCursor);
RemoteMessage message(NULL, fSendBuffer.Get());
message.Start(RP_SET_CURSOR);
message.AddCursor(CursorAndDragBitmap().Get());
}
RenderingBuffer*
RemoteHWInterface::FrontBuffer() const
{
return NULL;
}
RenderingBuffer*
RemoteHWInterface::BackBuffer() const
{
return NULL;
}
bool
RemoteHWInterface::IsDoubleBuffered() const
{
return false;
}
status_t
RemoteHWInterface::InvalidateRegion(const BRegion& region)
{
RemoteMessage message(NULL, fSendBuffer.Get());
message.Start(RP_INVALIDATE_REGION);
message.AddRegion(region);
return B_OK;
}
status_t
RemoteHWInterface::Invalidate(const BRect& frame)
{
RemoteMessage message(NULL, fSendBuffer.Get());
message.Start(RP_INVALIDATE_RECT);
message.Add(frame);
return B_OK;
}
status_t
RemoteHWInterface::CopyBackToFront(const BRect& frame)
{
return B_OK;
}
void
RemoteHWInterface::_FillDisplayModeTiming(display_mode &mode)
{
mode.timing.pixel_clock
= (uint64_t)mode.virtual_width * mode.virtual_height * 60 / 1000;
mode.timing.h_display = mode.timing.h_sync_start = mode.timing.h_sync_end
= mode.timing.h_total = mode.virtual_width;
mode.timing.v_display = mode.timing.v_sync_start = mode.timing.v_sync_end
= mode.timing.v_total = mode.virtual_height;
}