From 8f38bdb87a70e9dd61feadc3c50fc767c2b2c36d Mon Sep 17 00:00:00 2001 From: Not Nyguen Doze Date: Fri, 2 Dec 2011 14:51:30 -0800 Subject: [PATCH] Make popups transient, use local window manager for moves. This fixes a number if issues on Ubuntu in particular. --- client/X11/xf_event.c | 126 ++++++++++++++++++++--------- client/X11/xf_rail.c | 161 +++++++++++++++++++++++++------------ client/X11/xf_rail.h | 4 +- client/X11/xf_window.c | 214 +++++++++++++++++++++++++++++++------------------ client/X11/xf_window.h | 22 +++-- client/X11/xfreerdp.c | 40 ++++----- client/X11/xfreerdp.h | 3 + 7 files changed, 378 insertions(+), 192 deletions(-) diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c index ddf11f3..73516d4 100644 --- a/client/X11/xf_event.c +++ b/client/X11/xf_event.c @@ -24,6 +24,7 @@ #include #include "xf_rail.h" +#include "xf_window.h" #include "xf_cliprdr.h" #include "xf_event.h" @@ -340,7 +341,7 @@ boolean xf_event_FocusIn(xfInfo* xfi, XEvent* event, boolean app) xf_rail_send_activate(xfi, event->xany.window, true); xf_kbd_focus_in(xfi); - if (xfi->remote_app != true) + if (app != true) xf_cliprdr_check_owner(xfi); return true; @@ -462,7 +463,7 @@ boolean xf_event_ConfigureNotify(xfInfo* xfi, XEvent* event, boolean app) xfw->bottom = xfw->top + xfw->height - 1; if (app) - xf_rail_local_movesize(xfi, xfw); + xf_rail_adjust_position(xfi, window); } @@ -484,7 +485,7 @@ 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; + xfw->is_mapped = true; } return true; @@ -503,7 +504,7 @@ boolean xf_event_UnmapNotify(xfInfo* xfi, XEvent* event, boolean app) if (window != NULL) { xfWindow *xfw = (xfWindow*) window->extra; - xfw->isMapped = false; + xfw->is_mapped = false; } return true; @@ -511,7 +512,7 @@ boolean xf_event_UnmapNotify(xfInfo* xfi, XEvent* event, boolean app) boolean xf_event_SelectionNotify(xfInfo* xfi, XEvent* event, boolean app) { - if (xfi->remote_app != true) + if (app != true) { if (xf_cliprdr_process_selection_notify(xfi, event)) return true; @@ -522,7 +523,7 @@ boolean xf_event_SelectionNotify(xfInfo* xfi, XEvent* event, boolean app) boolean xf_event_SelectionRequest(xfInfo* xfi, XEvent* event, boolean app) { - if (xfi->remote_app != true) + if (app != true) { if (xf_cliprdr_process_selection_request(xfi, event)) return true; @@ -533,7 +534,7 @@ boolean xf_event_SelectionRequest(xfInfo* xfi, XEvent* event, boolean app) boolean xf_event_SelectionClear(xfInfo* xfi, XEvent* event, boolean app) { - if (xfi->remote_app != true) + if (app != true) { if (xf_cliprdr_process_selection_clear(xfi, event)) return true; @@ -544,7 +545,7 @@ boolean xf_event_SelectionClear(xfInfo* xfi, XEvent* event, boolean app) boolean xf_event_PropertyNotify(xfInfo* xfi, XEvent* event, boolean app) { - if (xfi->remote_app != true) + if (app != true) { if (xf_cliprdr_process_property_notify(xfi, event)) return true; @@ -555,20 +556,71 @@ boolean xf_event_PropertyNotify(xfInfo* xfi, XEvent* event, boolean app) boolean xf_event_process(freerdp* instance, XEvent* event) { - boolean app = false; boolean status = true; xfInfo* xfi = ((xfContext*) instance->context)->xfi; - if (xfi->remote_app == true) - { - app = true; - } - else + if (xfi->window && xfi->window->local_move.state == LMS_ACTIVE) { - if (event->xany.window != xfi->window->handle) - app = true; - } + xfWindow* xfw; + rdpWindow* window; + rdpRail* rail = ((rdpContext*) xfi->context)->rail; + window = window_list_get_by_extra_id(rail->list, (void*) event->xexpose.window); + if (window != NULL) + { + xfw = (xfWindow*) window->extra; + xfi->window = xfw; + switch (event->type) + { + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + case UnmapNotify: + { + // A button release event means the X window server did not grab the + // mouse before the user released it. In this case we must cancel + // the local move. The event will be processed below as normal, below. + xf_EndLocalMoveSize(xfi, xfw, true); + } + break; + + case FocusIn: + case FocusOut: + { + XFocusChangeEvent *focusEvent = (XFocusChangeEvent *)event; + if (focusEvent->mode == NotifyUngrab) + xf_rail_end_local_move(xfi, window); + else + return true; + } + break; + case EnterNotify: + case LeaveNotify: + { + XCrossingEvent *crossingEvent = (XCrossingEvent *)event; + if(crossingEvent->mode == NotifyUngrab) + xf_rail_end_local_move(xfi, window); + else + return true; + } + break; + + case VisibilityNotify: + case ConfigureNotify: + case Expose: + case PropertyNotify: + // Allow these events to be processed during move to keep + // our state up to date. + break; + default: + // Any other event should signify the root no longer + // has the grap, so the move has finished. + xf_rail_end_local_move(xfi, window); + } + + } + } if (event->type != MotionNotify) DEBUG_X11("%s Event: wnd=0x%04X", X11_EVENT_STRINGS[event->type], (uint32) event->xany.window); @@ -576,47 +628,47 @@ boolean xf_event_process(freerdp* instance, XEvent* event) switch (event->type) { case Expose: - status = xf_event_Expose(xfi, event, app); + status = xf_event_Expose(xfi, event, xfi->remote_app); break; case VisibilityNotify: - status = xf_event_VisibilityNotify(xfi, event, app); + status = xf_event_VisibilityNotify(xfi, event, xfi->remote_app); break; case MotionNotify: - status = xf_event_MotionNotify(xfi, event, app); + status = xf_event_MotionNotify(xfi, event, xfi->remote_app); break; case ButtonPress: - status = xf_event_ButtonPress(xfi, event, app); + status = xf_event_ButtonPress(xfi, event, xfi->remote_app); break; case ButtonRelease: - status = xf_event_ButtonRelease(xfi, event, app); + status = xf_event_ButtonRelease(xfi, event, xfi->remote_app); break; case KeyPress: - status = xf_event_KeyPress(xfi, event, app); + status = xf_event_KeyPress(xfi, event, xfi->remote_app); break; case KeyRelease: - status = xf_event_KeyRelease(xfi, event, app); + status = xf_event_KeyRelease(xfi, event, xfi->remote_app); break; case FocusIn: - status = xf_event_FocusIn(xfi, event, app); + status = xf_event_FocusIn(xfi, event, xfi->remote_app); break; case FocusOut: - status = xf_event_FocusOut(xfi, event, app); + status = xf_event_FocusOut(xfi, event, xfi->remote_app); break; case EnterNotify: - status = xf_event_EnterNotify(xfi, event, app); + status = xf_event_EnterNotify(xfi, event, xfi->remote_app); break; case LeaveNotify: - status = xf_event_LeaveNotify(xfi, event, app); + status = xf_event_LeaveNotify(xfi, event, xfi->remote_app); break; case NoExpose: @@ -626,42 +678,42 @@ boolean xf_event_process(freerdp* instance, XEvent* event) break; case ConfigureNotify: - status = xf_event_ConfigureNotify(xfi, event, app); + status = xf_event_ConfigureNotify(xfi, event, xfi->remote_app); break; case MapNotify: - status = xf_event_MapNotify(xfi, event, app); + status = xf_event_MapNotify(xfi, event, xfi->remote_app); break; case UnmapNotify: - status = xf_event_UnmapNotify(xfi, event, app); + status = xf_event_UnmapNotify(xfi, event, xfi->remote_app); break; case ReparentNotify: break; case MappingNotify: - status = xf_event_MappingNotify(xfi, event, app); + status = xf_event_MappingNotify(xfi, event, xfi->remote_app); break; case ClientMessage: - status = xf_event_ClientMessage(xfi, event, app); + status = xf_event_ClientMessage(xfi, event, xfi->remote_app); break; case SelectionNotify: - status = xf_event_SelectionNotify(xfi, event, app); + status = xf_event_SelectionNotify(xfi, event, xfi->remote_app); break; case SelectionRequest: - status = xf_event_SelectionRequest(xfi, event, app); + status = xf_event_SelectionRequest(xfi, event, xfi->remote_app); break; case SelectionClear: - status = xf_event_SelectionClear(xfi, event, app); + status = xf_event_SelectionClear(xfi, event, xfi->remote_app); break; case PropertyNotify: - status = xf_event_PropertyNotify(xfi, event, app); + status = xf_event_PropertyNotify(xfi, event, xfi->remote_app); break; default: diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c index 7820b10..dc43229 100644 --- a/client/X11/xf_rail.c +++ b/client/X11/xf_rail.c @@ -108,39 +108,6 @@ 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; @@ -241,22 +208,6 @@ static void xf_send_rail_client_event(rdpChannels* channels, uint16 event_type, } } -void xf_rail_send_windowmove(xfInfo* xfi, uint32 windowId, uint32 left, uint32 top, uint32 right, uint32 bottom) -{ - rdpChannels* channels; - RAIL_WINDOW_MOVE_ORDER window_move; - - channels = xfi->_context->channels; - - window_move.windowId = windowId; - window_move.left = left; - window_move.top = top; - window_move.right = right; - window_move.bottom = bottom; - - xf_send_rail_client_event(channels, RDP_EVENT_TYPE_RAIL_CLIENT_WINDOW_MOVE, &window_move); -} - void xf_rail_send_activate(xfInfo* xfi, Window xwindow, boolean enabled) { rdpRail* rail; @@ -291,6 +242,84 @@ void xf_rail_send_client_system_command(xfInfo* xfi, uint32 windowId, uint16 com xf_send_rail_client_event(channels, RDP_EVENT_TYPE_RAIL_CLIENT_SYSCOMMAND, &syscommand); } +/** + * 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_adjust_position(xfInfo* xfi, rdpWindow *window) +{ + xfWindow* xfw; + rdpChannels* channels; + RAIL_WINDOW_MOVE_ORDER window_move; + + xfw = (xfWindow*) window->extra; + channels = xfi->_context->channels; + + if (! xfw->is_mapped || xfw->local_move.state != LMS_NOT_ACTIVE) + return; + + 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 lms_state=%d mapped=%d", + (uint32) xfw->handle, xfw->left, xfw->top, + xfw->right, xfw->bottom, xfw->width, xfw->height, + window->windowId, + window->windowOffsetX, window->windowOffsetY, + window->windowWidth, window->windowHeight, + xfw->local_move.state, xfw->is_mapped); + + // 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) + { + window_move.windowId = window->windowId; + window_move.left = xfw->left; + window_move.top = xfw->top; + window_move.right = xfw->right; + window_move.bottom = xfw->bottom; + + xf_send_rail_client_event(channels, RDP_EVENT_TYPE_RAIL_CLIENT_WINDOW_MOVE, &window_move); + } +} + +void xf_rail_end_local_move(xfInfo* xfi, rdpWindow *window) +{ + xfWindow* xfw; + rdpChannels* channels; + RAIL_WINDOW_MOVE_ORDER window_move; + int x,y; + rdpInput* input = xfi->instance->input; + + xfw = (xfWindow*) window->extra; + channels = xfi->_context->channels; + + // Send RDP client event to inform RDP server + + window_move.windowId = window->windowId; + window_move.left = xfw->left; + window_move.top = xfw->top; + window_move.right = xfw->right + 1; // In the update to RDP the position is one past the window + window_move.bottom = xfw->bottom + 1; + + DEBUG_X11_LMS("ClientWindowMove: window=0x%X rc={l=%d t=%d r=%d b=%d}", + (uint32) xfw->handle, xfw->left, xfw->top, xfw->right, xfw->bottom); + + xf_send_rail_client_event(channels, RDP_EVENT_TYPE_RAIL_CLIENT_WINDOW_MOVE, &window_move); + + // Send synthetic button up event to the RDP server. This is per the RDP spec to + // indicate a local move has finished. + + x = xfw->left + xfw->local_move.window_x; + y = xfw->top + xfw->local_move.window_y; + input->MouseEvent(input, PTR_FLAGS_BUTTON1, x, y); + + xfw->local_move.state = LMS_TERMINATING; +} + void xf_process_rail_get_sysparams_event(xfInfo* xfi, rdpChannels* channels, RDP_EVENT* event) { RAIL_SYSPARAM_ORDER* sysparam; @@ -398,14 +427,16 @@ void xf_process_rail_server_localmovesize_event(xfInfo* xfi, rdpChannels* channe rdpWindow* rail_window = NULL; RAIL_LOCALMOVESIZE_ORDER* movesize = (RAIL_LOCALMOVESIZE_ORDER*) event->user_data; int direction = 0; + Window child_window; + int x,y; rail = ((rdpContext*) xfi->context)->rail; rail_window = window_list_get_by_id(rail->list, movesize->windowId); if (rail_window != NULL) { - xfWindow* window = NULL; - window = (xfWindow*) rail_window->extra; + xfWindow* xfw = NULL; + xfw = (xfWindow*) rail_window->extra; DEBUG_X11_LMS("windowId=0x%X isMoveSizeStart=%d moveSizeType=%s PosX=%d PosY=%d", movesize->windowId, movesize->isMoveSizeStart, @@ -415,44 +446,68 @@ void xf_process_rail_server_localmovesize_event(xfInfo* xfi, rdpChannels* channe { case RAIL_WMSZ_LEFT: //0x1 direction = _NET_WM_MOVERESIZE_SIZE_LEFT; + x = movesize->posX; + y = movesize->posY; break; case RAIL_WMSZ_RIGHT: //0x2 direction = _NET_WM_MOVERESIZE_SIZE_RIGHT; + x = movesize->posX; + y = movesize->posY; break; case RAIL_WMSZ_TOP: //0x3 direction = _NET_WM_MOVERESIZE_SIZE_TOP; + x = movesize->posX; + y = movesize->posY; break; case RAIL_WMSZ_TOPLEFT: //0x4 direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; + x = movesize->posX; + y = movesize->posY; break; case RAIL_WMSZ_TOPRIGHT: //0x5 direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; + x = movesize->posX; + y = movesize->posY; break; case RAIL_WMSZ_BOTTOM: //0x6 direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM; + x = movesize->posX; + y = movesize->posY; break; case RAIL_WMSZ_BOTTOMLEFT: //0x7 direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; + x = movesize->posX; + y = movesize->posY; break; case RAIL_WMSZ_BOTTOMRIGHT: //0x8 direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; + x = movesize->posX; + y = movesize->posY; break; case RAIL_WMSZ_MOVE: //0x9 direction = _NET_WM_MOVERESIZE_MOVE; + XTranslateCoordinates(xfi->display, xfw->handle, DefaultRootWindow(xfi->display), + movesize->posX, movesize->posY, &x, &y, &child_window); break; case RAIL_WMSZ_KEYMOVE: //0xA direction = _NET_WM_MOVERESIZE_MOVE_KEYBOARD; + x = movesize->posX; + y = movesize->posY; break; case RAIL_WMSZ_KEYSIZE: //0xB direction = _NET_WM_MOVERESIZE_SIZE_KEYBOARD; + x = movesize->posX; + y = movesize->posY; break; } if (movesize->isMoveSizeStart) { - xf_StartLocalMoveSize(xfi, window, direction, movesize->posX, movesize->posY); + xf_StartLocalMoveSize(xfi, xfw, direction, x, y); } else { - xf_EndLocalMoveSize(xfi, window, True); + xf_MoveWindow(xfi, xfw, movesize->posX, movesize->posY, + xfw->width, xfw->height); + xf_EndLocalMoveSize(xfi, xfw, false); } } } diff --git a/client/X11/xf_rail.h b/client/X11/xf_rail.h index efae96a..3d768bb 100644 --- a/client/X11/xf_rail.h +++ b/client/X11/xf_rail.h @@ -26,8 +26,8 @@ void xf_rail_paint(xfInfo* xfi, rdpRail* rail, uint32 uleft, uint32 utop, uint32 void xf_rail_register_callbacks(xfInfo* xfi, rdpRail* rail); void xf_rail_send_client_system_command(xfInfo* xfi, uint32 windowId, uint16 command); 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); +void xf_rail_adjust_position(xfInfo* xfi, rdpWindow *window); +void xf_rail_end_local_move(xfInfo* xfi, rdpWindow *window); #endif /* __XF_RAIL_H */ diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c index b4be8a3..74e130a 100644 --- a/client/X11/xf_window.c +++ b/client/X11/xf_window.c @@ -37,7 +37,29 @@ /* Extended Window Manager Hints: http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html */ -#define MWM_HINTS_DECORATIONS (1L << 1) +/* bit definitions for MwmHints.flags */ +#define MWM_HINTS_FUNCTIONS (1L << 0) +#define MWM_HINTS_DECORATIONS (1L << 1) +#define MWM_HINTS_INPUT_MODE (1L << 2) +#define MWM_HINTS_STATUS (1L << 3) + +/* bit definitions for MwmHints.functions */ +#define MWM_FUNC_ALL (1L << 0) +#define MWM_FUNC_RESIZE (1L << 1) +#define MWM_FUNC_MOVE (1L << 2) +#define MWM_FUNC_MINIMIZE (1L << 3) +#define MWM_FUNC_MAXIMIZE (1L << 4) +#define MWM_FUNC_CLOSE (1L << 5) + +/* bit definitions for MwmHints.decorations */ +#define MWM_DECOR_ALL (1L << 0) +#define MWM_DECOR_BORDER (1L << 1) +#define MWM_DECOR_RESIZEH (1L << 2) +#define MWM_DECOR_TITLE (1L << 3) +#define MWM_DECOR_MENU (1L << 4) +#define MWM_DECOR_MINIMIZE (1L << 5) +#define MWM_DECOR_MAXIMIZE (1L << 6) + #define PROP_MOTIF_WM_HINTS_ELEMENTS 5 struct _PropMotifWmHints @@ -75,7 +97,8 @@ void xf_SendClientEvent(xfInfo *xfi, xfWindow* window, Atom atom, unsigned int n } DEBUG_X11("Send ClientMessage Event: wnd=0x%04X", (unsigned int) xevent.xclient.window); - XSendEvent(xfi->display, window->handle, False, NoEventMask, &xevent); + XSendEvent(xfi->display, DefaultRootWindow(xfi->display), False, + SubstructureRedirectMask | SubstructureNotifyMask, &xevent); XSync(xfi->display, False); va_end(argp); @@ -181,8 +204,9 @@ void xf_SetWindowDecorations(xfInfo* xfi, xfWindow* window, boolean show) { PropMotifWmHints hints; - hints.decorations = show; - hints.flags = MWM_HINTS_DECORATIONS; + hints.decorations = (show) ? MWM_DECOR_ALL : 0; + hints.functions = MWM_FUNC_ALL ; + hints.flags = MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS; XChangeProperty(xfi->display, window->handle, xfi->_MOTIF_WM_HINTS, xfi->_MOTIF_WM_HINTS, 32, PropModeReplace, (uint8*) &hints, PROP_MOTIF_WM_HINTS_ELEMENTS); @@ -203,21 +227,27 @@ void xf_SetWindowStyle(xfInfo* xfi, xfWindow* window, uint32 style, uint32 ex_st { Atom window_type; - window_type = xfi->_NET_WM_WINDOW_TYPE_NORMAL; - - if ((style & WS_POPUP) || (style & WS_DLGFRAME) || (ex_style & WS_EX_DLGMODALFRAME)) + if (style & WS_POPUP) { - window_type = xfi->_NET_WM_WINDOW_TYPE_DIALOG; + // WS_POPUP includes tool tips, dropdown menus, etc. These won't work + // correctly if the local window manager resizes or moves them. Set + // override redirect to prevent this from occurring. + + XSetWindowAttributes attrs; + attrs.override_redirect = True; + XChangeWindowAttributes(xfi->display, window->handle, CWOverrideRedirect, &attrs); + window->is_transient = true; + + window_type = xfi->_NET_WM_WINDOW_TYPE_POPUP; } - - if (ex_style & WS_EX_TOOLWINDOW) + else { - xf_SetWindowUnlisted(xfi, window); - window_type = xfi->_NET_WM_WINDOW_TYPE_UTILITY; + window_type = xfi->_NET_WM_WINDOW_TYPE_NORMAL; } XChangeProperty(xfi->display, window->handle, xfi->_NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (uint8*) &window_type, 1); + } xfWindow* xf_CreateDesktopWindow(xfInfo* xfi, char* name, int width, int height, boolean decorations) @@ -235,12 +265,14 @@ xfWindow* xf_CreateDesktopWindow(xfInfo* xfi, char* name, int width, int height, window->height = height; window->fullscreen = false; window->decorations = decorations; - window->isMapped = false; + window->local_move.state = LMS_NOT_ACTIVE; + window->is_mapped = false; + window->is_transient = 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, - CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap | - CWBorderPixel, &xfi->attribs); + CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap | + CWBorderPixel | CWWinGravity | CWBitGravity, &xfi->attribs); class_hints = XAllocClassHint(); @@ -345,21 +377,32 @@ xfWindow* xf_CreateWindow(xfInfo* xfi, rdpWindow* wnd, int x, int y, int width, window->height = height; XGCValues gcv; - int input_mask; XClassHint* class_hints; + int input_mask; window->decorations = false; window->fullscreen = false; window->window = wnd; - window->localMove.inProgress = false; - window->isMapped = false; + window->local_move.state = LMS_NOT_ACTIVE; + window->is_mapped = false; + window->is_transient = false; + + // Proper behavior of tooltips, dropdown menus, etc, depend on the local window + // manager not modify them. Set override_redirect on these windows. RDP window + // styles don't map 1 to 1 to X window styles, but the presence of WM_POPUP + // appears to be sufficient for setting override_redirect. window->handle = XCreateWindow(xfi->display, RootWindowOfScreen(xfi->screen), x, y, window->width, window->height, 0, xfi->depth, InputOutput, xfi->visual, - CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap | - CWBorderPixel, &xfi->attribs); + CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap | + CWBorderPixel | CWWinGravity | CWBitGravity, &xfi->attribs); + + DEBUG_X11_LMS("Create window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d rdp=0x%X", + (uint32) window->handle, window->left, window->top, window->right, window->bottom, + window->width, window->height, wnd->windowId); xf_SetWindowDecorations(xfi, window, window->decorations); + xf_SetWindowStyle(xfi, window, wnd->style, wnd->extendedStyle); class_hints = XAllocClassHint(); @@ -377,10 +420,14 @@ xfWindow* xf_CreateWindow(xfInfo* xfi, rdpWindow* wnd, int x, int y, int width, XSetWMProtocols(xfi->display, window->handle, &(xfi->WM_DELETE_WINDOW), 1); - input_mask = - KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | - VisibilityChangeMask | FocusChangeMask | StructureNotifyMask | - PointerMotionMask | ExposureMask | EnterWindowMask; + input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | + ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | + PointerMotionMask | Button1MotionMask | Button2MotionMask | + Button3MotionMask | Button4MotionMask | Button5MotionMask | + ButtonMotionMask | KeymapStateMask | ExposureMask | + VisibilityChangeMask | StructureNotifyMask | SubstructureNotifyMask | + SubstructureRedirectMask | FocusChangeMask | PropertyChangeMask | + ColormapChangeMask | OwnerGrabButtonMask; XSelectInput(xfi->display, window->handle, input_mask); XMapWindow(xfi->display, window->handle); @@ -419,32 +466,31 @@ void xf_SetWindowMinMaxInfo(xfInfo* xfi, xfWindow* window, } } -void xf_StartLocalMoveSize(xfInfo* xfi, xfWindow* window, int direction, int windowRelativeX, int windowRelativeY) +void xf_StartLocalMoveSize(xfInfo* xfi, xfWindow* window, int direction, int x, int y) { - window->localMove.windowRelativeX = windowRelativeX; - window->localMove.windowRelativeY = windowRelativeY; - - 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); - - // 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); + rdpWindow* wnd = window->window; + Window child_window; + + DEBUG_X11_LMS("direction=%d window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d " + "RDP=0x%X rc={l=%d t=%d} w=%d h=%d mouse_x=%d mouse_y=%d", + direction, (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, + x, y); + + window->local_move.root_x = x; + window->local_move.root_y = y; + window->local_move.state = LMS_ACTIVE; + + XTranslateCoordinates(xfi->display, DefaultRootWindow(xfi->display), window->handle, + window->local_move.root_x, + window->local_move.root_y, + &window->local_move.window_x, + &window->local_move.window_y, + &child_window); XUngrabPointer(xfi->display, CurrentTime); xf_SendClientEvent(xfi, window, @@ -459,40 +505,42 @@ void xf_StartLocalMoveSize(xfInfo* xfi, xfWindow* window, int direction, int win void xf_EndLocalMoveSize(xfInfo *xfi, xfWindow *window, boolean cancel) { - 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); + rdpWindow* wnd = window->window; - if (!window->localMove.inProgress) + DEBUG_X11_LMS("inProcess=%d cancel=%d window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d " + "RDP=0x%X rc={l=%d t=%d} w=%d h=%d", + window->local_move.state, cancel, + (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); + + if (window->local_move.state == LMS_NOT_ACTIVE) 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 + xfi->_NET_WM_MOVERESIZE, // Request X window manager to abort a local move 5, // 5 arguments to follow - x, // x relative to root window - y, // y relative to root window + window->local_move.root_x, // x relative to root window + window->local_move.root_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; + window->local_move.state = LMS_NOT_ACTIVE; } void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int height) { boolean resize = false; + rdpWindow* wnd = window->window; if ((width * height) < 1) return; @@ -500,6 +548,16 @@ void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int h if ((window->width != width) || (window->height != height)) resize = true; + DEBUG_X11_LMS("window=0x%X current rc={l=%d t=%d r=%d b=%d} w=%u h=%u " + "new 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, + x, y, x + width -1, y + height -1, width, height, + wnd->windowId, + wnd->windowOffsetX, wnd->windowOffsetY, + wnd->windowWidth, wnd->windowHeight); + window->left = x; window->top = y; window->right = x + width - 1; @@ -507,20 +565,24 @@ void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int h window->width = width; window->height = 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) + if (window->is_transient) { - xf_UpdateWindowArea(xfi, window, 0, 0, width, height); + if (resize) + XMoveResizeWindow(xfi->display, window->handle, x, y, width, height); + else + XMoveWindow(xfi->display, window->handle, x, y); + } else { + // Sending a client event preserves + // window gravity + xf_SendClientEvent(xfi, window, + xfi->_NET_MOVERESIZE_WINDOW, // Request X window manager to move window + 5, // 5 arguments to follow + 0x1F0A, // STATIC gravity + x, // x relative to root window + y, // y relative to root window + width, + height); } - } void xf_ShowWindow(xfInfo* xfi, xfWindow* window, uint8 state) @@ -630,8 +692,8 @@ void xf_UpdateWindowArea(xfInfo* xfi, xfWindow* window, int x, int y, int width, { int ax, ay; rdpWindow* wnd; - wnd = window->window; + ax = x + wnd->windowOffsetX; ay = y + wnd->windowOffsetY; diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h index cc7d3ac..d9667b4 100644 --- a/client/X11/xf_window.h +++ b/client/X11/xf_window.h @@ -43,11 +43,20 @@ typedef struct xf_window xfWindow; #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ #define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ +enum xf_localmove_state +{ + LMS_NOT_ACTIVE, + LMS_ACTIVE, + LMS_TERMINATING +}; + struct xf_localmove { - int windowRelativeX; - int windowRelativeY; - boolean inProgress; + int root_x; // relative to root + int root_y; + int window_x; // relative to window + int window_y; + enum xf_localmove_state state; }; struct xf_window @@ -63,8 +72,9 @@ struct xf_window boolean fullscreen; boolean decorations; rdpWindow* window; - boolean isMapped; - xfLocalMove localMove; + boolean is_mapped; + boolean is_transient; + xfLocalMove local_move; }; void xf_ewmhints_init(xfInfo* xfi); @@ -94,7 +104,7 @@ 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, int direction, int windowRelativeX, int windowRelativeY); +void xf_StartLocalMoveSize(xfInfo* xfi, xfWindow* window, int direction, int x, int y); void xf_EndLocalMoveSize(xfInfo *xfi, xfWindow *window, boolean cancel); void xf_SendClientEvent(xfInfo *xfi, xfWindow* window, Atom atom, unsigned int numArgs, ...); diff --git a/client/X11/xfreerdp.c b/client/X11/xfreerdp.c index 1ef5e22..ee9c8ea 100644 --- a/client/X11/xfreerdp.c +++ b/client/X11/xfreerdp.c @@ -288,6 +288,8 @@ void xf_create_window(xfInfo* xfi) xfi->attribs.backing_store = xfi->primary ? NotUseful : Always; xfi->attribs.override_redirect = xfi->fullscreen; xfi->attribs.colormap = xfi->colormap; + xfi->attribs.bit_gravity = ForgetGravity; + xfi->attribs.win_gravity = StaticGravity; if (xfi->remote_app != true) { @@ -470,24 +472,26 @@ boolean xf_pre_connect(freerdp* instance) return false; } - xfi->_NET_WM_ICON = XInternAtom(xfi->display, "_NET_WM_ICON", false); - xfi->_MOTIF_WM_HINTS = XInternAtom(xfi->display, "_MOTIF_WM_HINTS", false); - xfi->_NET_CURRENT_DESKTOP = XInternAtom(xfi->display, "_NET_CURRENT_DESKTOP", false); - xfi->_NET_WORKAREA = XInternAtom(xfi->display, "_NET_WORKAREA", false); - xfi->_NET_WM_STATE = XInternAtom(xfi->display, "_NET_WM_STATE", false); - xfi->_NET_WM_STATE_FULLSCREEN = XInternAtom(xfi->display, "_NET_WM_STATE_FULLSCREEN", false); - xfi->_NET_WM_WINDOW_TYPE = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE", false); - - xfi->_NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_NORMAL", false); - xfi->_NET_WM_WINDOW_TYPE_DIALOG = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_DIALOG", false); - xfi->_NET_WM_WINDOW_TYPE_UTILITY = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_UTILITY", false); - xfi->_NET_WM_STATE_SKIP_TASKBAR = XInternAtom(xfi->display, "_NET_WM_STATE_SKIP_TASKBAR", false); - xfi->_NET_WM_STATE_SKIP_PAGER = XInternAtom(xfi->display, "_NET_WM_STATE_SKIP_PAGER", false); - - xfi->_NET_WM_MOVERESIZE = XInternAtom(xfi->display, "_NET_WM_MOVERESIZE", false); - - xfi->WM_PROTOCOLS = XInternAtom(xfi->display, "WM_PROTOCOLS", false); - xfi->WM_DELETE_WINDOW = XInternAtom(xfi->display, "WM_DELETE_WINDOW", false); + xfi->_NET_WM_ICON = XInternAtom(xfi->display, "_NET_WM_ICON", True); + xfi->_MOTIF_WM_HINTS = XInternAtom(xfi->display, "_MOTIF_WM_HINTS", True); + xfi->_NET_CURRENT_DESKTOP = XInternAtom(xfi->display, "_NET_CURRENT_DESKTOP", True); + xfi->_NET_WORKAREA = XInternAtom(xfi->display, "_NET_WORKAREA", True); + xfi->_NET_WM_STATE = XInternAtom(xfi->display, "_NET_WM_STATE", True); + xfi->_NET_WM_STATE_FULLSCREEN = XInternAtom(xfi->display, "_NET_WM_STATE_FULLSCREEN", True); + xfi->_NET_WM_WINDOW_TYPE = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE", True); + + xfi->_NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_NORMAL", True); + xfi->_NET_WM_WINDOW_TYPE_DIALOG = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_DIALOG", True); + xfi->_NET_WM_WINDOW_TYPE_POPUP= XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_POPUP", True); + xfi->_NET_WM_WINDOW_TYPE_UTILITY = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_UTILITY", True); + xfi->_NET_WM_WINDOW_TYPE_DROPDOWN_MENU = XInternAtom(xfi->display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", True); + xfi->_NET_WM_STATE_SKIP_TASKBAR = XInternAtom(xfi->display, "_NET_WM_STATE_SKIP_TASKBAR", True); + xfi->_NET_WM_STATE_SKIP_PAGER = XInternAtom(xfi->display, "_NET_WM_STATE_SKIP_PAGER", True); + xfi->_NET_WM_MOVERESIZE = XInternAtom(xfi->display, "_NET_WM_MOVERESIZE", True); + xfi->_NET_MOVERESIZE_WINDOW = XInternAtom(xfi->display, "_NET_MOVERESIZE_WINDOW", True); + + xfi->WM_PROTOCOLS = XInternAtom(xfi->display, "WM_PROTOCOLS", True); + xfi->WM_DELETE_WINDOW = XInternAtom(xfi->display, "WM_DELETE_WINDOW", True); xf_kbd_init(xfi); diff --git a/client/X11/xfreerdp.h b/client/X11/xfreerdp.h index b3c2f04..08e0f91 100644 --- a/client/X11/xfreerdp.h +++ b/client/X11/xfreerdp.h @@ -142,8 +142,11 @@ struct xf_info Atom _NET_WM_WINDOW_TYPE_NORMAL; Atom _NET_WM_WINDOW_TYPE_DIALOG; Atom _NET_WM_WINDOW_TYPE_UTILITY; + Atom _NET_WM_WINDOW_TYPE_POPUP; + Atom _NET_WM_WINDOW_TYPE_DROPDOWN_MENU; Atom _NET_WM_MOVERESIZE; + Atom _NET_MOVERESIZE_WINDOW; Atom WM_PROTOCOLS; Atom WM_DELETE_WINDOW; -- 2.7.4