Hi Jakub, On Fri, 2017-07-07 at 11:57 +0200, Jakub Janků wrote: > --- > Demo: https://youtu.be/IX49z8VbD-c Cool! > > VDAgent: https://github.com/jjanku/win32-vd_agent/tree/seamless-mode > Protocol: https://gitlab.com/xerus/spice-protocol/tree/seamless-mode > Gtk: https://github.com/jjanku/spice-gtk/tree/seamless-mode > > This patch adds very basic implementation of seamless mode for Windows, that > was partialy implemented for linux by Ondrej Holy and Lukas Venhoda earlier. > > It's just a proof of concept as it's very buggy at the moment: > -occasional screen tearing (can be seen in the demo with MineSweeper, Gtk > issue maybe?) > -tested just on Win7 & Win10 > -EnumWindows doesn't work for Win10 ModernUI apps > (possible fix: https://wj32.org/wp/2012/12/12/enumwindows-no-longer-finds- > metromodern-ui-windows-a-workaround-2/) that is a pity > -narrow black bar on top in Win10 > -weird behaviour with multiple screens > -other... > --- > spice-protocol | 2 +- > vdagent/vdagent.cpp | 148 > ++++++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 149 insertions(+), 1 deletion(-) > > diff --git a/spice-protocol b/spice-protocol > index 666b5c5..d52016e 160000 > --- a/spice-protocol > +++ b/spice-protocol > @@ -1 +1 @@ > -Subproject commit 666b5c5780acf3176a9cff61ad549d30bb1b9824 > +Subproject commit d52016e727e48f6eb214becafa9103e6d4fb7f64 > diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp > index cd49755..4d445c0 100644 > --- a/vdagent/vdagent.cpp > +++ b/vdagent/vdagent.cpp > @@ -28,6 +28,8 @@ > #include <queue> > #include <set> > #include <vector> > +#include <dwmapi.h> > +#include <versionhelpers.h> > > #define VD_AGENT_LOG_PATH TEXT("%svdagent.log") > #define VD_AGENT_WINCLASS_NAME TEXT("VDAGENT") > @@ -64,6 +66,7 @@ typedef struct ALIGN_VC VDIChunk { > #define VD_READ_BUF_SIZE (sizeof(VDIChunk) + VD_AGENT_MAX_DATA_SIZE) > > typedef BOOL (WINAPI *PCLIPBOARD_OP)(HWND); > +typedef HRESULT (WINAPI *DWM_GET_PROC)(HWND hwnd, DWORD, PVOID, DWORD); > > class VDAgent { > public: > @@ -92,6 +95,12 @@ private: > DWORD get_buttons_change(DWORD last_buttons_state, DWORD > new_buttons_state, > DWORD mask, DWORD down_flag, DWORD up_flag); > static HGLOBAL utf8_alloc(LPCSTR data, int size); > + void send_seamless_mode_list(); > + void set_seamless_mode(uint8_t enabled); > + static void CALLBACK wnd_event_proc(HWINEVENTHOOK hWinEventHook, DWORD > event,HWND hwnd, > + LONG idObject, LONG idChild, DWORD > dwEventThread, > + DWORD dwmsEventTime); > + static BOOL CALLBACK enum_wnd_proc(HWND hwnd, LPARAM lparam); > static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, > LPARAM lparam); > static DWORD WINAPI event_thread_proc(LPVOID param); > static VOID CALLBACK read_completion(DWORD err, DWORD bytes, LPOVERLAPPED > overlapped); > @@ -168,6 +177,10 @@ private: > > std::set<uint32_t> _grab_types; > > + HWINEVENTHOOK _seamless_window_change_hooks[2]; > + HMODULE _dwm_lib; > + DWM_GET_PROC _dwm_get_wnd_attr; > + > VDLog* _log; > }; > > @@ -339,6 +352,7 @@ bool VDAgent::run() > } > vd_printf("Agent stopped"); > CloseHandle(event_thread); > + set_seamless_mode(FALSE); > cleanup(); > return true; > } > @@ -833,6 +847,7 @@ bool VDAgent::send_announce_capabilities(bool request) > VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG); > VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_GUEST_LINEEND_CRLF); > VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MAX_CLIPBOARD); > + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_SEAMLESS_MODE); > vd_printf("Sending capabilities:"); > for (uint32_t i = 0 ; i < caps_size; ++i) { > vd_printf("%X", caps->caps[i]); > @@ -1292,6 +1307,11 @@ void VDAgent::dispatch_message(VDAgentMessage* msg, > uint32_t port) > case VD_AGENT_MAX_CLIPBOARD: > res = handle_max_clipboard((VDAgentMaxClipboard*)msg->data, msg- > >size); > break; > + case VD_AGENT_SEAMLESS_MODE: { > + VDAgentSeamlessMode *seamless_msg = (VDAgentSeamlessMode*)msg->data; > + set_seamless_mode(seamless_msg->enabled); > + break; > + } > default: > vd_printf("Unsupported message type %u size %u", msg->type, msg- > >size); > } > @@ -1301,6 +1321,134 @@ void VDAgent::dispatch_message(VDAgentMessage* msg, > uint32_t port) > } > } > > +void VDAgent::set_seamless_mode(uint8_t enabled) > +{ > + if (enabled) { > + if (IsWindows8OrGreater()) { > + _dwm_lib = LoadLibrary(L"Dwmapi.dll"); > + if (_dwm_lib) { > + _dwm_get_wnd_attr = (DWM_GET_PROC)GetProcAddress(_dwm_lib, > "DwmGetWindowAttribute"); > + if (!_dwm_get_wnd_attr) { > + vd_printf("GetProcAddress for DwmGetWindowAttribute > failed with error %lu", GetLastError()); > + } > + } else { > + vd_printf("Loading Dwmapi.dll failed with error %lu", > GetLastError()); > + } > + } > + > + //TODO maybe the range of events is too large? > + //TODO check that we don't create new hooks when the old ones haven't > been removed yet > + _seamless_window_change_hooks[0] = > SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, > + EVENT_OBJECT_LOCAT > IONCHANGE, > + NULL, > + wnd_event_proc, > + 0, 0, > + WINEVENT_OUTOFCONT > EXT | WINEVENT_SKIPOWNPROCESS); > + _seamless_window_change_hooks[1] = > SetWinEventHook(EVENT_OBJECT_CREATE, > + EVENT_OBJECT_HIDE, > + NULL, > + wnd_event_proc, > + 0, 0, > + WINEVENT_OUTOFCONT > EXT | WINEVENT_SKIPOWNPROCESS); > + } else { > + UnhookWinEvent(_seamless_window_change_hooks[0]); > + UnhookWinEvent(_seamless_window_change_hooks[1]); > + if (_dwm_lib) { > + FreeLibrary(_dwm_lib); > + _dwm_lib = NULL; > + _dwm_get_wnd_attr = NULL; > + } > + } > +} > + > +void CALLBACK VDAgent::wnd_event_proc(HWINEVENTHOOK hWinEventHook, DWORD > event, HWND hwnd, > + LONG idObject, LONG idChild, > DWORD dwEventThread, > + DWORD dwmsEventTime) indentation > +{ > + LONG_PTR style; > + > + if (idObject != OBJID_WINDOW || hwnd == NULL) > + return; > + > + style = GetWindowLongPtr(hwnd, GWL_STYLE); > + if (style & WS_CHILD) > + return; > + > + switch (event) { > + case EVENT_OBJECT_LOCATIONCHANGE: > + case EVENT_OBJECT_CREATE: > + case EVENT_OBJECT_DESTROY: > + case EVENT_OBJECT_HIDE: > + case EVENT_OBJECT_SHOW: > + _singleton->send_seamless_mode_list(); > + break; > + } > +} > + > +void VDAgent::send_seamless_mode_list() > +{ > + std::queue<HWND> windows; > + RECT rect; > + VDAgentSeamlessModeList *list; > + uint32_t size; > + > + EnumWindows(enum_wnd_proc, reinterpret_cast<LPARAM>(&windows)); > + > + size = sizeof(VDAgentSeamlessModeList) + > + windows.size() * sizeof(VDAgentSeamlessModeWindow); > + list = (VDAgentSeamlessModeList*) malloc(size); > + list->num_of_windows = 0; > + > + while (!windows.empty()) { > + if (_dwm_get_wnd_attr) > + _dwm_get_wnd_attr(windows.front(), DWMWA_EXTENDED_FRAME_BOUNDS, > + &rect, sizeof(RECT)); > + else > + GetWindowRect(windows.front(), &rect); > + > + windows.pop(); > + > + list->windows[list->num_of_windows].w = rect.right - rect.left; > + list->windows[list->num_of_windows].h = rect.bottom - rect.top; > + if (list->windows[list->num_of_windows].w == 0 || > + list->windows[list->num_of_windows].h == 0) > + continue; > + list->windows[list->num_of_windows].x = rect.left; > + list->windows[list->num_of_windows].y = rect.top; > + > + list->num_of_windows++; > + } > + > + write_message(VD_AGENT_SEAMLESS_MODE_LIST, size, list); > + free(list); > +} > + > +BOOL CALLBACK VDAgent::enum_wnd_proc(HWND hwnd, LPARAM lparam) > +{ > + std::queue<HWND>* windows = reinterpret_cast<std::queue<HWND>*>(lparam); > + char window_text[256]; > + char window_class[256]; > + //TITLEBARINFO titlebar; > + > + if (!IsWindowVisible(hwnd)) > + return TRUE; > + > + GetWindowTextA(hwnd, window_text, sizeof(window_text)); > + GetClassNameA(hwnd, window_class, sizeof(window_class)); > + if (!strcmp(window_text, "Program Manager") || !strcmp(window_class, > "ApplicationFrameWindow")) > + return TRUE; > + > + vd_printf("seamless::: text: %s, class: %s", window_text, window_class); > + > + //titlebar.cbSize = sizeof(TITLEBARINFO); > + //GetTitleBarInfo(hwnd, &titlebar); > + //if((titlebar.rgstate[0] & STATE_SYSTEM_INVISIBLE)) > + // return TRUE; > + > + windows->push(hwnd); > + return TRUE; > +} > + > VOID VDAgent::read_completion(DWORD err, DWORD bytes, LPOVERLAPPED > overlapped) > { > VDAgent* a = _singleton; It works nicely. It should disable the seamless mode as soon as the client disconnects (otherwise a new client may recieve the messages even if it does not have the capability). Pavel _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel