diff options
author | Arnon Gilboa <agilboa@redhat.com> | 2010-10-01 16:06:10 +0200 |
---|---|---|
committer | Hans de Goede <hdegoede@redhat.com> | 2010-10-01 16:06:10 +0200 |
commit | c909198eca202f1a2424ce739c541a740dd61281 (patch) | |
tree | e7d47f0b137fcb987ca298616bbaf083df6feb97 /client/windows/platform.cpp | |
parent | 6a26992410c6bc8824e047e8a844a90d5fae46c1 (diff) | |
download | spice-c909198eca202f1a2424ce739c541a740dd61281.tar.gz spice-c909198eca202f1a2424ce739c541a740dd61281.tar.xz spice-c909198eca202f1a2424ce739c541a740dd61281.zip |
client: support clipboard/selection-owner model (v2)
-includes most of Hans' review fixes (up to the SelectionRequest comment [4]) & X11 wips sent by Hans (10x!)
-use the VD_AGENT_CLIPBOARD_* types in the platform code
-add ifs for VD_AGENT_CAP_CLIPBOARD_BY_DEMAND in both sides
-support the GRAB/REQUEST/DATA/RELEASE verbs in both ways
-pasting clipboard data is now "only-by-demand" from both sides (client and agent), whose behavior is symmetric
-client and agent don't read or send the contents of the clipboard unnecessarily (e.g. copy, internal paste, repeating paste, focus change)
-set client as clipboard listener instead of application
-add atexit(cleanup) in win platform
linux:
-instead of clipboard atom selection instead of XA_PRIMARY
-enable USE_XRANDR_1_2 and support clipboard in MultyMonScreen
-send utf8 with no null termination, remove ++size
-add xfixes in configure.ac & Makefile.am
windows:
-bonus: support image cut & paste, currently only on windows
not done yet:
-clipboards formats are still uint32_t, not mime types stores as strings
-platform_win is still used, not the root window
-not replaced the ugly windows CF_DIB in agent/winclient
Diffstat (limited to 'client/windows/platform.cpp')
-rw-r--r-- | client/windows/platform.cpp | 264 |
1 files changed, 212 insertions, 52 deletions
diff --git a/client/windows/platform.cpp b/client/windows/platform.cpp index ae49d028..95876dc5 100644 --- a/client/windows/platform.cpp +++ b/client/windows/platform.cpp @@ -32,6 +32,7 @@ #include "playback.h" #include "cursor.h" #include "named_pipe.h" +#include <spice/vd_agent.h> int gdi_handlers = 0; extern HINSTANCE instance; @@ -45,9 +46,40 @@ public: static DefaultEventListener default_event_listener; static Platform::EventListener* event_listener = &default_event_listener; -static HWND paltform_win; +static HWND platform_win = NULL; static ProcessLoop* main_loop = NULL; +class DefaultClipboardListener: public Platform::ClipboardListener { +public: + virtual void on_clipboard_grab(uint32_t type) {} + virtual void on_clipboard_request(uint32_t type) {} + virtual void on_clipboard_notify(uint32_t type, uint8_t* data, int32_t size) {} +}; + +static DefaultClipboardListener default_clipboard_listener; +static Platform::ClipboardListener* clipboard_listener = &default_clipboard_listener; + +// The next window in the clipboard viewer chain, which is refered in all clipboard events. +static HWND next_clipboard_viewer_win = NULL; +static HANDLE clipboard_event = NULL; + +// clipboard_changer says whether the client was the last one to change cliboard, for loop +// prevention. It's initialized to true so we ignore the first clipboard change event which +// happens right when we call SetClipboardViewer(). +static bool clipboard_changer = true; + +static const int CLIPBOARD_TIMEOUT_MS = 10000; + +typedef struct ClipboardFormat { + uint32_t format; + uint32_t type; +} ClipboardFormat; + +static ClipboardFormat clipboard_formats[] = { + {CF_UNICODETEXT, VD_AGENT_CLIPBOARD_UTF8_TEXT}, + {CF_DIB, VD_AGENT_CLIPBOARD_BITMAP}, + {0, 0}}; + static const unsigned long MODAL_LOOP_TIMER_ID = 1; static const int MODAL_LOOP_DEFAULT_TIMEOUT = 100; static bool modal_loop_active = false; @@ -59,6 +91,32 @@ void Platform::send_quit_request() main_loop->quit(0); } +static uint32_t get_clipboard_type(uint32_t format) { + ClipboardFormat* iter; + + for (iter = clipboard_formats; iter->type && iter->format != format; iter++); + return iter->type; +} + +static uint32_t get_clipboard_format(uint32_t type) { + ClipboardFormat* iter; + + for (iter = clipboard_formats; iter->format && iter->type != type; iter++); + return iter->format; +} + +static uint32_t get_available_clipboard_type() +{ + uint32_t type = 0; + + for (ClipboardFormat* iter = clipboard_formats; iter->format && !type; iter++) { + if (IsClipboardFormatAvailable(iter->format)) { + type = iter->type; + } + } + return type; +} + static LRESULT CALLBACK PlatformWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { @@ -82,6 +140,44 @@ static LRESULT CALLBACK PlatformWinProc(HWND hWnd, UINT message, WPARAM wParam, case WM_DISPLAYCHANGE: event_listener->on_monitors_change(); break; + case WM_CHANGECBCHAIN: + if (next_clipboard_viewer_win == (HWND)wParam) { + next_clipboard_viewer_win = (HWND)lParam; + } else if (next_clipboard_viewer_win) { + SendMessage(next_clipboard_viewer_win, message, wParam, lParam); + } + break; + case WM_DRAWCLIPBOARD: + if (!clipboard_changer) { + uint32_t type = get_available_clipboard_type(); + if (type) { + clipboard_listener->on_clipboard_grab(type); + } else { + LOG_INFO("Unsupported clipboard format"); + } + } else { + clipboard_changer = false; + } + if (next_clipboard_viewer_win) { + SendMessage(next_clipboard_viewer_win, message, wParam, lParam); + } + break; + case WM_RENDERFORMAT: { + // In delayed rendering, Windows requires us to SetClipboardData before we return from + // handling WM_RENDERFORMAT. Therefore, we try our best by sending CLIPBOARD_REQUEST to the + // agent, while waiting alertably for a while (hoping for good) for receiving CLIPBOARD data + // or CLIPBOARD_RELEASE from the agent, which both will signal clipboard_event. + uint32_t type = get_clipboard_type(wParam); + if (!type) { + LOG_INFO("Unsupported clipboard format %u", wParam); + break; + } + clipboard_listener->on_clipboard_request(type); + DWORD start_tick = GetTickCount(); + while (WaitForSingleObjectEx(clipboard_event, 1000, TRUE) != WAIT_OBJECT_0 && + GetTickCount() < start_tick + CLIPBOARD_TIMEOUT_MS); + break; + } default: return DefWindowProc(hWnd, message, wParam, lParam); } @@ -92,7 +188,6 @@ static void create_message_wind() { WNDCLASSEX wclass; ATOM class_atom; - HWND window; const LPCWSTR class_name = L"spicec_platform_wclass"; @@ -113,11 +208,16 @@ static void create_message_wind() THROW("register class failed"); } - if (!(window = CreateWindow(class_name, L"", 0, 0, 0, 0, 0, NULL, NULL, instance, NULL))) { + if (!(platform_win = CreateWindow(class_name, L"", 0, 0, 0, 0, 0, NULL, NULL, instance, NULL))) { THROW("create message window failed"); } - paltform_win = window; + if (!(next_clipboard_viewer_win = SetClipboardViewer(platform_win)) && GetLastError()) { + THROW("set clipboard viewer failed"); + } + if (!(clipboard_event = CreateEvent(NULL, FALSE, FALSE, NULL))) { + THROW("create clipboard event failed"); + } } NamedPipe::ListenerRef NamedPipe::create(const char *name, ListenerInterface& listener_interface) @@ -460,9 +560,16 @@ void Platform::path_append(std::string& path, const std::string& partial_path) path += partial_path; } +static void cleanup() +{ + ChangeClipboardChain(platform_win, next_clipboard_viewer_win); + CloseHandle(clipboard_event); +} + void Platform::init() { create_message_wind(); + atexit(cleanup); } void Platform::set_process_loop(ProcessLoop& main_process_loop) @@ -733,7 +840,7 @@ static bool set_modal_loop_timer() the enterance to the loop*/ } - if (!SetTimer(paltform_win, MODAL_LOOP_TIMER_ID, timeout, NULL)) { + if (!SetTimer(platform_win, MODAL_LOOP_TIMER_ID, timeout, NULL)) { return false; } return true; @@ -745,99 +852,152 @@ void WinPlatform::exit_modal_loop() LOG_INFO("not inside the loop"); return; } - KillTimer(paltform_win, MODAL_LOOP_TIMER_ID); + KillTimer(platform_win, MODAL_LOOP_TIMER_ID); modal_loop_active = false; } -void Platform::set_clipboard_listener(ClipboardListener* listener) +bool Platform::set_clipboard_owner(uint32_t type) { - //FIXME: call only on change, use statics - listener->on_clipboard_change(); + uint32_t format = get_clipboard_format(type); + + if (!format) { + LOG_INFO("Unsupported clipboard type %u", type); + return false; + } + if (!OpenClipboard(platform_win)) { + return false; + } + clipboard_changer = true; + EmptyClipboard(); + SetClipboardData(format, NULL); + CloseClipboard(); + return true; } -UINT get_format(uint32_t type) +void Platform::set_clipboard_listener(ClipboardListener* listener) { - switch (type) { - case Platform::CLIPBOARD_UTF8_TEXT: - return CF_UNICODETEXT; - default: - return 0; - } + clipboard_listener = listener ? listener : &default_clipboard_listener; } bool Platform::set_clipboard_data(uint32_t type, const uint8_t* data, int32_t size) { - UINT format = get_format(type); HGLOBAL clip_data; LPVOID clip_buf; int clip_size; - bool ret; + int clip_len; + UINT format; + bool ret = false; - //LOG_INFO("type %u size %d %s", type, size, data); - if (!format || !OpenClipboard(paltform_win)) { + // Get the required clipboard size + switch (type) { + case VD_AGENT_CLIPBOARD_UTF8_TEXT: + // Received utf8 string is not null-terminated + if (!(clip_len = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)data, size, NULL, 0))) { + return false; + } + clip_len++; + clip_size = clip_len * sizeof(WCHAR); + break; + case VD_AGENT_CLIPBOARD_BITMAP: + clip_size = size; + break; + default: + LOG_INFO("Unsupported clipboard type %u", type); return false; } - clip_size = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)data, size, NULL, 0); - if (!clip_size || !(clip_data = GlobalAlloc(GMEM_DDESHARE, clip_size * sizeof(WCHAR)))) { - CloseClipboard(); + + // Allocate and lock clipboard memory + if (!(clip_data = GlobalAlloc(GMEM_DDESHARE, clip_size))) { return false; } if (!(clip_buf = GlobalLock(clip_data))) { GlobalFree(clip_data); - CloseClipboard(); return false; } - ret = !!MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)data, size, (LPWSTR)clip_buf, clip_size); + + // Translate data and set clipboard content + switch (type) { + case VD_AGENT_CLIPBOARD_UTF8_TEXT: + ret = !!MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)data, size, (LPWSTR)clip_buf, clip_len); + ((LPWSTR)clip_buf)[clip_len - 1] = L'\0'; + break; + case VD_AGENT_CLIPBOARD_BITMAP: + memcpy(clip_buf, data, size); + ret = true; + break; + } GlobalUnlock(clip_data); - if (ret) { - EmptyClipboard(); - ret = !!SetClipboardData(format, clip_data); + if (!ret) { + return false; + } + format = get_clipboard_format(type); + if (SetClipboardData(format, clip_data)) { + SetEvent(clipboard_event); + return true; } + // We retry clipboard open-empty-set-close only when there is a timeout in WM_RENDERFORMAT + if (!OpenClipboard(platform_win)) { + return false; + } + EmptyClipboard(); + ret = !!SetClipboardData(format, clip_data); CloseClipboard(); return ret; } -bool Platform::get_clipboard_data(uint32_t type, uint8_t* data, int32_t size) +bool Platform::request_clipboard_notification(uint32_t type) { - UINT format = get_format(type); + UINT format = get_clipboard_format(type); HANDLE clip_data; LPVOID clip_buf; - bool ret; + bool ret = false; - LOG_INFO("type %u size %d", type, size); - if (!format || !IsClipboardFormatAvailable(format) || !OpenClipboard(paltform_win)) { + if (!format || !IsClipboardFormatAvailable(format) || !OpenClipboard(platform_win)) { return false; } if (!(clip_data = GetClipboardData(format)) || !(clip_buf = GlobalLock(clip_data))) { CloseClipboard(); return false; } - ret = !!WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, -1, (LPSTR)data, size, NULL, NULL); - GlobalUnlock(clip_data); - CloseClipboard(); - return ret; -} - -int32_t Platform::get_clipboard_data_size(uint32_t type) -{ - UINT format = get_format(type); - HANDLE clip_data; - LPVOID clip_buf; - int clip_size; - if (!format || !IsClipboardFormatAvailable(format) || !OpenClipboard(paltform_win)) { - return 0; + switch (type) { + case VD_AGENT_CLIPBOARD_UTF8_TEXT: { + size_t len = wcslen((wchar_t*)clip_buf); + int utf8_size = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, len, NULL, 0, NULL, NULL); + if (!utf8_size) { + break; + } + uint8_t* utf8_data = new uint8_t[utf8_size]; + if (WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, len, (LPSTR)utf8_data, utf8_size, + NULL, NULL)) { + clipboard_listener->on_clipboard_notify(type, utf8_data, utf8_size); + ret = true; + } + delete[] (uint8_t *)utf8_data; + break; } - if (!(clip_data = GetClipboardData(format)) || !(clip_buf = GlobalLock(clip_data))) { - CloseClipboard(); - return 0; + case VD_AGENT_CLIPBOARD_BITMAP: { + size_t clip_size = GlobalSize(clip_data); + if (!clip_size) { + break; + } + clipboard_listener->on_clipboard_notify(type, (uint8_t*)clip_buf, clip_size); + ret = true; + break; } - clip_size = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)clip_buf, -1, NULL, 0, NULL, NULL); + default: + LOG_INFO("Unsupported clipboard type %u", type); + } + GlobalUnlock(clip_data); CloseClipboard(); - return clip_size; + return ret; } +void Platform::release_clipboard() +{ + SetEvent(clipboard_event); +} static bool has_console = false; |