diff options
-rw-r--r-- | vdagent/vdagent.cpp | 239 |
1 files changed, 109 insertions, 130 deletions
diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp index 2bb466d..85244c0 100644 --- a/vdagent/vdagent.cpp +++ b/vdagent/vdagent.cpp @@ -16,8 +16,6 @@ */ #include "vdcommon.h" -#include "virtio_vdi_port.h" -#include "pci_vdi_port.h" #include "desktop_layout.h" #include "display_setting.h" #include "ximage.h" @@ -65,11 +63,7 @@ typedef struct ALIGN_VC VDIChunk { } ALIGN_GCC VDIChunk; #define VD_MESSAGE_HEADER_SIZE (sizeof(VDIChunk) + sizeof(VDAgentMessage)) - -enum { - VD_EVENT_CONTROL = 0, - VD_STATIC_EVENTS_COUNT // Must be last -}; +#define VD_READ_BUF_SIZE (sizeof(VDIChunk) + VD_AGENT_MAX_DATA_SIZE) class VDAgent { public: @@ -90,8 +84,6 @@ private: bool handle_clipboard_request(VDAgentClipboardRequest* clipboard_request); void handle_clipboard_release(); bool handle_display_config(VDAgentDisplayConfig* display_config, uint32_t port); - void handle_port_in(); - void handle_port_out(); void handle_chunk(VDIChunk* chunk); void on_clipboard_grab(); void on_clipboard_request(UINT format); @@ -101,7 +93,9 @@ private: static HGLOBAL utf8_alloc(LPCSTR data, int size); static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); static DWORD WINAPI event_thread_proc(LPVOID param); - static void dispatch_message(VDAgentMessage* msg, uint32_t port); + static VOID CALLBACK read_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlapped); + static VOID CALLBACK write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlapped); + void dispatch_message(VDAgentMessage* msg, uint32_t port); uint32_t get_clipboard_format(uint32_t type); uint32_t get_clipboard_type(uint32_t format); DWORD get_cximage_format(uint32_t type); @@ -114,7 +108,7 @@ private: void enqueue_chunk(VDIChunk* msg); bool write_message(uint32_t type, uint32_t size, void* data); bool write_clipboard(VDAgentMessage* msg, uint32_t size); - bool init_vdi_port(); + bool init_vio_serial(); bool send_input(); void set_display_depth(uint32_t depth); void load_display_setting(); @@ -134,16 +128,19 @@ private: INPUT _input; DWORD _input_time; HANDLE _control_event; - HANDLE* _events; VDAgentMessage* _in_msg; uint32_t _in_msg_pos; - uint32_t _events_count; bool _pending_input; bool _running; bool _desktop_switch; DesktopLayout* _desktop_layout; DisplaySetting _display_setting; - VDIPort* _vdi_port; + HANDLE _vio_serial; + OVERLAPPED _read_overlapped; + OVERLAPPED _write_overlapped; + CHAR _read_buf[VD_READ_BUF_SIZE]; + DWORD _read_pos; + DWORD _write_pos; mutex_t _control_mutex; mutex_t _message_mutex; std::queue<int> _control_queue; @@ -163,6 +160,8 @@ private: VDAgent* VDAgent::_singleton = NULL; +#define VIOSERIAL_PORT_PATH L"\\\\.\\Global\\com.redhat.spice.0" + VDAgent* VDAgent::get() { if (!_singleton) { @@ -181,16 +180,16 @@ VDAgent::VDAgent() , _mouse_y (0) , _input_time (0) , _control_event (NULL) - , _events (NULL) , _in_msg (NULL) , _in_msg_pos (0) - , _events_count (0) , _pending_input (false) , _running (false) , _desktop_switch (false) , _desktop_layout (NULL) , _display_setting (VD_AGENT_REGISTRY_KEY) - , _vdi_port (NULL) + , _vio_serial (NULL) + , _read_pos (0) + , _write_pos (0) , _logon_desktop (false) , _display_setting_initialized (false) , _client_caps (NULL) @@ -204,7 +203,10 @@ VDAgent::VDAgent() swprintf_s(log_path, MAX_PATH, VD_AGENT_LOG_PATH, temp_path); _log = VDLog::get(log_path); } - ZeroMemory(&_input, sizeof(INPUT)); + ZeroMemory(&_input, sizeof(_input)); + ZeroMemory(&_read_overlapped, sizeof(_read_overlapped)); + ZeroMemory(&_write_overlapped, sizeof(_write_overlapped)); + ZeroMemory(_read_buf, sizeof(_read_buf)); MUTEX_INIT(_control_mutex); MUTEX_INIT(_message_mutex); @@ -213,7 +215,6 @@ VDAgent::VDAgent() VDAgent::~VDAgent() { - delete _events; delete _log; delete[] _client_caps; } @@ -277,8 +278,13 @@ bool VDAgent::run() if (_desktop_layout->get_display_count() == 0) { vd_printf("No QXL devices!"); } - if (!init_vdi_port()) { - vd_printf("Failed to create VDIPort instance"); + if (!init_vio_serial()) { + cleanup(); + return false; + } + if (!ReadFileEx(_vio_serial, _read_buf, sizeof(VDIChunk), &_read_overlapped, read_completion) && + GetLastError() != ERROR_IO_PENDING) { + vd_printf("vio_serial read error %lu", GetLastError()); cleanup(); return false; } @@ -290,12 +296,6 @@ bool VDAgent::run() return false; } send_announce_capabilities(true); - - _events_count = VD_STATIC_EVENTS_COUNT + _vdi_port->get_num_events(); - _events = new HANDLE[_events_count]; - ZeroMemory(_events, _events_count); - _events[VD_EVENT_CONTROL] = _control_event; - _vdi_port->fill_events(&_events[VD_STATIC_EVENTS_COUNT]); vd_printf("Connected to server"); while (_running) { @@ -313,7 +313,7 @@ bool VDAgent::run() void VDAgent::cleanup() { CloseHandle(_control_event); - delete _vdi_port; + CloseHandle(_vio_serial); delete _desktop_layout; } @@ -431,52 +431,23 @@ void VDAgent::event_dispatcher(DWORD timeout, DWORD wake_mask) DWORD wait_ret; MSG msg; - int cont_read = _vdi_port->read(); - int cont_write = _vdi_port->write(); - bool cont = false; - - if (cont_read >= 0 && cont_write >= 0) { - cont = cont_read || cont_write; - } else if (cont_read == VDI_PORT_ERROR || cont_write == VDI_PORT_ERROR) { - vd_printf("VDI Port error, read %d write %d", cont_read, cont_write); - _running = false; - return; - } else if (cont_read == VDI_PORT_RESET || cont_write == VDI_PORT_RESET) { - vd_printf("VDI Port reset, read %d write %d", cont_read, cont_write); - _running = false; - return; - } - if (cont_read) { - handle_port_in(); - } - if (cont_write) { - handle_port_out(); - } - - wait_ret = MsgWaitForMultipleObjects(_events_count, _events, FALSE, cont ? 0 : timeout, - wake_mask); - - if (wake_mask && wait_ret == WAIT_OBJECT_0 + _events_count) { + wait_ret = MsgWaitForMultipleObjectsEx(1, &_control_event, timeout, wake_mask, MWMO_ALERTABLE); + switch (wait_ret) { + case WAIT_OBJECT_0: + handle_control_event(); + break; + case WAIT_OBJECT_0 + 1: while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } - return; - } - switch (wait_ret) { - case WAIT_OBJECT_0 + VD_EVENT_CONTROL: - handle_control_event(); break; + case WAIT_IO_COMPLETION: case WAIT_TIMEOUT: break; default: - DWORD vdi_event = wait_ret - VD_STATIC_EVENTS_COUNT - WAIT_OBJECT_0; - if (vdi_event >= 0 && vdi_event < _vdi_port->get_num_events()) { - _running = _vdi_port->handle_event(vdi_event); - } else { - vd_printf("MsgWaitForMultipleObjectsEx failed: %lu %lu", wait_ret, GetLastError()); - _running = false; - } + vd_printf("MsgWaitForMultipleObjectsEx failed: %lu %lu", wait_ret, GetLastError()); + _running = false; } } @@ -1145,29 +1116,15 @@ void VDAgent::set_clipboard_owner(int new_owner) _clipboard_owner = new_owner; } -VDIPort *create_virtio_vdi_port() -{ - return new VirtioVDIPort(); -} - -VDIPort *create_pci_vdi_port() +bool VDAgent::init_vio_serial() { - return new PCIVDIPort(); -} - -bool VDAgent::init_vdi_port() -{ - VDIPort* (*creators[])(void) = { create_virtio_vdi_port, create_pci_vdi_port }; - - for (unsigned int i = 0 ; i < sizeof(creators)/sizeof(creators[0]); ++i) { - _vdi_port = creators[i](); - if (_vdi_port->init()) { - return true; - } - delete _vdi_port; + _vio_serial = CreateFile(VIOSERIAL_PORT_PATH, GENERIC_READ | GENERIC_WRITE , 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (_vio_serial == INVALID_HANDLE_VALUE) { + vd_printf("Failed opening %ls, error %u", VIOSERIAL_PORT_PATH, GetLastError()); + return false; } - _vdi_port = NULL; - return false; + return true; } void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port) @@ -1213,37 +1170,42 @@ void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port) } } -void VDAgent::handle_port_in() +VOID VDAgent::read_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlapped) { - static char buf[sizeof(VDIChunk) + VD_AGENT_MAX_DATA_SIZE] = {0, }; - VDIChunk* chunk = (VDIChunk*)buf; - uint32_t chunk_size; - - while (_running) { - if (!chunk->hdr.size && _vdi_port->read_ring_size() >= sizeof(VDIChunk)) { - if (_vdi_port->ring_read(chunk, sizeof(VDIChunk)) != sizeof(VDIChunk)) { - vd_printf("ring_read of chunk header failed"); - _running = false; - break; - } - if (sizeof(VDIChunk) + chunk->hdr.size > sizeof(buf)) { - vd_printf("chunk is too large, size %u port %u", chunk->hdr.size, chunk->hdr.port); - _running = false; - break; - } - } - chunk_size = chunk->hdr.size; - if (!chunk_size || _vdi_port->read_ring_size() < chunk_size) { - break; - } - if (_vdi_port->ring_read(chunk->data, chunk_size) != chunk_size) { - vd_printf("ring_read of chunk data failed"); - _running = false; - break; + VDAgent* a = _singleton; + VDIChunk* chunk = (VDIChunk*)a->_read_buf; + DWORD count; + + if (err != 0 && err != ERROR_OPERATION_ABORTED && err != ERROR_NO_SYSTEM_RESOURCES) { + vd_printf("vio_serial read completion error %lu", err); + a->_running = false; + return; + } + + a->_read_pos += bytes; + if (a->_read_pos < sizeof(VDIChunk)) { + count = sizeof(VDIChunk) - a->_read_pos; + } else if (a->_read_pos == sizeof(VDIChunk)) { + count = chunk->hdr.size; + if (a->_read_pos + count > sizeof(a->_read_buf)) { + vd_printf("chunk is too large, size %u port %u", chunk->hdr.size, chunk->hdr.port); + a->_running = false; + return; } - handle_chunk(chunk); - chunk->hdr.size = 0; - } + } else if (a->_read_pos == sizeof(VDIChunk) + chunk->hdr.size){ + a->handle_chunk(chunk); + count = sizeof(VDIChunk); + a->_read_pos = 0; + } else { + ASSERT(a->_read_pos < sizeof(VDIChunk) + chunk->hdr.size); + count = sizeof(VDIChunk) + chunk->hdr.size - a->_read_pos; + } + + if (!ReadFileEx(a->_vio_serial, a->_read_buf + a->_read_pos, count, overlapped, + read_completion) && GetLastError() != ERROR_IO_PENDING) { + vd_printf("vio_serial read error %lu", GetLastError()); + a->_running = false; + } } void VDAgent::handle_chunk(VDIChunk* chunk) @@ -1289,24 +1251,39 @@ void VDAgent::cleanup_in_msg() _in_msg = NULL; } -void VDAgent::handle_port_out() +void VDAgent::write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlapped) { - MUTEX_LOCK(_message_mutex); - while (_running && !_message_queue.empty()) { - VDIChunk* chunk = _message_queue.front(); - DWORD size = sizeof(VDIChunk) + chunk->hdr.size; + VDAgent* a = _singleton; + VDIChunk* chunk; + DWORD count; - if (size > _vdi_port->write_ring_free_space()) { - break; + ASSERT(!a->_message_queue.empty()); + if (err != 0) { + vd_printf("vio_serial write completion error %lu", err); + a->_running = false; + return; + } + MUTEX_LOCK(a->_message_mutex); + a->_write_pos += bytes; + chunk = a->_message_queue.front(); + count = sizeof(VDIChunk) + chunk->hdr.size - a->_write_pos; + if (count == 0) { + a->_message_queue.pop(); + a->_write_pos = 0; + delete chunk; + if (!a->_message_queue.empty()) { + chunk = a->_message_queue.front(); + count = sizeof(VDIChunk) + chunk->hdr.size; } - _message_queue.pop(); - if (_vdi_port->ring_write(chunk, size) != size) { - vd_printf("ring_write failed"); - _running = false; + } + if (count) { + if (!WriteFileEx(a->_vio_serial, (char*)chunk + a->_write_pos, count, overlapped, + write_completion) && GetLastError() != ERROR_IO_PENDING) { + vd_printf("vio_serial write error %lu", GetLastError()); + a->_running = false; } - delete chunk; } - MUTEX_UNLOCK(_message_mutex); + MUTEX_UNLOCK(a->_message_mutex); } VDIChunk* VDAgent::new_chunk(DWORD bytes) @@ -1318,8 +1295,10 @@ void VDAgent::enqueue_chunk(VDIChunk* chunk) { MUTEX_LOCK(_message_mutex); _message_queue.push(chunk); + if (_message_queue.size() == 1) { + write_completion(0, 0, &_write_overlapped); + } MUTEX_UNLOCK(_message_mutex); - handle_port_out(); } LRESULT CALLBACK VDAgent::wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) |