diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c index e5e56f323..ddf11f3c6 100644 --- a/client/X11/xf_event.c +++ b/client/X11/xf_event.c @@ -461,28 +461,9 @@ boolean xf_event_ConfigureNotify(xfInfo* xfi, XEvent* event, boolean app) xfw->right = xfw->left + xfw->width - 1; xfw->bottom = xfw->top + xfw->height - 1; - if (app) - { - // If current window position disagrees with RDP window position, send - // update to RDP server - if ( xfw->left != window->windowOffsetX || - xfw->top != window->windowOffsetY || - xfw->width != window->windowWidth || - xfw->height != window->windowHeight) - { - xf_rail_send_windowmove(xfi, window->windowId, - xfw->left, xfw->top, xfw->right, xfw->bottom); - } - } + if (app) + xf_rail_local_movesize(xfi, xfw); - DEBUG_X11_LMS("ConfigureNotify: send_event=%d eventWindow=0x%X window=0x%X above=0x%X rc={l=%d t=%d r=%d b=%d} " - "w=%d h=%d override_redirect=%d", - event->xconfigure.send_event, - (uint32) event->xconfigure.event, - (uint32) event->xconfigure.window, - (uint32) event->xconfigure.above, - xfw->left, xfw->top, xfw->right, xfw->bottom, xfw->width, xfw->height, - event->xconfigure.override_redirect); } return True; @@ -502,6 +483,27 @@ boolean xf_event_MapNotify(xfInfo* xfi, XEvent* event, boolean app) { /* local restore event */ xf_rail_send_client_system_command(xfi, window->windowId, SC_RESTORE); + xfWindow *xfw = (xfWindow*) window->extra; + xfw->isMapped = true; + } + + return true; +} + +boolean xf_event_UnmapNotify(xfInfo* xfi, XEvent* event, boolean app) +{ + rdpWindow* window; + rdpRail* rail = ((rdpContext*) xfi->context)->rail; + + if (app != true) + return true; + + window = window_list_get_by_extra_id(rail->list, (void*) event->xany.window); + + if (window != NULL) + { + xfWindow *xfw = (xfWindow*) window->extra; + xfw->isMapped = false; } return true; @@ -631,6 +633,10 @@ boolean xf_event_process(freerdp* instance, XEvent* event) status = xf_event_MapNotify(xfi, event, app); break; + case UnmapNotify: + status = xf_event_UnmapNotify(xfi, event, app); + break; + case ReparentNotify: break; diff --git a/client/X11/xf_event.h b/client/X11/xf_event.h index 719e259fa..c6938b382 100644 --- a/client/X11/xf_event.h +++ b/client/X11/xf_event.h @@ -25,5 +25,6 @@ #include "xfreerdp.h" boolean xf_event_process(freerdp* instance, XEvent* event); +void xf_event_SendClientEvent(xfInfo *xfi, xfWindow* window, Atom atom, unsigned int numArgs, ...); #endif /* __XF_EVENT_H */ diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c index 7ce530d85..7820b1044 100644 --- a/client/X11/xf_rail.c +++ b/client/X11/xf_rail.c @@ -108,6 +108,39 @@ void xf_rail_MoveWindow(rdpRail* rail, rdpWindow* window) window->windowWidth, window->windowHeight); } +/** + * The position of the X window can become out of sync with the RDP window + * if the X window is moved locally by the window manager. In this event + * send an update to the RDP server informing it of the new window position + * and size. + */ +void xf_rail_local_movesize(xfInfo* xfi, xfWindow* window) +{ + rdpWindow* wnd = window->window; + + if (window->isMapped) + { + // If current window position disagrees with RDP window position, send + // update to RDP server + if ( window->left != wnd->windowOffsetX || + window->top != wnd->windowOffsetY || + window->width != wnd->windowWidth || + window->height != wnd->windowHeight) + { + xf_rail_send_windowmove(xfi, wnd->windowId, + window->left, window->top, window->right+1, window->bottom+1); + } + + DEBUG_X11_LMS("window=0x%X rc={l=%d t=%d r=%d b=%d} w=%u h=%u" + " RDP=0x%X rc={l=%d t=%d} w=%d h=%d", + (uint32) window->handle, window->left, window->top, + window->right, window->bottom, window->width, window->height, + wnd->windowId, + wnd->windowOffsetX, wnd->windowOffsetY, + wnd->windowWidth, wnd->windowHeight); + } +} + void xf_rail_ShowWindow(rdpRail* rail, rdpWindow* window, uint8 state) { xfInfo* xfi; @@ -364,6 +397,7 @@ void xf_process_rail_server_localmovesize_event(xfInfo* xfi, rdpChannels* channe rdpRail* rail; rdpWindow* rail_window = NULL; RAIL_LOCALMOVESIZE_ORDER* movesize = (RAIL_LOCALMOVESIZE_ORDER*) event->user_data; + int direction = 0; rail = ((rdpContext*) xfi->context)->rail; rail_window = window_list_get_by_id(rail->list, movesize->windowId); @@ -377,12 +411,50 @@ void xf_process_rail_server_localmovesize_event(xfInfo* xfi, rdpChannels* channe movesize->windowId, movesize->isMoveSizeStart, movetype_names[movesize->moveSizeType], (sint16) movesize->posX, (sint16) movesize->posY); - if (movesize->isMoveSizeStart) - xf_StartLocalMoveSize(xfi, window, movesize->moveSizeType, (int) movesize->posX, (int) movesize->posY); - else - xf_StopLocalMoveSize(xfi, window, movesize->moveSizeType, (int) movesize->posX, (int) movesize->posY); - } + switch (movesize->moveSizeType) + { + case RAIL_WMSZ_LEFT: //0x1 + direction = _NET_WM_MOVERESIZE_SIZE_LEFT; + break; + case RAIL_WMSZ_RIGHT: //0x2 + direction = _NET_WM_MOVERESIZE_SIZE_RIGHT; + break; + case RAIL_WMSZ_TOP: //0x3 + direction = _NET_WM_MOVERESIZE_SIZE_TOP; + break; + case RAIL_WMSZ_TOPLEFT: //0x4 + direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; + break; + case RAIL_WMSZ_TOPRIGHT: //0x5 + direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; + break; + case RAIL_WMSZ_BOTTOM: //0x6 + direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM; + break; + case RAIL_WMSZ_BOTTOMLEFT: //0x7 + direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; + break; + case RAIL_WMSZ_BOTTOMRIGHT: //0x8 + direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; + break; + case RAIL_WMSZ_MOVE: //0x9 + direction = _NET_WM_MOVERESIZE_MOVE; + break; + case RAIL_WMSZ_KEYMOVE: //0xA + direction = _NET_WM_MOVERESIZE_MOVE_KEYBOARD; + break; + case RAIL_WMSZ_KEYSIZE: //0xB + direction = _NET_WM_MOVERESIZE_SIZE_KEYBOARD; + break; + } + if (movesize->isMoveSizeStart) + { + xf_StartLocalMoveSize(xfi, window, direction, movesize->posX, movesize->posY); + } else { + xf_EndLocalMoveSize(xfi, window, True); + } + } } void xf_process_rail_appid_resp_event(xfInfo* xfi, rdpChannels* channels, RDP_EVENT* event) diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h index d3da658a6..efae96a04 100644 --- a/client/X11/xf_rail.h +++ b/client/X11/xf_rail.h @@ -28,5 +28,6 @@ void xf_rail_send_client_system_command(xfInfo* xfi, uint32 windowId, uint16 com void xf_rail_send_activate(xfInfo* xfi, Window xwindow, boolean enabled); void xf_rail_send_windowmove(xfInfo* xfi, uint32 windowId, uint32 left, uint32 top, uint32 right, uint32 bottom); void xf_process_rail_event(xfInfo* xfi, rdpChannels* chanman, RDP_EVENT* event); +void xf_rail_local_movesize(xfInfo* xfi, xfWindow* window); #endif /* __XF_RAIL_H */ diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c index 4185b0661..b4be8a3b6 100644 --- a/client/X11/xf_window.c +++ b/client/X11/xf_window.c @@ -17,6 +17,7 @@ * limitations under the License. */ +#include #include #include #include @@ -49,22 +50,35 @@ struct _PropMotifWmHints }; typedef struct _PropMotifWmHints PropMotifWmHints; -void xf_SendClientMessage(xfInfo* xfi, xfWindow* window, Atom atom, long msg, long d1, long d2, long d3) +/** + * Post an event from the client to the X server + */ +void xf_SendClientEvent(xfInfo *xfi, xfWindow* window, Atom atom, unsigned int numArgs, ...) { - XEvent xevent; + XEvent xevent; + unsigned int i; + va_list argp; - xevent.xclient.type = ClientMessage; - xevent.xclient.message_type = atom; - xevent.xclient.window = window->handle; - xevent.xclient.format = 32; - xevent.xclient.data.l[0] = CurrentTime; - xevent.xclient.data.l[1] = msg; - xevent.xclient.data.l[2] = d1; - xevent.xclient.data.l[3] = d2; - xevent.xclient.data.l[4] = d3; + va_start(argp, numArgs); - XSendEvent(xfi->display, window->handle, false, NoEventMask, &xevent); - XSync(xfi->display, false); + xevent.xclient.type = ClientMessage; + xevent.xclient.serial = 0; + xevent.xclient.send_event = True; + xevent.xclient.display = xfi->display; + xevent.xclient.window = window->handle; + xevent.xclient.message_type = atom; + xevent.xclient.format = 32; + + for (i=0; idisplay, window->handle, False, NoEventMask, &xevent); + XSync(xfi->display, False); + + va_end(argp); } void xf_SetWindowFullscreen(xfInfo* xfi, xfWindow* window, boolean fullscreen) @@ -221,6 +235,7 @@ xfWindow* xf_CreateDesktopWindow(xfInfo* xfi, char* name, int width, int height, window->height = height; window->fullscreen = false; window->decorations = decorations; + window->isMapped = false; window->handle = XCreateWindow(xfi->display, RootWindowOfScreen(xfi->screen), xfi->workArea.x, xfi->workArea.y, xfi->width, xfi->height, 0, xfi->depth, InputOutput, xfi->visual, @@ -336,7 +351,8 @@ xfWindow* xf_CreateWindow(xfInfo* xfi, rdpWindow* wnd, int x, int y, int width, window->decorations = false; window->fullscreen = false; window->window = wnd; - window->localMoveSize = false; + window->localMove.inProgress = false; + window->isMapped = false; window->handle = XCreateWindow(xfi->display, RootWindowOfScreen(xfi->screen), x, y, window->width, window->height, 0, xfi->depth, InputOutput, xfi->visual, @@ -403,41 +419,75 @@ void xf_SetWindowMinMaxInfo(xfInfo* xfi, xfWindow* window, } } - -void xf_SendMoveResizeEvent(xfInfo* xfi, xfWindow* window, int direction, int x_root, int y_root) +void xf_StartLocalMoveSize(xfInfo* xfi, xfWindow* window, int direction, int windowRelativeX, int windowRelativeY) { - // TODO: - // - how to receive movesize canceling event? - // - how to produce correct RAIL movesize finish? - // - how to receive move/size window coordinates in process of local move/size? + window->localMove.windowRelativeX = windowRelativeX; + window->localMove.windowRelativeY = windowRelativeY; - XEvent event; + DEBUG_X11_LMS("direction=%d coords=%d,%d window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d", + direction, windowRelativeX, windowRelativeY, + (uint32) window->handle, window->left, window->top, window->right, window->bottom, + window->width, window->height); - event.xclient.type = ClientMessage; - event.xclient.window = window->handle; - event.xclient.message_type = xfi->_NET_WM_MOVERESIZE; - event.xclient.serial = 0; - event.xclient.display = xfi->display; - event.xclient.send_event = true; - event.xclient.format = 32; - event.xclient.data.l[0] = x_root; - event.xclient.data.l[1] = y_root; - event.xclient.data.l[2] = direction; - event.xclient.data.l[3] = 1; /* button 1 */ - event.xclient.data.l[4] = 0; + // FIXME: There does not appear a way to tell when the local window manager completes + // a window move or resize. The client will receive a number of ConfigureNotify events + // but nothing indicates when the user has completed the move gesture (keyboard or mouse). + // + return; + + // X Server _WM_MOVERESIZE coordinates are expressed relative to the root window. + // RDP coordinates are expressed relative to the local window. + // Translate these to root window coordinates. + + window->localMove.inProgress = True; + Window childWindow; + int x,y; + + XTranslateCoordinates(xfi->display, window->handle, DefaultRootWindow(xfi->display), + windowRelativeX, windowRelativeY, &x, &y, &childWindow); XUngrabPointer(xfi->display, CurrentTime); - XSendEvent(xfi->display, RootWindowOfScreen(xfi->screen), false, SubstructureNotifyMask, &event); + xf_SendClientEvent(xfi, window, + xfi->_NET_WM_MOVERESIZE, // Request X window manager to initate a local move + 5, // 5 arguments to follow + x, // x relative to root window + y, // y relative to root window + direction, // extended ICCM direction flag + 1, // simulated mouse button 1 + 1);// 1 == application request per extended ICCM } -void xf_StartLocalMoveSize(xfInfo* xfi, xfWindow* window, uint16 moveSizeType, int posX, int posY) +void xf_EndLocalMoveSize(xfInfo *xfi, xfWindow *window, boolean cancel) { - window->localMoveSize = true; -} + DEBUG_X11_LMS("inProcess=%d cancel=%d window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d", + window->localMove.inProgress, cancel, + (uint32) window->handle, window->left, window->top, window->right, window->bottom, + window->width, window->height); -void xf_StopLocalMoveSize(xfInfo* xfi, xfWindow* window, uint16 moveSizeType, int posX, int posY) -{ - window->localMoveSize = false; + if (!window->localMove.inProgress) + return; + + if (cancel) + { + // Per ICCM, the X client can ask to cancel an active move. Do this if we + // receive a local move stop from RDP while a local move is in progress + Window childWindow; + int x,y; + + XTranslateCoordinates(xfi->display, window->handle, DefaultRootWindow(xfi->display), + window->localMove.windowRelativeX, window->localMove.windowRelativeY, &x, &y, &childWindow); + + xf_SendClientEvent(xfi, window, + xfi->_NET_WM_MOVERESIZE, // Request X window manager to initate a local move + 5, // 5 arguments to follow + x, // x relative to root window + y, // y relative to root window + _NET_WM_MOVERESIZE_CANCEL, // extended ICCM direction flag + 1, // simulated mouse button 1 + 1);// 1 == application request per extended ICCM + } + + window->localMove.inProgress = False; } void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int height) @@ -450,11 +500,6 @@ void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int h if ((window->width != width) || (window->height != height)) resize = true; - if (resize) - XMoveResizeWindow(xfi->display, window->handle, x, y, width, height); - else - XMoveWindow(xfi->display, window->handle, x, y); - window->left = x; window->top = y; window->right = x + width - 1; @@ -462,14 +507,20 @@ void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int h window->width = width; window->height = height; - DEBUG_X11_LMS("xf_MoveWindow: window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d", - (uint32) window->handle, window->left, window->top, window->right, window->bottom, - window->width, window->height); + if (resize) + XMoveResizeWindow(xfi->display, window->handle, x, y, width, height); + else + XMoveWindow(xfi->display, window->handle, x, y); + + DEBUG_X11_LMS("window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d", + (uint32) window->handle, window->left, window->top, window->right, window->bottom, + window->width, window->height); if (resize) { xf_UpdateWindowArea(xfi, window, 0, 0, width, height); } + } void xf_ShowWindow(xfInfo* xfi, xfWindow* window, uint8 state) diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h index 9e8c31711..cc7d3acf1 100644 --- a/client/X11/xf_window.h +++ b/client/X11/xf_window.h @@ -24,10 +24,32 @@ #include #include +typedef struct xf_localmove xfLocalMove; typedef struct xf_window xfWindow; #include "xfreerdp.h" +// Extended ICCM flags http://standards.freedesktop.org/wm-spec/wm-spec-latest.html +#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 +#define _NET_WM_MOVERESIZE_SIZE_TOP 1 +#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 +#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 +#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 +#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */ +#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */ +#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ +#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ + +struct xf_localmove +{ + int windowRelativeX; + int windowRelativeY; + boolean inProgress; +}; + struct xf_window { GC gc; @@ -41,7 +63,8 @@ struct xf_window boolean fullscreen; boolean decorations; rdpWindow* window; - boolean localMoveSize; + boolean isMapped; + xfLocalMove localMove; }; void xf_ewmhints_init(xfInfo* xfi); @@ -71,7 +94,8 @@ void xf_SetWindowMinMaxInfo(xfInfo* xfi, xfWindow* window, int maxWidth, int max int maxPosX, int maxPosY, int minTrackWidth, int minTrackHeight, int maxTrackWidth, int maxTrackHeight); -void xf_StartLocalMoveSize(xfInfo* xfi, xfWindow* window, uint16 moveSizeType, int posX, int posY); -void xf_StopLocalMoveSize(xfInfo* xfi, xfWindow* window, uint16 moveSizeType, int topLeftX, int topLeftY); +void xf_StartLocalMoveSize(xfInfo* xfi, xfWindow* window, int direction, int windowRelativeX, int windowRelativeY); +void xf_EndLocalMoveSize(xfInfo *xfi, xfWindow *window, boolean cancel); +void xf_SendClientEvent(xfInfo *xfi, xfWindow* window, Atom atom, unsigned int numArgs, ...); #endif /* __XF_WINDOW_H */