FreeRDP/client/X11/xf_window.c
bjcollins d4a9a2d3a8 Remove override redirect flag for fullscreen with keyboard grab enabled. Window manager should always know about the main window.
Small cleanup of passing around decorations flag.
Limit PercentScreen to single monitor vs. entire desktop. IMO - this is better behavior in a multimonitor environment.

Handle fullscreen windows better:
1. Ensure that size hints are set to allow resizing before setting a window to fullscreen as some window managers do not behave properly.
2. Handle fullscreen toggles without destroying and recreating window.
3. Use NET_WM_STATE_FULLSCREEN Extended Window Manager Hint for fullscreen functionality
4. Use the NET_WM_FULLSCREEN_MONITORS Extended Window Manager Hint when appropriate
5. When a single monitor fullscreen is requested - use the current monitor(as determined from mouse location)
6. Handle cases where there is no local monitor at coordinate 0,0. The Windows server expect there to be a monitor at this location, so we maintain offset if necessary between our local primary monitor and the server side primary monitor located at 0,0.
2015-02-23 11:22:28 -06:00

1037 lines
28 KiB
C

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Windows
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2012 HP Development Company, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <winpr/thread.h>
#include <winpr/crt.h>
#include <freerdp/rail.h>
#include <freerdp/log.h>
#ifdef WITH_XEXT
#include <X11/extensions/shape.h>
#endif
#ifdef WITH_XI
#include <X11/extensions/XInput2.h>
#include "xf_input.h"
#endif
#include "xf_rail.h"
#include "xf_input.h"
#define TAG CLIENT_TAG("x11")
#ifdef WITH_DEBUG_X11
#define DEBUG_X11(fmt, ...) WLog_DBG(TAG, fmt, ## __VA_ARGS__)
#else
#define DEBUG_X11(fmt, ...) do { } while (0)
#endif
#include "FreeRDP_Icon_256px.h"
#define xf_icon_prop FreeRDP_Icon_256px_prop
#include "xf_window.h"
/* Extended Window Manager Hints: http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html */
/* 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
{
unsigned long flags;
unsigned long functions;
unsigned long decorations;
long inputMode;
unsigned long status;
};
typedef struct _PropMotifWmHints PropMotifWmHints;
/**
* Post an event from the client to the X server
*/
void xf_SendClientEvent(xfContext* xfc, Window window, Atom atom, unsigned int numArgs, ...)
{
XEvent xevent;
unsigned int i;
va_list argp;
va_start(argp, numArgs);
ZeroMemory(&xevent, sizeof(XEvent));
xevent.xclient.type = ClientMessage;
xevent.xclient.serial = 0;
xevent.xclient.send_event = False;
xevent.xclient.display = xfc->display;
xevent.xclient.window = window;
xevent.xclient.message_type = atom;
xevent.xclient.format = 32;
for (i=0; i<numArgs; i++)
{
xevent.xclient.data.l[i] = va_arg(argp, int);
}
DEBUG_X11("Send ClientMessage Event: wnd=0x%04X", (unsigned int) xevent.xclient.window);
XSendEvent(xfc->display, RootWindowOfScreen(xfc->screen), False,
SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
XSync(xfc->display, False);
va_end(argp);
}
void xf_SetWindowFullscreen(xfContext* xfc, xfWindow* window, BOOL fullscreen)
{
int i;
int startX = xfc->instance->settings->DesktopPosX;
int startY = xfc->instance->settings->DesktopPosY;
window->decorations = xfc->decorations;
xf_SetWindowDecorations(xfc, window->handle, window->decorations);
/* Determine the x,y starting location for the fullscreen window */
if (xfc->instance->settings->MonitorCount)
{
/* Initialize startX and startY with reasonable values */
startX = xfc->instance->settings->MonitorDefArray[0].x;
startY = xfc->instance->settings->MonitorDefArray[0].y;
/* Search all monitors to find the lowest startX and startY values */
for (i=0; i < xfc->instance->settings->MonitorCount; i++)
{
startX = MIN(startX, xfc->instance->settings->MonitorDefArray[i].x);
startY = MIN(startY, xfc->instance->settings->MonitorDefArray[i].y);
}
/* Lastly apply any monitor shift(translation from remote to local coordinate system)
* to startX and startY values
*/
startX = startX + xfc->instance->settings->MonitorLocalShiftX;
startY = startY + xfc->instance->settings->MonitorLocalShiftY;
}
XMoveResizeWindow(xfc->display, window->handle, startX, startY, window->width, window->height);
XMapRaised(xfc->display, window->handle);
/* Set the fullscreen state */
xf_SendClientEvent(xfc, window->handle, xfc->_NET_WM_STATE, 4,
fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
xfc->_NET_WM_STATE_FULLSCREEN, 0, 0);
/* Only send monitor bounds if they are valid */
if ((xfc->fullscreenMonitors.top >= 0) &&
(xfc->fullscreenMonitors.bottom >= 0) &&
(xfc->fullscreenMonitors.left >= 0) &&
(xfc->fullscreenMonitors.right >= 0))
{
xf_SendClientEvent(xfc, window->handle, xfc->_NET_WM_FULLSCREEN_MONITORS, 5,
xfc->fullscreenMonitors.top,
xfc->fullscreenMonitors.bottom,
xfc->fullscreenMonitors.left,
xfc->fullscreenMonitors.right,
1);
}
window->fullscreen = TRUE;
}
/* http://tronche.com/gui/x/xlib/window-information/XGetWindowProperty.html */
BOOL xf_GetWindowProperty(xfContext* xfc, Window window, Atom property, int length,
unsigned long* nitems, unsigned long* bytes, BYTE** prop)
{
int status;
Atom actual_type;
int actual_format;
if (property == None)
return FALSE;
status = XGetWindowProperty(xfc->display, window,
property, 0, length, FALSE, AnyPropertyType,
&actual_type, &actual_format, nitems, bytes, prop);
if (status != Success)
return FALSE;
if (actual_type == None)
{
WLog_ERR(TAG, "Property %lu does not exist", property);
return FALSE;
}
return TRUE;
}
BOOL xf_GetCurrentDesktop(xfContext* xfc)
{
BOOL status;
unsigned long nitems;
unsigned long bytes;
unsigned char *prop;
status = xf_GetWindowProperty(xfc, DefaultRootWindow(xfc->display),
xfc->_NET_CURRENT_DESKTOP, 1, &nitems, &bytes, &prop);
if (!status)
return FALSE;
xfc->current_desktop = (int) *prop;
free(prop);
return TRUE;
}
BOOL xf_GetWorkArea(xfContext* xfc)
{
long *plong;
BOOL status;
unsigned long nitems;
unsigned long bytes;
unsigned char *prop;
status = xf_GetCurrentDesktop(xfc);
if (!status)
return FALSE;
status = xf_GetWindowProperty(xfc, DefaultRootWindow(xfc->display),
xfc->_NET_WORKAREA, 32 * 4, &nitems, &bytes, &prop);
if (!status)
return FALSE;
if ((xfc->current_desktop * 4 + 3) >= nitems)
{
free(prop);
return FALSE;
}
plong = (long*) prop;
xfc->workArea.x = plong[xfc->current_desktop * 4 + 0];
xfc->workArea.y = plong[xfc->current_desktop * 4 + 1];
xfc->workArea.width = plong[xfc->current_desktop * 4 + 2];
xfc->workArea.height = plong[xfc->current_desktop * 4 + 3];
free(prop);
return TRUE;
}
void xf_SetWindowDecorations(xfContext* xfc, Window window, BOOL show)
{
PropMotifWmHints hints;
hints.decorations = (show) ? MWM_DECOR_ALL : 0;
hints.functions = MWM_FUNC_ALL ;
hints.flags = MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS;
hints.inputMode = 0;
hints.status = 0;
XChangeProperty(xfc->display, window, xfc->_MOTIF_WM_HINTS, xfc->_MOTIF_WM_HINTS, 32,
PropModeReplace, (BYTE*) &hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
}
void xf_SetWindowUnlisted(xfContext* xfc, Window window)
{
Atom window_state[2];
window_state[0] = xfc->_NET_WM_STATE_SKIP_PAGER;
window_state[1] = xfc->_NET_WM_STATE_SKIP_TASKBAR;
XChangeProperty(xfc->display, window, xfc->_NET_WM_STATE,
XA_ATOM, 32, PropModeReplace, (BYTE*) &window_state, 2);
}
static void xf_SetWindowPID(xfContext* xfc, Window window, pid_t pid)
{
Atom am_wm_pid;
if (!pid)
pid = getpid();
am_wm_pid = XInternAtom(xfc->display, "_NET_WM_PID", False);
XChangeProperty(xfc->display, window, am_wm_pid, XA_CARDINAL,
32, PropModeReplace, (BYTE*) &pid, 1);
}
static const char* get_shm_id()
{
static char shm_id[64];
snprintf(shm_id, sizeof(shm_id), "com.freerdp.xfreerdp.tsmf_%016X", GetCurrentProcessId());
return shm_id;
}
xfWindow* xf_CreateDesktopWindow(xfContext* xfc, char* name, int width, int height)
{
XEvent xevent;
int input_mask;
xfWindow* window;
Window parentWindow;
XClassHint* classHints;
rdpSettings* settings;
window = (xfWindow*) calloc(1, sizeof(xfWindow));
if (!window)
return NULL;
settings = xfc->instance->settings;
parentWindow = (Window) xfc->settings->ParentWindowId;
window->width = width;
window->height = height;
window->fullscreen = FALSE;
window->decorations = xfc->decorations;
window->is_mapped = FALSE;
window->is_transient = FALSE;
window->handle = XCreateWindow(xfc->display, RootWindowOfScreen(xfc->screen),
xfc->workArea.x, xfc->workArea.y, xfc->workArea.width, xfc->workArea.height,
0, xfc->depth, InputOutput, xfc->visual,
CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
CWBorderPixel | CWWinGravity | CWBitGravity, &xfc->attribs);
window->shmid = shm_open(get_shm_id(), O_CREAT | O_EXCL | O_RDWR, S_IREAD | S_IWRITE);
if (window->shmid < 0)
{
DEBUG_X11("xf_CreateDesktopWindow: failed to get access to shared memory - shmget()\n");
}
else
{
void* mem;
ftruncate(window->shmid, sizeof(window->handle));
mem = mmap(0, sizeof(window->handle), PROT_READ | PROT_WRITE, MAP_SHARED, window->shmid, 0);
if (mem == ((int*) -1))
{
DEBUG_X11("xf_CreateDesktopWindow: failed to assign pointer to the memory address - shmat()\n");
}
else
{
window->xfwin = mem;
*window->xfwin = window->handle;
}
}
classHints = XAllocClassHint();
if (classHints)
{
classHints->res_name = "xfreerdp";
if (xfc->settings->WmClass)
classHints->res_class = xfc->settings->WmClass;
else
classHints->res_class = "xfreerdp";
XSetClassHint(xfc->display, window->handle, classHints);
XFree(classHints);
}
xf_ResizeDesktopWindow(xfc, window, width, height);
xf_SetWindowDecorations(xfc, window->handle, window->decorations);
xf_SetWindowPID(xfc, window->handle, 0);
/* Set the window hints to allow minimal resize, so fullscreen
* changes can work in window managers that might disallow otherwise.
* We will set back afterwards.
*/
xf_SetWindowSizeHints(xfc, window, TRUE, xfc->width, xfc->height);
input_mask =
KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
VisibilityChangeMask | FocusChangeMask | StructureNotifyMask |
PointerMotionMask | ExposureMask | PropertyChangeMask;
if (xfc->grab_keyboard)
input_mask |= EnterWindowMask | LeaveWindowMask;
XChangeProperty(xfc->display, window->handle, xfc->_NET_WM_ICON, XA_CARDINAL, 32,
PropModeReplace, (BYTE*) xf_icon_prop, ARRAYSIZE(xf_icon_prop));
if (parentWindow)
XReparentWindow(xfc->display, window->handle, parentWindow, 0, 0);
XSelectInput(xfc->display, window->handle, input_mask);
XClearWindow(xfc->display, window->handle);
XMapWindow(xfc->display, window->handle);
xf_input_init(xfc, window->handle);
/*
* NOTE: This must be done here to handle reparenting the window,
* so that we don't miss the event and hang waiting for the next one
*/
do
{
XMaskEvent(xfc->display, VisibilityChangeMask, &xevent);
}
while (xevent.type != VisibilityNotify);
/*
* The XCreateWindow call will start the window in the upper-left corner of our current
* monitor instead of the upper-left monitor for remote app mode (which uses all monitors).
* This extra call after the window is mapped will position the login window correctly
*/
if (xfc->settings->RemoteApplicationMode)
{
XMoveWindow(xfc->display, window->handle, 0, 0);
}
else if (settings->DesktopPosX || settings->DesktopPosY)
{
XMoveWindow(xfc->display, window->handle, settings->DesktopPosX, settings->DesktopPosY);
}
XStoreName(xfc->display, window->handle, name);
return window;
}
void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width, int height)
{
xf_SetWindowSizeHints(xfc, window, FALSE, width, height);
}
void xf_SetWindowSizeHints(xfContext* xfc, xfWindow *window, BOOL can_resize, int width, int height)
{
XSizeHints* size_hints;
size_hints = XAllocSizeHints();
if (size_hints)
{
size_hints->flags = PMinSize | PMaxSize | PWinGravity;
size_hints->win_gravity = NorthWestGravity;
size_hints->min_width = size_hints->max_width = width;
size_hints->min_height = size_hints->max_height = height;
#ifdef WITH_XRENDER
if (xfc->settings->SmartSizing)
{
size_hints->min_width = size_hints->min_height = 1;
size_hints->max_width = size_hints->max_height = 16384;
}
#endif
/* Allows the window to resize larger by 1 pixel - so we can
* fullscreen the window with no worries about window manager disallowing based
* on size parameters
*/
if (can_resize)
{
size_hints->width_inc = size_hints->height_inc = 1;
size_hints->max_width = xfc->width + 1;
size_hints->max_height = xfc->height + 1;
}
XSetWMNormalHints(xfc->display, window->handle, size_hints);
XResizeWindow(xfc->display, window->handle, width, height);
XFree(size_hints);
}
}
void xf_DestroyDesktopWindow(xfContext* xfc, xfWindow* window)
{
if (!window)
return;
if (xfc->window == window)
xfc->window = NULL;
if (window->gc)
XFreeGC(xfc->display, window->gc);
if (window->handle)
{
XUnmapWindow(xfc->display, window->handle);
XDestroyWindow(xfc->display, window->handle);
}
if (window->xfwin)
munmap(0, sizeof(*window->xfwin));
if (window->shmid >= 0)
close(window->shmid);
shm_unlink(get_shm_id());
window->xfwin = (Window*) -1;
window->shmid = -1;
free(window);
}
void xf_SetWindowStyle(xfContext* xfc, xfAppWindow* appWindow, UINT32 style, UINT32 ex_style)
{
Atom window_type;
if (/*(ex_style & WS_EX_TOPMOST) ||*/ (ex_style & WS_EX_TOOLWINDOW))
{
/*
* Tooltips and menu items should be unmanaged windows
* (called "override redirect" in X windows parlance)
* If they are managed, there are issues with window focus that
* cause the windows to behave improperly. For example, a mouse
* press will dismiss a drop-down menu because the RDP server
* sees that as a focus out event from the window owning the
* dropdown.
*/
XSetWindowAttributes attrs;
attrs.override_redirect = True;
XChangeWindowAttributes(xfc->display, appWindow->handle, CWOverrideRedirect, &attrs);
appWindow->is_transient = TRUE;
xf_SetWindowUnlisted(xfc, appWindow->handle);
window_type = xfc->_NET_WM_WINDOW_TYPE_POPUP;
}
/*
* TOPMOST window that is not a tool window is treated like a regular window (i.e. task manager).
* Want to do this here, since the window may have type WS_POPUP
*/
else if (ex_style & WS_EX_TOPMOST)
{
window_type = xfc->_NET_WM_WINDOW_TYPE_NORMAL;
}
else if (style & WS_POPUP)
{
/* this includes dialogs, popups, etc, that need to be full-fledged windows */
appWindow->is_transient = TRUE;
window_type = xfc->_NET_WM_WINDOW_TYPE_DIALOG;
xf_SetWindowUnlisted(xfc, appWindow->handle);
}
else
{
XChangeProperty(xfc->display, appWindow->handle, xfc->_NET_WM_WINDOW_TYPE,
XA_ATOM, 32, PropModeReplace, (BYTE*) &window_type, 1);
}
}
void xf_SetWindowText(xfContext* xfc, xfAppWindow* appWindow, char* name)
{
XStoreName(xfc->display, appWindow->handle, name);
const size_t i = strlen(name);
XStoreName(xfc->display, appWindow->handle, name);
Atom wm_Name = XInternAtom(xfc->display, "_NET_WM_NAME", FALSE);
Atom utf8Str = XInternAtom(xfc->display, "UTF8_STRING", FALSE);
XChangeProperty(xfc->display, appWindow->handle, wm_Name, utf8Str, 8,
PropModeReplace, (unsigned char *)name, i);
}
void xf_FixWindowCoordinates(xfContext* xfc, int* x, int* y, int* width, int* height)
{
int vscreen_width;
int vscreen_height;
vscreen_width = xfc->vscreen.area.right - xfc->vscreen.area.left + 1;
vscreen_height = xfc->vscreen.area.bottom - xfc->vscreen.area.top + 1;
if (*width < 1)
{
*width = 1;
}
if (*height < 1)
{
*height = 1;
}
if (*x < xfc->vscreen.area.left)
{
*width += *x;
*x = xfc->vscreen.area.left;
}
if (*y < xfc->vscreen.area.top)
{
*height += *y;
*y = xfc->vscreen.area.top;
}
if (*width > vscreen_width)
{
*width = vscreen_width;
}
if (*height > vscreen_height)
{
*height = vscreen_height;
}
}
int xf_AppWindowInit(xfContext* xfc, xfAppWindow* appWindow)
{
XGCValues gcv;
int input_mask;
XWMHints* InputModeHint;
XClassHint* class_hints;
xf_FixWindowCoordinates(xfc, &appWindow->x, &appWindow->y, &appWindow->width, &appWindow->height);
appWindow->decorations = FALSE;
appWindow->fullscreen = FALSE;
appWindow->local_move.state = LMS_NOT_ACTIVE;
appWindow->is_mapped = FALSE;
appWindow->is_transient = FALSE;
appWindow->rail_state = 0;
appWindow->rail_ignore_configure = FALSE;
appWindow->handle = XCreateWindow(xfc->display, RootWindowOfScreen(xfc->screen),
appWindow->x, appWindow->y, appWindow->width, appWindow->height,
0, xfc->depth, InputOutput, xfc->visual, 0, &xfc->attribs);
if (!appWindow->handle)
return -1;
ZeroMemory(&gcv, sizeof(gcv));
appWindow->gc = XCreateGC(xfc->display, appWindow->handle, GCGraphicsExposures, &gcv);
class_hints = XAllocClassHint();
if (class_hints)
{
char* class = NULL;
if (xfc->settings->WmClass)
{
class_hints->res_class = xfc->settings->WmClass;
}
else
{
class = malloc(sizeof("RAIL:00000000"));
snprintf(class, sizeof("RAIL:00000000"), "RAIL:%08X", appWindow->windowId);
class_hints->res_class = class;
}
class_hints->res_name = "RAIL";
XSetClassHint(xfc->display, appWindow->handle, class_hints);
XFree(class_hints);
if (class)
free(class);
}
/* Set the input mode hint for the WM */
InputModeHint = XAllocWMHints();
InputModeHint->flags = (1L << 0);
InputModeHint->input = True;
XSetWMHints(xfc->display, appWindow->handle, InputModeHint);
XFree(InputModeHint);
XSetWMProtocols(xfc->display, appWindow->handle, &(xfc->WM_DELETE_WINDOW), 1);
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(xfc->display, appWindow->handle, input_mask);
xf_SetWindowDecorations(xfc, appWindow->handle, appWindow->decorations);
xf_SetWindowStyle(xfc, appWindow, appWindow->dwStyle, appWindow->dwExStyle);
xf_SetWindowPID(xfc, appWindow->handle, 0);
xf_ShowWindow(xfc, appWindow, WINDOW_SHOW);
XClearWindow(xfc->display, appWindow->handle);
XMapWindow(xfc->display, appWindow->handle);
/* Move doesn't seem to work until window is mapped. */
xf_MoveWindow(xfc, appWindow, appWindow->x, appWindow->y, appWindow->width, appWindow->height);
return 1;
}
void xf_SetWindowMinMaxInfo(xfContext* xfc, xfAppWindow* appWindow,
int maxWidth, int maxHeight, int maxPosX, int maxPosY,
int minTrackWidth, int minTrackHeight, int maxTrackWidth, int maxTrackHeight)
{
XSizeHints* size_hints;
size_hints = XAllocSizeHints();
if (size_hints)
{
size_hints->flags = PMinSize | PMaxSize | PResizeInc;
size_hints->min_width = minTrackWidth;
size_hints->min_height = minTrackHeight;
size_hints->max_width = maxTrackWidth;
size_hints->max_height = maxTrackHeight;
/* to speedup window drawing we need to select optimal value for sizing step. */
size_hints->width_inc = size_hints->height_inc = 1;
XSetWMNormalHints(xfc->display, appWindow->handle, size_hints);
XFree(size_hints);
}
}
void xf_StartLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow, int direction, int x, int y)
{
if (appWindow->local_move.state != LMS_NOT_ACTIVE)
return;
/*
* Save original mouse location relative to root. This will be needed
* to end local move to RDP server and/or X server
*/
appWindow->local_move.root_x = x;
appWindow->local_move.root_y = y;
appWindow->local_move.state = LMS_STARTING;
appWindow->local_move.direction = direction;
XUngrabPointer(xfc->display, CurrentTime);
xf_SendClientEvent(xfc, appWindow->handle,
xfc->_NET_WM_MOVERESIZE, /* request X window manager to initiate 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_EndLocalMoveSize(xfContext* xfc, xfAppWindow* appWindow)
{
if (appWindow->local_move.state == LMS_NOT_ACTIVE)
return;
if (appWindow->local_move.state == LMS_STARTING)
{
/*
* The move never was property started. This can happen due to race
* conditions between the mouse button up and the communications to the
* RDP server for local moves. We must cancel the X window manager move.
* Per ICCM, the X client can ask to cancel an active move.
*/
xf_SendClientEvent(xfc, appWindow->handle,
xfc->_NET_WM_MOVERESIZE, /* request X window manager to abort a local move */
5, /* 5 arguments to follow */
appWindow->local_move.root_x, /* x relative to root window */
appWindow->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 */
}
appWindow->local_move.state = LMS_NOT_ACTIVE;
}
void xf_MoveWindow(xfContext* xfc, xfAppWindow* appWindow, int x, int y, int width, int height)
{
BOOL resize = FALSE;
if ((width * height) < 1)
return;
if ((appWindow->width != width) || (appWindow->height != height))
resize = TRUE;
if (appWindow->local_move.state == LMS_STARTING ||
appWindow->local_move.state == LMS_ACTIVE)
return;
appWindow->x = x;
appWindow->y = y;
appWindow->width = width;
appWindow->height = height;
if (resize)
XMoveResizeWindow(xfc->display, appWindow->handle, x, y, width, height);
else
XMoveWindow(xfc->display, appWindow->handle, x, y);
xf_UpdateWindowArea(xfc, appWindow, 0, 0, width, height);
}
void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state)
{
switch (state)
{
case WINDOW_HIDE:
XWithdrawWindow(xfc->display, appWindow->handle, xfc->screen_number);
break;
case WINDOW_SHOW_MINIMIZED:
XIconifyWindow(xfc->display, appWindow->handle, xfc->screen_number);
break;
case WINDOW_SHOW_MAXIMIZED:
/* Set the window as maximized */
xf_SendClientEvent(xfc, appWindow->handle, xfc->_NET_WM_STATE, 4, 1,
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT", False),
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False), 0);
/*
* This is a workaround for the case where the window is maximized locally before the rail server is told to maximize
* the window, this appears to be a race condition where the local window with incomplete data and once the window is
* actually maximized on the server - an update of the new areas may not happen. So, we simply to do a full update of
* the entire window once the rail server notifies us that the window is now maximized.
*/
if (appWindow->rail_state == WINDOW_SHOW_MAXIMIZED)
{
xf_UpdateWindowArea(xfc, appWindow, 0, 0, appWindow->windowWidth, appWindow->windowHeight);
}
break;
case WINDOW_SHOW:
/* Ensure the window is not maximized */
xf_SendClientEvent(xfc, appWindow->handle, xfc->_NET_WM_STATE, 4, 0,
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT", False),
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False), 0);
/*
* Ignore configure requests until both the Maximized properties have been processed
* to prevent condition where WM overrides size of request due to one or both of these properties
* still being set - which causes a position adjustment to be sent back to the server
* thus causing the window to not return to its original size
*/
if (appWindow->rail_state == WINDOW_SHOW_MAXIMIZED)
appWindow->rail_ignore_configure = TRUE;
if (appWindow->is_transient)
xf_SetWindowUnlisted(xfc, appWindow->handle);
break;
}
/* Save the current rail state of this window */
appWindow->rail_state = state;
XFlush(xfc->display);
}
#if 0
void xf_SetWindowIcon(xfContext* xfc, xfAppWindow* appWindow, rdpIcon* icon)
{
int x, y;
int pixels;
int propsize;
long *propdata;
long *dstp;
UINT32 *srcp;
if (!icon->big)
return;
pixels = icon->entry->width * icon->entry->height;
propsize = 2 + pixels;
propdata = malloc(propsize * sizeof(long));
propdata[0] = icon->entry->width;
propdata[1] = icon->entry->height;
dstp = &(propdata[2]);
srcp = (UINT32*) icon->extra;
for (y = 0; y < icon->entry->height; y++)
{
for (x = 0; x < icon->entry->width; x++)
{
*dstp++ = *srcp++;
}
}
XChangeProperty(xfc->display, appWindow->handle, xfc->_NET_WM_ICON, XA_CARDINAL, 32,
PropModeReplace, (BYTE *) propdata, propsize);
XFlush(xfc->display);
free(propdata);
}
#endif
void xf_SetWindowRects(xfContext* xfc, xfAppWindow* appWindow, RECTANGLE_16* rects, int nrects)
{
int i;
XRectangle* xrects;
if (nrects < 1)
return;
xrects = (XRectangle*) calloc(nrects, sizeof(XRectangle));
for (i = 0; i < nrects; i++)
{
xrects[i].x = rects[i].left;
xrects[i].y = rects[i].top;
xrects[i].width = rects[i].right - rects[i].left;
xrects[i].height = rects[i].bottom - rects[i].top;
}
#ifdef WITH_XEXT
XShapeCombineRectangles(xfc->display, appWindow->handle, ShapeBounding, 0, 0, xrects, nrects, ShapeSet, 0);
#endif
free(xrects);
}
void xf_SetWindowVisibilityRects(xfContext* xfc, xfAppWindow* appWindow, RECTANGLE_16* rects, int nrects)
{
int i;
XRectangle* xrects;
if (nrects < 1)
return;
xrects = (XRectangle*) calloc(nrects, sizeof(XRectangle));
for (i = 0; i < nrects; i++)
{
xrects[i].x = rects[i].left;
xrects[i].y = rects[i].top;
xrects[i].width = rects[i].right - rects[i].left;
xrects[i].height = rects[i].bottom - rects[i].top;
}
#ifdef WITH_XEXT
XShapeCombineRectangles(xfc->display, appWindow->handle, ShapeBounding, 0, 0, xrects, nrects, ShapeSet, 0);
#endif
free(xrects);
}
void xf_UpdateWindowArea(xfContext* xfc, xfAppWindow* appWindow, int x, int y, int width, int height)
{
int ax, ay;
UINT32 translatedWindowOffsetX;
UINT32 translatedWindowOffsetY;
/* Translate the server rail window offset to a local offset */
translatedWindowOffsetX = (appWindow->windowOffsetX - appWindow->localWindowOffsetCorrX);
translatedWindowOffsetY = (appWindow->windowOffsetY - appWindow->localWindowOffsetCorrY);
ax = x + translatedWindowOffsetX;
ay = y + translatedWindowOffsetY;
if (ax + width > translatedWindowOffsetX + appWindow->width)
width = (translatedWindowOffsetX + appWindow->width - 1) - ax;
if (ay + height > translatedWindowOffsetY + appWindow->height)
height = (translatedWindowOffsetY + appWindow->height - 1) - ay;
xf_lock_x11(xfc, TRUE);
if (xfc->settings->SoftwareGdi)
{
XPutImage(xfc->display, xfc->primary, appWindow->gc, xfc->image,
ax, ay, ax, ay, width, height);
}
XCopyArea(xfc->display, xfc->primary, appWindow->handle, appWindow->gc,
ax, ay, width, height, x, y);
XFlush(xfc->display);
xf_unlock_x11(xfc, TRUE);
}
void xf_DestroyWindow(xfContext* xfc, xfAppWindow* appWindow)
{
if (!appWindow)
return;
if (appWindow->gc)
XFreeGC(xfc->display, appWindow->gc);
if (appWindow->handle)
{
XUnmapWindow(xfc->display, appWindow->handle);
XDestroyWindow(xfc->display, appWindow->handle);
}
if (appWindow->xfwin)
munmap(0, sizeof(*appWindow->xfwin));
if (appWindow->shmid >= 0)
close(appWindow->shmid);
shm_unlink(get_shm_id());
appWindow->xfwin = (Window*) -1;
appWindow->shmid = -1;
free(appWindow->title);
free(appWindow->windowRects);
free(appWindow->visibilityRects);
free(appWindow);
}
xfAppWindow* xf_AppWindowFromX11Window(xfContext* xfc, Window wnd)
{
int index;
int count;
ULONG_PTR* pKeys = NULL;
xfAppWindow* appWindow;
count = HashTable_GetKeys(xfc->railWindows, &pKeys);
for (index = 0; index < count; index++)
{
appWindow = (xfAppWindow*) HashTable_GetItemValue(xfc->railWindows, (void*) pKeys[index]);
if (appWindow->handle == wnd)
{
free(pKeys);
return appWindow;
}
}
free(pKeys);
return NULL;
}