2 * FreeRDP: A Remote Desktop Protocol Client
5 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
22 #include <X11/Xutil.h>
23 #include <X11/Xatom.h>
25 #include <freerdp/rail.h>
26 #include <freerdp/utils/rail.h>
29 #include <X11/extensions/shape.h>
32 #include "FreeRDP_Icon_256px.h"
33 #define xf_icon_prop FreeRDP_Icon_256px_prop
35 #include "xf_window.h"
37 /* Extended Window Manager Hints: http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html */
39 /* bit definitions for MwmHints.flags */
40 #define MWM_HINTS_FUNCTIONS (1L << 0)
41 #define MWM_HINTS_DECORATIONS (1L << 1)
42 #define MWM_HINTS_INPUT_MODE (1L << 2)
43 #define MWM_HINTS_STATUS (1L << 3)
45 /* bit definitions for MwmHints.functions */
46 #define MWM_FUNC_ALL (1L << 0)
47 #define MWM_FUNC_RESIZE (1L << 1)
48 #define MWM_FUNC_MOVE (1L << 2)
49 #define MWM_FUNC_MINIMIZE (1L << 3)
50 #define MWM_FUNC_MAXIMIZE (1L << 4)
51 #define MWM_FUNC_CLOSE (1L << 5)
53 /* bit definitions for MwmHints.decorations */
54 #define MWM_DECOR_ALL (1L << 0)
55 #define MWM_DECOR_BORDER (1L << 1)
56 #define MWM_DECOR_RESIZEH (1L << 2)
57 #define MWM_DECOR_TITLE (1L << 3)
58 #define MWM_DECOR_MENU (1L << 4)
59 #define MWM_DECOR_MINIMIZE (1L << 5)
60 #define MWM_DECOR_MAXIMIZE (1L << 6)
62 #define PROP_MOTIF_WM_HINTS_ELEMENTS 5
64 struct _PropMotifWmHints
67 unsigned long functions;
68 unsigned long decorations;
72 typedef struct _PropMotifWmHints PropMotifWmHints;
75 * Post an event from the client to the X server
77 void xf_SendClientEvent(xfInfo *xfi, xfWindow* window, Atom atom, unsigned int numArgs, ...)
83 va_start(argp, numArgs);
85 xevent.xclient.type = ClientMessage;
86 xevent.xclient.serial = 0;
87 xevent.xclient.send_event = False;
88 xevent.xclient.display = xfi->display;
89 xevent.xclient.window = window->handle;
90 xevent.xclient.message_type = atom;
91 xevent.xclient.format = 32;
93 for (i=0; i<numArgs; i++)
95 xevent.xclient.data.l[i] = va_arg(argp, int);
98 DEBUG_X11("Send ClientMessage Event: wnd=0x%04X", (unsigned int) xevent.xclient.window);
99 XSendEvent(xfi->display, RootWindowOfScreen(xfi->screen), False,
100 SubstructureRedirectMask | SubstructureNotifyMask, &xevent);
101 XSync(xfi->display, False);
106 void xf_SetWindowFullscreen(xfInfo* xfi, xfWindow* window, boolean fullscreen)
110 xf_SetWindowDecorations(xfi, window, false);
112 XMoveResizeWindow(xfi->display, window->handle, 0, 0, window->width, window->height);
113 XMapRaised(xfi->display, window->handle);
115 window->fullscreen = true;
119 /* http://tronche.com/gui/x/xlib/window-information/XGetWindowProperty.html */
121 boolean xf_GetWindowProperty(xfInfo* xfi, Window window, Atom property, int length,
122 unsigned long* nitems, unsigned long* bytes, uint8** prop)
128 if (property == None)
131 status = XGetWindowProperty(xfi->display, window,
132 property, 0, length, false, AnyPropertyType,
133 &actual_type, &actual_format, nitems, bytes, prop);
135 if (status != Success)
138 if (actual_type == None)
140 DEBUG_WARN("Property %lu does not exist", property);
147 boolean xf_GetCurrentDesktop(xfInfo* xfi)
150 unsigned long nitems;
154 status = xf_GetWindowProperty(xfi, DefaultRootWindow(xfi->display),
155 xfi->_NET_CURRENT_DESKTOP, 1, &nitems, &bytes, &prop);
157 if (status != true) {
161 xfi->current_desktop = (int) *prop;
167 boolean xf_GetWorkArea(xfInfo* xfi)
171 unsigned long nitems;
175 status = xf_GetCurrentDesktop(xfi);
180 status = xf_GetWindowProperty(xfi, DefaultRootWindow(xfi->display),
181 xfi->_NET_WORKAREA, 32 * 4, &nitems, &bytes, &prop);
186 if ((xfi->current_desktop * 4 + 3) >= nitems) {
191 plong = (long*) prop;
193 xfi->workArea.x = plong[xfi->current_desktop * 4 + 0];
194 xfi->workArea.y = plong[xfi->current_desktop * 4 + 1];
195 xfi->workArea.width = plong[xfi->current_desktop * 4 + 2];
196 xfi->workArea.height = plong[xfi->current_desktop * 4 + 3];
202 void xf_SetWindowDecorations(xfInfo* xfi, xfWindow* window, boolean show)
204 PropMotifWmHints hints;
206 hints.decorations = (show) ? MWM_DECOR_ALL : 0;
207 hints.functions = MWM_FUNC_ALL ;
208 hints.flags = MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS;
210 XChangeProperty(xfi->display, window->handle, xfi->_MOTIF_WM_HINTS, xfi->_MOTIF_WM_HINTS, 32,
211 PropModeReplace, (uint8*) &hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
214 void xf_SetWindowUnlisted(xfInfo* xfi, xfWindow* window)
216 Atom window_state[2];
218 window_state[0] = xfi->_NET_WM_STATE_SKIP_PAGER;
219 window_state[1] = xfi->_NET_WM_STATE_SKIP_TASKBAR;
221 XChangeProperty(xfi->display, window->handle, xfi->_NET_WM_STATE,
222 XA_ATOM, 32, PropModeReplace, (uint8*) &window_state, 2);
225 void xf_SetWindowStyle(xfInfo* xfi, xfWindow* window, uint32 style, uint32 ex_style)
229 if ((ex_style & WS_EX_TOPMOST) || (ex_style & WS_EX_TOOLWINDOW))
232 * These include tool tips, dropdown menus, etc. These won't work
233 * correctly if the local window manager resizes or moves them.
234 * Set override redirect to prevent this from occurring.
237 XSetWindowAttributes attrs;
238 attrs.override_redirect = True;
239 XChangeWindowAttributes(xfi->display, window->handle, CWOverrideRedirect, &attrs);
240 window->is_transient = true;
241 xf_SetWindowUnlisted(xfi, window);
243 window_type = xfi->_NET_WM_WINDOW_TYPE_POPUP;
245 else if (style & WS_POPUP)
247 /* this includes dialogs, popups, etc, that need to be full-fledged windows */
248 window_type = xfi->_NET_WM_WINDOW_TYPE_DIALOG;
249 xf_SetWindowUnlisted(xfi, window);
253 window_type = xfi->_NET_WM_WINDOW_TYPE_NORMAL;
256 XChangeProperty(xfi->display, window->handle, xfi->_NET_WM_WINDOW_TYPE,
257 XA_ATOM, 32, PropModeReplace, (uint8*) &window_type, 1);
261 xfWindow* xf_CreateDesktopWindow(xfInfo* xfi, char* name, int width, int height, boolean decorations)
265 window = (xfWindow*) xzalloc(sizeof(xfWindow));
270 XClassHint* class_hints;
272 window->width = width;
273 window->height = height;
274 window->fullscreen = false;
275 window->decorations = decorations;
276 window->local_move.state = LMS_NOT_ACTIVE;
277 window->is_mapped = false;
278 window->is_transient = false;
280 window->handle = XCreateWindow(xfi->display, RootWindowOfScreen(xfi->screen),
281 xfi->workArea.x, xfi->workArea.y, xfi->width, xfi->height, 0, xfi->depth, InputOutput, xfi->visual,
282 CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
283 CWBorderPixel | CWWinGravity | CWBitGravity, &xfi->attribs);
285 class_hints = XAllocClassHint();
287 if (class_hints != NULL)
289 class_hints->res_name = "xfreerdp";
290 class_hints->res_class = "xfreerdp";
291 XSetClassHint(xfi->display, window->handle, class_hints);
295 xf_ResizeDesktopWindow(xfi, window, width, height);
296 xf_SetWindowDecorations(xfi, window, decorations);
299 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
300 VisibilityChangeMask | FocusChangeMask | StructureNotifyMask |
301 PointerMotionMask | ExposureMask | PropertyChangeMask;
303 if (xfi->grab_keyboard)
304 input_mask |= EnterWindowMask | LeaveWindowMask;
306 XChangeProperty(xfi->display, window->handle, xfi->_NET_WM_ICON, XA_CARDINAL, 32,
307 PropModeReplace, (uint8*) xf_icon_prop, sizeof(xf_icon_prop) / sizeof(long));
309 XSelectInput(xfi->display, window->handle, input_mask);
310 XMapWindow(xfi->display, window->handle);
313 XStoreName(xfi->display, window->handle, name);
318 void xf_ResizeDesktopWindow(xfInfo* xfi, xfWindow* window, int width, int height)
320 XSizeHints* size_hints;
322 size_hints = XAllocSizeHints();
326 size_hints->flags = PMinSize | PMaxSize;
327 size_hints->min_width = size_hints->max_width = xfi->width;
328 size_hints->min_height = size_hints->max_height = xfi->height;
329 XSetWMNormalHints(xfi->display, window->handle, size_hints);
334 void xf_FixWindowCoordinates(xfInfo* xfi, int* x, int* y, int* width, int* height)
339 vscreen_width = xfi->vscreen.area.right - xfi->vscreen.area.left + 1;
340 vscreen_height = xfi->vscreen.area.bottom - xfi->vscreen.area.top + 1;
350 if (*x < xfi->vscreen.area.left)
353 *x = xfi->vscreen.area.left;
355 if (*y < xfi->vscreen.area.top)
358 *y = xfi->vscreen.area.top;
360 if (*width > vscreen_width)
362 *width = vscreen_width;
364 if (*height > vscreen_height)
366 *height = vscreen_height;
370 char rail_window_class[] = "RAIL:00000000";
372 xfWindow* xf_CreateWindow(xfInfo* xfi, rdpWindow* wnd, int x, int y, int width, int height, uint32 id)
376 window = (xfWindow*) xzalloc(sizeof(xfWindow));
378 xf_FixWindowCoordinates(xfi, &x, &y, &width, &height);
382 window->right = x + width - 1;
383 window->bottom = y + height - 1;
384 window->width = width;
385 window->height = height;
388 XClassHint* class_hints;
391 window->decorations = false;
392 window->fullscreen = false;
393 window->window = wnd;
394 window->local_move.state = LMS_NOT_ACTIVE;
395 window->is_mapped = false;
396 window->is_transient = false;
398 window->handle = XCreateWindow(xfi->display, RootWindowOfScreen(xfi->screen),
399 x, y, window->width, window->height, 0, xfi->depth, InputOutput, xfi->visual,
400 CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
401 CWBorderPixel | CWWinGravity | CWBitGravity, &xfi->attribs);
403 DEBUG_X11_LMS("Create window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d rdp=0x%X",
404 (uint32) window->handle, window->left, window->top, window->right, window->bottom,
405 window->width, window->height, wnd->windowId);
407 xf_SetWindowDecorations(xfi, window, window->decorations);
408 xf_SetWindowStyle(xfi, window, wnd->style, wnd->extendedStyle);
410 class_hints = XAllocClassHint();
412 if (class_hints != NULL)
415 class = xmalloc(sizeof(rail_window_class));
416 snprintf(class, sizeof(rail_window_class), "RAIL:%08X", id);
417 class_hints->res_name = "RAIL";
418 class_hints->res_class = class;
419 XSetClassHint(xfi->display, window->handle, class_hints);
424 XSetWMProtocols(xfi->display, window->handle, &(xfi->WM_DELETE_WINDOW), 1);
426 input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask |
427 ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
428 PointerMotionMask | Button1MotionMask | Button2MotionMask |
429 Button3MotionMask | Button4MotionMask | Button5MotionMask |
430 ButtonMotionMask | KeymapStateMask | ExposureMask |
431 VisibilityChangeMask | StructureNotifyMask | SubstructureNotifyMask |
432 SubstructureRedirectMask | FocusChangeMask | PropertyChangeMask |
433 ColormapChangeMask | OwnerGrabButtonMask;
435 XSelectInput(xfi->display, window->handle, input_mask);
436 XMapWindow(xfi->display, window->handle);
438 memset(&gcv, 0, sizeof(gcv));
439 window->gc = XCreateGC(xfi->display, window->handle, GCGraphicsExposures, &gcv);
441 xf_MoveWindow(xfi, window, x, y, width, height);
446 void xf_SetWindowMinMaxInfo(xfInfo* xfi, xfWindow* window,
447 int maxWidth, int maxHeight, int maxPosX, int maxPosY,
448 int minTrackWidth, int minTrackHeight, int maxTrackWidth, int maxTrackHeight)
450 XSizeHints* size_hints;
452 size_hints = XAllocSizeHints();
456 size_hints->flags = PMinSize | PMaxSize | PResizeInc;
458 size_hints->min_width = minTrackWidth;
459 size_hints->min_height = minTrackHeight;
461 size_hints->max_width = maxTrackWidth;
462 size_hints->max_height = maxTrackHeight;
464 /* to speedup window drawing we need to select optimal value for sizing step. */
465 size_hints->width_inc = size_hints->height_inc = 1;
467 XSetWMNormalHints(xfi->display, window->handle, size_hints);
472 void xf_StartLocalMoveSize(xfInfo* xfi, xfWindow* window, int direction, int x, int y)
476 if (window->local_move.state != LMS_NOT_ACTIVE)
479 DEBUG_X11_LMS("direction=%d window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d "
480 "RDP=0x%X rc={l=%d t=%d} w=%d h=%d mouse_x=%d mouse_y=%d",
481 direction, (uint32) window->handle,
482 window->left, window->top, window->right, window->bottom,
483 window->width, window->height, window->window->windowId,
484 window->window->windowOffsetX, window->window->windowOffsetY,
485 window->window->windowWidth, window->window->windowHeight, x, y);
487 window->local_move.root_x = x;
488 window->local_move.root_y = y;
489 window->local_move.state = LMS_STARTING;
491 XTranslateCoordinates(xfi->display, RootWindowOfScreen(xfi->screen), window->handle,
492 window->local_move.root_x,
493 window->local_move.root_y,
494 &window->local_move.window_x,
495 &window->local_move.window_y,
498 XUngrabPointer(xfi->display, CurrentTime);
500 xf_SendClientEvent(xfi, window,
501 xfi->_NET_WM_MOVERESIZE, /* request X window manager to initiate a local move */
502 5, /* 5 arguments to follow */
503 x, /* x relative to root window */
504 y, /* y relative to root window */
505 direction, /* extended ICCM direction flag */
506 1, /* simulated mouse button 1 */
507 1); /* 1 == application request per extended ICCM */
510 void xf_EndLocalMoveSize(xfInfo *xfi, xfWindow *window)
513 DEBUG_X11_LMS("state=%d window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d "
514 "RDP=0x%X rc={l=%d t=%d} w=%d h=%d",
515 window->local_move.state,
516 (uint32) window->handle, window->left, window->top, window->right, window->bottom,
517 window->width, window->height, window->window->windowId,
518 window->window->windowOffsetX, window->window->windowOffsetY,
519 window->window->windowWidth, window->window->windowHeight);
521 if (window->local_move.state == LMS_NOT_ACTIVE)
524 if (window->local_move.state == LMS_STARTING)
527 * The move never was property started. This can happen due to race
528 * conditions between the mouse button up and the communications to the
529 * RDP server for local moves. We must cancel the X window manager move.
530 * Per ICCM, the X client can ask to cancel an active move.
532 xf_SendClientEvent(xfi, window,
533 xfi->_NET_WM_MOVERESIZE, /* request X window manager to abort a local move */
534 5, /* 5 arguments to follow */
535 window->local_move.root_x, /* x relative to root window */
536 window->local_move.root_y, /* y relative to root window */
537 _NET_WM_MOVERESIZE_CANCEL, /* extended ICCM direction flag */
538 1, /* simulated mouse button 1 */
539 1); /* 1 == application request per extended ICCM */
542 window->local_move.state = LMS_NOT_ACTIVE;
545 void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int height)
547 boolean resize = false;
549 if ((width * height) < 1)
552 if ((window->width != width) || (window->height != height))
555 if (window->local_move.state == LMS_STARTING ||
556 window->local_move.state == LMS_ACTIVE)
559 DEBUG_X11_LMS("window=0x%X rc={l=%d t=%d r=%d b=%d} w=%u h=%u "
560 "new rc={l=%d t=%d r=%d b=%d} w=%u h=%u"
561 " RDP=0x%X rc={l=%d t=%d} w=%d h=%d",
562 (uint32) window->handle, window->left, window->top,
563 window->right, window->bottom, window->width, window->height,
564 x, y, x + width -1, y + height -1, width, height,
565 window->window->windowId,
566 window->window->windowOffsetX, window->window->windowOffsetY,
567 window->window->windowWidth, window->window->windowHeight);
571 window->right = x + width - 1;
572 window->bottom = y + height - 1;
573 window->width = width;
574 window->height = height;
577 XMoveResizeWindow(xfi->display, window->handle, x, y, width, height);
579 XMoveWindow(xfi->display, window->handle, x, y);
581 xf_UpdateWindowArea(xfi, window, 0, 0, width, height);
584 void xf_ShowWindow(xfInfo* xfi, xfWindow* window, uint8 state)
589 XWithdrawWindow(xfi->display, window->handle, xfi->screen_number);
592 case WINDOW_SHOW_MINIMIZED:
593 XIconifyWindow(xfi->display, window->handle, xfi->screen_number);
596 case WINDOW_SHOW_MAXIMIZED:
597 XRaiseWindow(xfi->display, window->handle);
601 XMapWindow(xfi->display, window->handle);
605 XFlush(xfi->display);
608 void xf_SetWindowIcon(xfInfo* xfi, xfWindow* window, rdpIcon* icon)
617 if (icon->big != true)
620 pixels = icon->entry->width * icon->entry->height;
621 propsize = 2 + pixels;
622 propdata = xmalloc(propsize * sizeof(long));
624 propdata[0] = icon->entry->width;
625 propdata[1] = icon->entry->height;
626 dstp = &(propdata[2]);
627 srcp = (uint32*) icon->extra;
629 for (y = 0; y < icon->entry->height; y++)
631 for (x = 0; x < icon->entry->width; x++)
637 XChangeProperty(xfi->display, window->handle, xfi->_NET_WM_ICON, XA_CARDINAL, 32,
638 PropModeReplace, (uint8*) propdata, propsize);
640 XFlush(xfi->display);
643 void xf_SetWindowRects(xfInfo* xfi, xfWindow* window, RECTANGLE_16* rects, int nrects)
648 xrects = xmalloc(sizeof(XRectangle) * nrects);
650 for (i = 0; i < nrects; i++)
652 xrects[i].x = rects[i].left;
653 xrects[i].y = rects[i].top;
654 xrects[i].width = rects[i].right - rects[i].left;
655 xrects[i].height = rects[i].bottom - rects[i].top;
659 XShapeCombineRectangles(xfi->display, window->handle, ShapeBounding, 0, 0, xrects, nrects, ShapeSet, 0);
665 void xf_SetWindowVisibilityRects(xfInfo* xfi, xfWindow* window, RECTANGLE_16* rects, int nrects)
670 xrects = xmalloc(sizeof(XRectangle) * nrects);
672 for (i = 0; i < nrects; i++)
674 xrects[i].x = rects[i].left;
675 xrects[i].y = rects[i].top;
676 xrects[i].width = rects[i].right - rects[i].left;
677 xrects[i].height = rects[i].bottom - rects[i].top;
681 //XShapeCombineRectangles(xfi->display, window->handle, ShapeBounding, 0, 0, xrects, nrects, ShapeSet, 0);
687 void xf_UpdateWindowArea(xfInfo* xfi, xfWindow* window, int x, int y, int width, int height)
691 wnd = window->window;
693 ax = x + wnd->windowOffsetX;
694 ay = y + wnd->windowOffsetY;
696 if (ax + width > wnd->windowOffsetX + wnd->windowWidth)
697 width = (wnd->windowOffsetX + wnd->windowWidth - 1) - ax;
699 if (ay + height > wnd->windowOffsetY + wnd->windowHeight)
700 height = (wnd->windowOffsetY + wnd->windowHeight - 1) - ay;
704 XPutImage(xfi->display, xfi->primary, window->gc, xfi->image,
705 ax, ay, ax, ay, width, height);
708 XCopyArea(xfi->display, xfi->primary, window->handle, window->gc,
709 ax, ay, width, height, x, y);
711 XFlush(xfi->display);
714 boolean xf_IsWindowBorder(xfInfo* xfi, xfWindow* xfw, int x, int y)
717 boolean clientArea = false;
718 boolean windowArea = false;
722 if (((x > wnd->clientOffsetX) && (x < wnd->clientOffsetX + wnd->clientAreaWidth)) &&
723 ((y > wnd->clientOffsetY) && (y < wnd->clientOffsetY + wnd->clientAreaHeight)))
726 if (((x > wnd->windowOffsetX) && (x < wnd->windowOffsetX + wnd->windowWidth)) &&
727 ((y > wnd->windowOffsetY) && (y < wnd->windowOffsetY + wnd->windowHeight)))
730 return (windowArea && !(clientArea));
733 void xf_DestroyWindow(xfInfo* xfi, xfWindow* window)
738 if (xfi->window == window)
742 XFreeGC(xfi->display, window->gc);
746 XUnmapWindow(xfi->display, window->handle);
747 XDestroyWindow(xfi->display, window->handle);