win32-vd_agent/vdagent/vdagent.cpp
Arnon Gilboa 8eb3e2e816 vdagent: support basic clipboard support (disabled by default)
-add CLIPBOARD_ENABLED ifdefs in the agent for disabling clipboard support
 -currently supports text only (UTF8)
2010-08-23 18:54:30 +03:00

793 lines
25 KiB
C++

/*
Copyright (C) 2009 Red Hat, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "vdcommon.h"
#include "desktop_layout.h"
#include <lmcons.h>
//#define CLIPBOARD_ENABLED
#define VD_AGENT_LOG_PATH TEXT("%svdagent.log")
#define VD_AGENT_WINCLASS_NAME TEXT("VDAGENT")
#define VD_INPUT_INTERVAL_MS 20
#define VD_TIMER_ID 1
class VDAgent {
public:
static VDAgent* get();
~VDAgent();
bool run();
private:
VDAgent();
void input_desktop_message_loop();
bool handle_mouse_event(VDAgentMouseState* state);
bool handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port);
bool handle_clipboard(VDAgentClipboard* clipboard, uint32_t size);
bool handle_control(VDPipeMessage* msg);
bool on_clipboard_change();
DWORD get_buttons_change(DWORD last_buttons_state, DWORD new_buttons_state,
DWORD mask, DWORD down_flag, DWORD up_flag);
static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
static VOID CALLBACK read_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap);
static VOID CALLBACK write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap);
static DWORD WINAPI event_thread_proc(LPVOID param);
static void dispatch_message(VDAgentMessage* msg, uint32_t port);
uint8_t* write_lock(DWORD bytes = 0);
void write_unlock(DWORD bytes = 0);
bool write_clipboard();
bool connect_pipe();
bool send_input();
private:
static VDAgent* _singleton;
HWND _hwnd;
HWND _hwnd_next_viewer;
bool _clipboard_changer;
DWORD _buttons_state;
LONG _mouse_x;
LONG _mouse_y;
INPUT _input;
DWORD _input_time;
HANDLE _desktop_switch_event;
VDAgentMessage* _in_msg;
uint32_t _in_msg_pos;
VDAgentMessage* _out_msg;
uint32_t _out_msg_pos;
uint32_t _out_msg_size;
bool _pending_input;
bool _pending_write;
bool _running;
DesktopLayout* _desktop_layout;
VDPipeState _pipe_state;
mutex_t _write_mutex;
VDLog* _log;
};
VDAgent* VDAgent::_singleton = NULL;
VDAgent* VDAgent::get()
{
if (!_singleton) {
_singleton = new VDAgent();
}
return _singleton;
}
VDAgent::VDAgent()
: _hwnd (NULL)
, _hwnd_next_viewer (NULL)
, _clipboard_changer (false)
, _buttons_state (0)
, _mouse_x (0)
, _mouse_y (0)
, _input_time (0)
, _in_msg (NULL)
, _in_msg_pos (0)
, _out_msg (NULL)
, _out_msg_pos (0)
, _out_msg_size (0)
, _pending_input (false)
, _pending_write (false)
, _running (false)
, _desktop_layout (NULL)
, _log (NULL)
{
TCHAR log_path[MAX_PATH];
TCHAR temp_path[MAX_PATH];
if (GetTempPath(MAX_PATH, temp_path)) {
swprintf_s(log_path, MAX_PATH, VD_AGENT_LOG_PATH, temp_path);
_log = VDLog::get(log_path);
}
ZeroMemory(&_input, sizeof(INPUT));
ZeroMemory(&_pipe_state, sizeof(VDPipeState));
MUTEX_INIT(_write_mutex);
_singleton = this;
}
VDAgent::~VDAgent()
{
delete _log;
}
DWORD WINAPI VDAgent::event_thread_proc(LPVOID param)
{
HANDLE desktop_event = OpenEvent(SYNCHRONIZE, FALSE, L"WinSta0_DesktopSwitch");
if (!desktop_event) {
vd_printf("OpenEvent() failed: %d", GetLastError());
return 1;
}
while (_singleton->_running) {
DWORD wait_ret = WaitForSingleObject(desktop_event, INFINITE);
switch (wait_ret) {
case WAIT_OBJECT_0:
SetEvent((HANDLE)param);
break;
case WAIT_TIMEOUT:
default:
vd_printf("WaitForSingleObject(): %u", wait_ret);
}
}
CloseHandle(desktop_event);
return 0;
}
bool VDAgent::run()
{
DWORD session_id;
DWORD event_thread_id;
HANDLE event_thread;
WNDCLASS wcls;
if (!ProcessIdToSessionId(GetCurrentProcessId(), &session_id)) {
vd_printf("ProcessIdToSessionId failed %u", GetLastError());
return false;
}
vd_printf("***Agent started in session %u***", session_id);
log_version();
if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) {
vd_printf("SetPriorityClass failed %u", GetLastError());
}
if (!SetProcessShutdownParameters(0x100, 0)) {
vd_printf("SetProcessShutdownParameters failed %u", GetLastError());
}
_desktop_switch_event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!_desktop_switch_event) {
vd_printf("CreateEvent() failed: %d", GetLastError());
return false;
}
memset(&wcls, 0, sizeof(wcls));
wcls.lpfnWndProc = &VDAgent::wnd_proc;
wcls.lpszClassName = VD_AGENT_WINCLASS_NAME;
if (!RegisterClass(&wcls)) {
vd_printf("RegisterClass() failed: %d", GetLastError());
return false;
}
_desktop_layout = new DesktopLayout();
if (_desktop_layout->get_display_count() == 0) {
vd_printf("No QXL devices!");
}
if (!connect_pipe()) {
CloseHandle(_desktop_switch_event);
delete _desktop_layout;
return false;
}
_running = true;
event_thread = CreateThread(NULL, 0, event_thread_proc, _desktop_switch_event, 0,
&event_thread_id);
if (!event_thread) {
vd_printf("CreateThread() failed: %d", GetLastError());
CloseHandle(_desktop_switch_event);
CloseHandle(_pipe_state.pipe);
delete _desktop_layout;
return false;
}
read_completion(0, 0, &_pipe_state.read.overlap);
while (_running) {
input_desktop_message_loop();
}
vd_printf("Agent stopped");
CloseHandle(event_thread);
CloseHandle(_desktop_switch_event);
CloseHandle(_pipe_state.pipe);
delete _desktop_layout;
return true;
}
void VDAgent::input_desktop_message_loop()
{
bool desktop_switch = false;
TCHAR desktop_name[MAX_PATH];
DWORD wait_ret;
HDESK hdesk;
MSG msg;
hdesk = OpenInputDesktop(0, FALSE, GENERIC_ALL);
if (!hdesk) {
vd_printf("OpenInputDesktop() failed: %u", GetLastError());
_running = false;
return;
}
if (!SetThreadDesktop(hdesk)) {
vd_printf("SetThreadDesktop failed %u", GetLastError());
_running = false;
return;
}
if (GetUserObjectInformation(hdesk, UOI_NAME, desktop_name, sizeof(desktop_name), NULL)) {
vd_printf("Desktop: %S", desktop_name);
} else {
vd_printf("GetUserObjectInformation failed %u", GetLastError());
}
_hwnd = CreateWindow(VD_AGENT_WINCLASS_NAME, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
if (!_hwnd) {
vd_printf("CreateWindow() failed: %u", GetLastError());
_running = false;
return;
}
_hwnd_next_viewer = SetClipboardViewer(_hwnd);
while (_running && !desktop_switch) {
wait_ret = MsgWaitForMultipleObjectsEx(1, &_desktop_switch_event, INFINITE, QS_ALLINPUT,
MWMO_ALERTABLE);
switch (wait_ret) {
case WAIT_OBJECT_0:
vd_printf("WinSta0_DesktopSwitch");
desktop_switch = true;
break;
case WAIT_OBJECT_0 + 1:
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
break;
case WAIT_IO_COMPLETION:
break;
case WAIT_TIMEOUT:
default:
vd_printf("MsgWaitForMultipleObjectsEx(): %u", wait_ret);
}
}
if (_pending_input) {
KillTimer(_hwnd, VD_TIMER_ID);
_pending_input = false;
}
ChangeClipboardChain(_hwnd, _hwnd_next_viewer);
DestroyWindow(_hwnd);
CloseDesktop(hdesk);
}
DWORD VDAgent::get_buttons_change(DWORD last_buttons_state, DWORD new_buttons_state,
DWORD mask, DWORD down_flag, DWORD up_flag)
{
DWORD ret = 0;
if (!(last_buttons_state & mask) && (new_buttons_state & mask)) {
ret = down_flag;
} else if ((last_buttons_state & mask) && !(new_buttons_state & mask)) {
ret = up_flag;
}
return ret;
}
bool VDAgent::send_input()
{
bool ret = true;
_desktop_layout->lock();
if (_pending_input) {
if (KillTimer(_hwnd, VD_TIMER_ID)) {
_pending_input = false;
} else {
vd_printf("KillTimer failed: %d", GetLastError());
_running = false;
_desktop_layout->unlock();
return false;
}
}
if (!SendInput(1, &_input, sizeof(INPUT)) && GetLastError() != ERROR_ACCESS_DENIED) {
vd_printf("SendInput failed: %d", GetLastError());
ret = _running = false;
}
_input_time = GetTickCount();
_desktop_layout->unlock();
return ret;
}
bool VDAgent::handle_mouse_event(VDAgentMouseState* state)
{
DisplayMode* mode = NULL;
DWORD mouse_move = 0;
DWORD buttons_change = 0;
DWORD mouse_wheel = 0;
bool ret = true;
ASSERT(_desktop_layout);
_desktop_layout->lock();
if (state->display_id < _desktop_layout->get_display_count()) {
mode = _desktop_layout->get_display(state->display_id);
}
if (!mode || !mode->get_attached()) {
_desktop_layout->unlock();
return true;
}
ZeroMemory(&_input, sizeof(INPUT));
_input.type = INPUT_MOUSE;
if (state->x != _mouse_x || state->y != _mouse_y) {
_mouse_x = state->x;
_mouse_y = state->y;
mouse_move = MOUSEEVENTF_MOVE;
_input.mi.dx = (mode->get_pos_x() + _mouse_x) * 0xffff /
_desktop_layout->get_total_width();
_input.mi.dy = (mode->get_pos_y() + _mouse_y) * 0xffff /
_desktop_layout->get_total_height();
}
if (state->buttons != _buttons_state) {
buttons_change = get_buttons_change(_buttons_state, state->buttons, VD_AGENT_LBUTTON_MASK,
MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP) |
get_buttons_change(_buttons_state, state->buttons, VD_AGENT_MBUTTON_MASK,
MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP) |
get_buttons_change(_buttons_state, state->buttons, VD_AGENT_RBUTTON_MASK,
MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP);
mouse_wheel = get_buttons_change(_buttons_state, state->buttons,
VD_AGENT_UBUTTON_MASK | VD_AGENT_DBUTTON_MASK,
MOUSEEVENTF_WHEEL, 0);
if (mouse_wheel) {
if (state->buttons & VD_AGENT_UBUTTON_MASK) {
_input.mi.mouseData = WHEEL_DELTA;
} else if (state->buttons & VD_AGENT_DBUTTON_MASK) {
_input.mi.mouseData = (DWORD)(-WHEEL_DELTA);
}
}
_buttons_state = state->buttons;
}
_input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | mouse_move |
mouse_wheel | buttons_change;
if ((mouse_move && GetTickCount() - _input_time > VD_INPUT_INTERVAL_MS) || buttons_change ||
mouse_wheel) {
ret = send_input();
} else if (!_pending_input) {
if (SetTimer(_hwnd, VD_TIMER_ID, VD_INPUT_INTERVAL_MS, NULL)) {
_pending_input = true;
} else {
vd_printf("SetTimer failed: %d", GetLastError());
_running = false;
ret = false;
}
}
_desktop_layout->unlock();
return ret;
}
bool VDAgent::handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port)
{
VDPipeMessage* reply_pipe_msg;
VDAgentMessage* reply_msg;
VDAgentReply* reply;
size_t display_count;
display_count = _desktop_layout->get_display_count();
for (uint32_t i = 0; i < display_count; i++) {
DisplayMode* mode = _desktop_layout->get_display(i);
ASSERT(mode);
if (i >= mon_config->num_of_monitors) {
vd_printf("%d. detached", i);
mode->set_attached(false);
continue;
}
VDAgentMonConfig* mon = &mon_config->monitors[i];
vd_printf("%d. %u*%u*%u (%d,%d) %u", i, mon->width, mon->height, mon->depth, mon->x,
mon->y, !!(mon_config->flags & VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS));
mode->set_res(mon->width, mon->height, mon->depth);
if (mon_config->flags & VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS) {
mode->set_pos(mon->x, mon->y);
}
mode->set_attached(true);
}
if (display_count) {
_desktop_layout->set_displays();
}
DWORD msg_size = VD_MESSAGE_HEADER_SIZE + sizeof(VDAgentReply);
reply_pipe_msg = (VDPipeMessage*)write_lock(msg_size);
if (!reply_pipe_msg) {
return false;
}
reply_pipe_msg->type = VD_AGENT_COMMAND;
reply_pipe_msg->opaque = port;
reply_pipe_msg->size = sizeof(VDAgentMessage) + sizeof(VDAgentReply);
reply_msg = (VDAgentMessage*)reply_pipe_msg->data;
reply_msg->protocol = VD_AGENT_PROTOCOL;
reply_msg->type = VD_AGENT_REPLY;
reply_msg->opaque = 0;
reply_msg->size = sizeof(VDAgentReply);
reply = (VDAgentReply*)reply_msg->data;
reply->type = VD_AGENT_MONITORS_CONFIG;
reply->error = display_count ? VD_AGENT_SUCCESS : VD_AGENT_ERROR;
write_unlock(msg_size);
if (!_pending_write) {
write_completion(0, 0, &_pipe_state.write.overlap);
}
return true;
}
//FIXME: currently supports text only
bool VDAgent::handle_clipboard(VDAgentClipboard* clipboard, uint32_t size)
{
HGLOBAL clip_data;
LPVOID clip_buf;
int clip_size;
UINT format;
bool ret;
switch (clipboard->type) {
case VD_AGENT_CLIPBOARD_UTF8_TEXT:
format = CF_UNICODETEXT;
break;
default:
vd_printf("Unsupported clipboard type %u", clipboard->type);
return false;
}
if (!OpenClipboard(_hwnd)) {
return false;
}
clip_size = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)clipboard->data, -1, NULL, 0);
if (!clip_size || !(clip_data = GlobalAlloc(GMEM_DDESHARE, clip_size * sizeof(WCHAR)))) {
CloseClipboard();
return false;
}
if (!(clip_buf = GlobalLock(clip_data))) {
GlobalFree(clip_data);
CloseClipboard();
return false;
}
ret = !!MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)clipboard->data, -1, (LPWSTR)clip_buf, clip_size);
GlobalUnlock(clip_data);
if (ret) {
EmptyClipboard();
//FIXME: nicify (compare clip_data)
_clipboard_changer = true;
ret = !!SetClipboardData(format, clip_data);
}
CloseClipboard();
return ret;
}
bool VDAgent::handle_control(VDPipeMessage* msg)
{
switch (msg->type) {
case VD_AGENT_RESET: {
vd_printf("Agent reset");
VDPipeMessage* ack = (VDPipeMessage*)write_lock(sizeof(VDPipeMessage));
if (!ack) {
return false;
}
ack->type = VD_AGENT_RESET_ACK;
ack->opaque = msg->opaque;
write_unlock(sizeof(VDPipeMessage));
if (!_pending_write) {
write_completion(0, 0, &_pipe_state.write.overlap);
}
break;
}
case VD_AGENT_QUIT:
vd_printf("Agent quit");
_running = false;
break;
default:
vd_printf("Unsupported control %u", msg->type);
return false;
}
return true;
}
#define MIN(a, b) ((a) > (b) ? (b) : (a))
//FIXME: cleanup
//FIXME: division to max size chunks should NOT be here, but in the service
// here we should write the max possible size to the pipe
bool VDAgent::write_clipboard()
{
ASSERT(_out_msg);
DWORD n = MIN(sizeof(VDPipeMessage) + _out_msg_size - _out_msg_pos, VD_AGENT_MAX_DATA_SIZE);
VDPipeMessage* pipe_msg = (VDPipeMessage*)write_lock(n);
if (!pipe_msg) {
return false;
}
pipe_msg->type = VD_AGENT_COMMAND;
//FIXME: client port should be in vd_agent.h
pipe_msg->opaque = 1;
pipe_msg->size = n - sizeof(VDPipeMessage);
memcpy(pipe_msg->data, (char*)_out_msg + _out_msg_pos, n - sizeof(VDPipeMessage));
write_unlock(n);
if (!_pending_write) {
write_completion(0, 0, &_pipe_state.write.overlap);
}
_out_msg_pos += (n - sizeof(VDPipeMessage));
if (_out_msg_pos == _out_msg_size) {
delete[] (uint8_t *)_out_msg;
_out_msg = NULL;
_out_msg_size = 0;
_out_msg_pos = 0;
}
return true;
}
//FIXME: currently supports text only
bool VDAgent::on_clipboard_change()
{
UINT format = CF_UNICODETEXT;
HANDLE clip_data;
LPVOID clip_buf;
int clip_size;
if (_out_msg) {
vd_printf("clipboard change is already pending");
return false;
}
if (!IsClipboardFormatAvailable(format) || !OpenClipboard(_hwnd)) {
return false;
}
if (!(clip_data = GetClipboardData(format)) || !(clip_buf = GlobalLock(clip_data))) {
CloseClipboard();
return false;
}
clip_size = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, -1, NULL, 0, NULL, NULL);
if (!clip_size) {
GlobalUnlock(clip_data);
CloseClipboard();
return false;
}
_out_msg_pos = 0;
_out_msg_size = sizeof(VDAgentMessage) + sizeof(VDAgentClipboard) + clip_size;
_out_msg = (VDAgentMessage*)new uint8_t[_out_msg_size];
_out_msg->protocol = VD_AGENT_PROTOCOL;
_out_msg->type = VD_AGENT_CLIPBOARD;
_out_msg->opaque = 0;
_out_msg->size = (uint32_t)(sizeof(VDAgentClipboard) + clip_size);
VDAgentClipboard* clipboard = (VDAgentClipboard*)_out_msg->data;
clipboard->type = VD_AGENT_CLIPBOARD_UTF8_TEXT;
WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, -1, (LPSTR)clipboard->data, clip_size, NULL, NULL);
GlobalUnlock(clip_data);
CloseClipboard();
return write_clipboard();
}
bool VDAgent::connect_pipe()
{
VDAgent* a = _singleton;
HANDLE pipe;
ZeroMemory(&a->_pipe_state, sizeof(VDPipeState));
if (!WaitNamedPipe(VD_SERVICE_PIPE_NAME, NMPWAIT_USE_DEFAULT_WAIT)) {
vd_printf("WaitNamedPipe() failed: %d", GetLastError());
return false;
}
//assuming vdservice created the named pipe before creating this vdagent process
pipe = CreateFile(VD_SERVICE_PIPE_NAME, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (pipe == INVALID_HANDLE_VALUE) {
vd_printf("CreateFile() failed: %d", GetLastError());
return false;
}
DWORD pipe_mode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
if (!SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL)) {
vd_printf("SetNamedPipeHandleState() failed: %d", GetLastError());
CloseHandle(pipe);
return false;
}
a->_pipe_state.pipe = pipe;
vd_printf("Connected to service pipe");
return true;
}
void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port)
{
VDAgent* a = _singleton;
switch (msg->type) {
case VD_AGENT_MOUSE_STATE:
if (!a->handle_mouse_event((VDAgentMouseState*)msg->data)) {
vd_printf("handle_mouse_event failed: %u", GetLastError());
a->_running = false;
}
break;
case VD_AGENT_MONITORS_CONFIG:
if (!a->handle_mon_config((VDAgentMonitorsConfig*)msg->data, port)) {
vd_printf("handle_mon_config failed: %u", GetLastError());
a->_running = false;
}
break;
#ifdef CLIPBOARD_ENABLED
case VD_AGENT_CLIPBOARD:
if (!a->handle_clipboard((VDAgentClipboard*)msg->data,
msg->size - sizeof(VDAgentClipboard))) {
vd_printf("handle_clipboard failed: %u", GetLastError());
a->_running = false;
}
break;
#endif // CLIPBOARD_ENABLED
default:
vd_printf("Unsupported message type %u size %u", msg->type, msg->size);
}
}
VOID CALLBACK VDAgent::read_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap)
{
VDAgent* a = _singleton;
VDPipeState* ps = &a->_pipe_state;
DWORD len;
if (!a->_running) {
return;
}
if (err) {
vd_printf("error %u", err);
a->_running = false;
return;
}
ps->read.end += bytes;
while (a->_running && (len = ps->read.end - ps->read.start) >= sizeof(VDPipeMessage)) {
VDPipeMessage* pipe_msg = (VDPipeMessage*)&ps->read.data[ps->read.start];
if (pipe_msg->type != VD_AGENT_COMMAND) {
a->handle_control(pipe_msg);
ps->read.start += sizeof(VDPipeMessage);
continue;
}
if (len < sizeof(VDPipeMessage) + pipe_msg->size) {
break;
}
//FIXME: cleanup, specific to one port
if (a->_in_msg_pos == 0 || pipe_msg->opaque == 2 /*FIXME!*/) {
if (len < VD_MESSAGE_HEADER_SIZE) {
break;
}
VDAgentMessage* msg = (VDAgentMessage*)pipe_msg->data;
if (msg->protocol != VD_AGENT_PROTOCOL) {
vd_printf("Invalid protocol %u bytes %u", msg->protocol, bytes);
a->_running = false;
break;
}
uint32_t msg_size = sizeof(VDAgentMessage) + msg->size;
if (pipe_msg->size == msg_size) {
dispatch_message(msg, pipe_msg->opaque);
} else {
ASSERT(pipe_msg->size < msg_size);
a->_in_msg = (VDAgentMessage*)new uint8_t[msg_size];
memcpy(a->_in_msg, pipe_msg->data, pipe_msg->size);
a->_in_msg_pos = pipe_msg->size;
}
} else {
memcpy((uint8_t*)a->_in_msg + a->_in_msg_pos, pipe_msg->data, pipe_msg->size);
a->_in_msg_pos += pipe_msg->size;
//vd_printf("DEBUG: pipe_msg size %u pos %u total %u", pipe_msg->size, a->_in_msg_pos, sizeof(VDAgentMessage) + a->_in_msg->size);
if (a->_in_msg_pos == sizeof(VDAgentMessage) + a->_in_msg->size) {
dispatch_message(a->_in_msg, 0);
a->_in_msg_pos = 0;
delete[] (uint8_t *)a->_in_msg;
a->_in_msg = NULL;
}
}
ps->read.start += (sizeof(VDPipeMessage) + pipe_msg->size);
if (ps->read.start == ps->read.end) {
ps->read.start = ps->read.end = 0;
}
}
if (a->_running && ps->read.end < sizeof(ps->read.data) &&
!ReadFileEx(ps->pipe, ps->read.data + ps->read.end, sizeof(ps->read.data) - ps->read.end,
overlap, read_completion)) {
vd_printf("ReadFileEx() failed: %u", GetLastError());
a->_running = false;
}
}
VOID CALLBACK VDAgent::write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap)
{
VDAgent* a = _singleton;
VDPipeState* ps = &a->_pipe_state;
a->_pending_write = false;
if (!a->_running) {
return;
}
if (err) {
vd_printf("error %u", err);
a->_running = false;
return;
}
if (!a->write_lock()) {
a->_running = false;
return;
}
ps->write.start += bytes;
if (ps->write.start == ps->write.end) {
ps->write.start = ps->write.end = 0;
//DEBUG
while (a->_out_msg && a->write_clipboard());
} else if (WriteFileEx(ps->pipe, ps->write.data + ps->write.start,
ps->write.end - ps->write.start, overlap, write_completion)) {
a->_pending_write = true;
} else {
vd_printf("WriteFileEx() failed: %u", GetLastError());
a->_running = false;
}
a->write_unlock();
}
uint8_t* VDAgent::write_lock(DWORD bytes)
{
MUTEX_LOCK(_write_mutex);
if (_pipe_state.write.end + bytes <= sizeof(_pipe_state.write.data)) {
return &_pipe_state.write.data[_pipe_state.write.end];
} else {
MUTEX_UNLOCK(_write_mutex);
vd_printf("write buffer is full");
return NULL;
}
}
void VDAgent::write_unlock(DWORD bytes)
{
_pipe_state.write.end += bytes;
MUTEX_UNLOCK(_write_mutex);
}
LRESULT CALLBACK VDAgent::wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
VDAgent* a = _singleton;
switch (message) {
case WM_DISPLAYCHANGE:
vd_printf("Display change");
a->_desktop_layout->get_displays();
break;
case WM_TIMER:
a->send_input();
break;
#ifdef CLIPBOARD_ENABLED
case WM_CHANGECBCHAIN:
if (a->_hwnd_next_viewer == (HWND)wparam) {
a->_hwnd_next_viewer = (HWND)lparam;
} else if (a->_hwnd_next_viewer) {
SendMessage(a->_hwnd_next_viewer, message, wparam, lparam);
}
break;
case WM_DRAWCLIPBOARD:
if (!a->_clipboard_changer) {
a->on_clipboard_change();
} else {
a->_clipboard_changer = false;
}
SendMessage(a->_hwnd_next_viewer, message, wparam, lparam);
break;
#endif // CLIPBOARD_ENABLED
default:
return DefWindowProc(hwnd, message, wparam, lparam);
}
return 0;
}
int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPTSTR cmd_line, int cmd_show)
{
VDAgent* vdagent = VDAgent::get();
vdagent->run();
delete vdagent;
return 0;
}