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.
21 #include <X11/Xutil.h>
23 #include <freerdp/utils/event.h>
24 #include <freerdp/utils/hexdump.h>
25 #include <freerdp/utils/rail.h>
26 #include <freerdp/rail/rail.h>
28 #include "xf_window.h"
31 void xf_rail_enable_remoteapp_mode(xfInfo* xfi)
33 if (xfi->remote_app == false)
35 xfi->remote_app = true;
36 xfi->drawable = DefaultRootWindow(xfi->display);
37 xf_DestroyWindow(xfi, xfi->window);
42 void xf_rail_paint(xfInfo* xfi, rdpRail* rail, sint32 uleft, sint32 utop, uint32 uright, uint32 ubottom)
47 uint32 iwidth, iheight;
49 uint32 iright, ibottom;
51 uint32 wright, wbottom;
53 window_list_rewind(rail->list);
55 while (window_list_has_next(rail->list))
57 window = window_list_get_next(rail->list);
58 xfw = (xfWindow*) window->extra;
60 // RDP can have zero width or height windows. X cannot, so we ignore these.
62 if (window->windowWidth == 0 || window->windowHeight == 0)
67 wleft = window->windowOffsetX;
68 wtop = window->windowOffsetY;
69 wright = window->windowOffsetX + window->windowWidth - 1;
70 wbottom = window->windowOffsetY + window->windowHeight - 1;
72 ileft = MAX(uleft, wleft);
73 itop = MAX(utop, wtop);
74 iright = MIN(uright, wright);
75 ibottom = MIN(ubottom, wbottom);
77 iwidth = iright - ileft + 1;
78 iheight = ibottom - itop + 1;
80 intersect = ((iright > ileft) && (ibottom > itop)) ? true : false;
84 xf_UpdateWindowArea(xfi, xfw, ileft - wleft, itop - wtop, iwidth, iheight);
89 void xf_rail_CreateWindow(rdpRail* rail, rdpWindow* window)
94 xfi = (xfInfo*) rail->extra;
96 xf_rail_enable_remoteapp_mode(xfi);
98 xfw = xf_CreateWindow((xfInfo*) rail->extra, window,
99 window->windowOffsetX, window->windowOffsetY,
100 window->windowWidth, window->windowHeight,
103 xf_SetWindowStyle(xfi, xfw, window->style, window->extendedStyle);
105 XStoreName(xfi->display, xfw->handle, window->title);
107 window->extra = (void*) xfw;
108 window->extraId = (void*) xfw->handle;
111 void xf_rail_MoveWindow(rdpRail* rail, rdpWindow* window)
116 xfi = (xfInfo*) rail->extra;
117 xfw = (xfWindow*) window->extra;
119 // Do nothing if window is already in the correct position
120 if ( xfw->left == window->windowOffsetX &&
121 xfw->top == window->windowOffsetY &&
122 xfw->width == window->windowWidth &&
123 xfw->height == window->windowHeight)
128 xf_MoveWindow((xfInfo*) rail->extra, xfw,
129 window->windowOffsetX, window->windowOffsetY,
130 window->windowWidth, window->windowHeight);
133 void xf_rail_ShowWindow(rdpRail* rail, rdpWindow* window, uint8 state)
138 xfi = (xfInfo*) rail->extra;
139 xfw = (xfWindow*) window->extra;
141 xf_ShowWindow((xfInfo*) rail->extra, xfw, state);
144 void xf_rail_SetWindowText(rdpRail* rail, rdpWindow* window)
149 xfi = (xfInfo*) rail->extra;
150 xfw = (xfWindow*) window->extra;
152 XStoreName(xfi->display, xfw->handle, window->title);
155 void xf_rail_SetWindowIcon(rdpRail* rail, rdpWindow* window, rdpIcon* icon)
160 xfi = (xfInfo*) rail->extra;
161 xfw = (xfWindow*) window->extra;
163 icon->extra = freerdp_icon_convert(icon->entry->bitsColor, NULL, icon->entry->bitsMask,
164 icon->entry->width, icon->entry->height, icon->entry->bpp, rail->clrconv);
166 xf_SetWindowIcon(xfi, xfw, icon);
169 void xf_rail_SetWindowRects(rdpRail* rail, rdpWindow* window)
174 xfi = (xfInfo*) rail->extra;
175 xfw = (xfWindow*) window->extra;
177 xf_SetWindowRects(xfi, xfw, window->windowRects, window->numWindowRects);
180 void xf_rail_SetWindowVisibilityRects(rdpRail* rail, rdpWindow* window)
185 xfi = (xfInfo*) rail->extra;
186 xfw = (xfWindow*) window->extra;
188 xf_SetWindowVisibilityRects(xfi, xfw, window->windowRects, window->numWindowRects);
191 void xf_rail_DestroyWindow(rdpRail* rail, rdpWindow* window)
194 xfw = (xfWindow*) window->extra;
195 xf_DestroyWindow((xfInfo*) rail->extra, xfw);
198 void xf_rail_register_callbacks(xfInfo* xfi, rdpRail* rail)
200 rail->extra = (void*) xfi;
201 rail->rail_CreateWindow = xf_rail_CreateWindow;
202 rail->rail_MoveWindow = xf_rail_MoveWindow;
203 rail->rail_ShowWindow = xf_rail_ShowWindow;
204 rail->rail_SetWindowText = xf_rail_SetWindowText;
205 rail->rail_SetWindowIcon = xf_rail_SetWindowIcon;
206 rail->rail_SetWindowRects = xf_rail_SetWindowRects;
207 rail->rail_SetWindowVisibilityRects = xf_rail_SetWindowVisibilityRects;
208 rail->rail_DestroyWindow = xf_rail_DestroyWindow;
211 static void xf_on_free_rail_client_event(RDP_EVENT* event)
213 if (event->event_class == RDP_EVENT_CLASS_RAIL)
215 rail_free_cloned_order(event->event_type, event->user_data);
219 static void xf_send_rail_client_event(rdpChannels* channels, uint16 event_type, void* param)
221 RDP_EVENT* out_event = NULL;
222 void * payload = NULL;
224 payload = rail_clone_order(event_type, param);
227 out_event = freerdp_event_new(RDP_EVENT_CLASS_RAIL, event_type,
228 xf_on_free_rail_client_event, payload);
229 freerdp_channels_send_event(channels, out_event);
233 void xf_rail_send_activate(xfInfo* xfi, Window xwindow, boolean enabled)
236 rdpChannels* channels;
237 rdpWindow* rail_window;
238 RAIL_ACTIVATE_ORDER activate;
240 rail = xfi->_context->rail;
241 channels = xfi->_context->channels;
243 rail_window = window_list_get_by_extra_id(rail->list, (void*) xwindow);
245 if (rail_window == NULL)
248 activate.windowId = rail_window->windowId;
249 activate.enabled = enabled;
251 xf_send_rail_client_event(channels, RDP_EVENT_TYPE_RAIL_CLIENT_ACTIVATE, &activate);
254 void xf_rail_send_client_system_command(xfInfo* xfi, uint32 windowId, uint16 command)
256 rdpChannels* channels;
257 RAIL_SYSCOMMAND_ORDER syscommand;
259 channels = xfi->_context->channels;
261 syscommand.windowId = windowId;
262 syscommand.command = command;
264 xf_send_rail_client_event(channels, RDP_EVENT_TYPE_RAIL_CLIENT_SYSCOMMAND, &syscommand);
268 * The position of the X window can become out of sync with the RDP window
269 * if the X window is moved locally by the window manager. In this event
270 * send an update to the RDP server informing it of the new window position
273 void xf_rail_adjust_position(xfInfo* xfi, rdpWindow *window)
276 rdpChannels* channels;
277 RAIL_WINDOW_MOVE_ORDER window_move;
279 xfw = (xfWindow*) window->extra;
280 channels = xfi->_context->channels;
282 if (! xfw->is_mapped || xfw->local_move.state != LMS_NOT_ACTIVE)
285 // If current window position disagrees with RDP window position, send
286 // update to RDP server
287 if ( xfw->left != window->windowOffsetX ||
288 xfw->top != window->windowOffsetY ||
289 xfw->width != window->windowWidth ||
290 xfw->height != window->windowHeight)
292 window_move.windowId = window->windowId;
293 window_move.left = xfw->left;
294 window_move.top = xfw->top;
295 window_move.right = xfw->right;
296 window_move.bottom = xfw->bottom;
298 DEBUG_X11_LMS("window=0x%X rc={l=%d t=%d r=%d b=%d} w=%u h=%u"
299 " RDP=0x%X rc={l=%d t=%d} w=%d h=%d",
300 (uint32) xfw->handle, xfw->left, xfw->top,
301 xfw->right, xfw->bottom, xfw->width, xfw->height,
303 window->windowOffsetX, window->windowOffsetY,
304 window->windowWidth, window->windowHeight);
306 xf_send_rail_client_event(channels, RDP_EVENT_TYPE_RAIL_CLIENT_WINDOW_MOVE, &window_move);
310 void xf_rail_end_local_move(xfInfo* xfi, rdpWindow *window)
313 rdpChannels* channels;
314 RAIL_WINDOW_MOVE_ORDER window_move;
316 rdpInput* input = xfi->instance->input;
318 xfw = (xfWindow*) window->extra;
319 channels = xfi->_context->channels;
321 // Send RDP client event to inform RDP server
323 window_move.windowId = window->windowId;
324 window_move.left = xfw->left;
325 window_move.top = xfw->top;
326 window_move.right = xfw->right + 1; // In the update to RDP the position is one past the window
327 window_move.bottom = xfw->bottom + 1;
329 DEBUG_X11_LMS("window=0x%X rc={l=%d t=%d r=%d b=%d} w=%d h=%d",
330 (uint32) xfw->handle,
331 xfw->left, xfw->top, xfw->right, xfw->bottom,
332 xfw->width, xfw->height);
334 xf_send_rail_client_event(channels, RDP_EVENT_TYPE_RAIL_CLIENT_WINDOW_MOVE, &window_move);
336 // Send synthetic button up event to the RDP server. This is per the RDP spec to
337 // indicate a local move has finished.
339 x = xfw->left + xfw->local_move.window_x;
340 y = xfw->top + xfw->local_move.window_y;
341 input->MouseEvent(input, PTR_FLAGS_BUTTON1, x, y);
343 // Proactively update the RAIL window dimensions. There is a race condition where
344 // we can start to receive GDI orders for the new window dimensions before we
345 // receive the RAIL ORDER for the new window size. This avoids that race condition.
347 window->windowOffsetX = xfw->left;
348 window->windowOffsetY = xfw->top;
349 window->windowWidth = xfw->width;
350 window->windowHeight = xfw->height;
352 xfw->local_move.state = LMS_TERMINATING;
355 void xf_process_rail_get_sysparams_event(xfInfo* xfi, rdpChannels* channels, RDP_EVENT* event)
357 RAIL_SYSPARAM_ORDER* sysparam;
359 sysparam = (RAIL_SYSPARAM_ORDER*) event->user_data;
361 sysparam->workArea.left = xfi->workArea.x;
362 sysparam->workArea.top = xfi->workArea.y;
363 sysparam->workArea.right = xfi->workArea.x + xfi->workArea.width;
364 sysparam->workArea.bottom = xfi->workArea.y + xfi->workArea.height;
366 sysparam->taskbarPos.left = 0;
367 sysparam->taskbarPos.top = 0;
368 sysparam->taskbarPos.right = 0;
369 sysparam->taskbarPos.bottom = 0;
371 sysparam->dragFullWindows = false;
373 xf_send_rail_client_event(channels, RDP_EVENT_TYPE_RAIL_CLIENT_SET_SYSPARAMS, sysparam);
376 const char* error_code_names[] =
379 "RAIL_EXEC_E_HOOK_NOT_LOADED",
380 "RAIL_EXEC_E_DECODE_FAILED",
381 "RAIL_EXEC_E_NOT_IN_ALLOWLIST",
382 "RAIL_EXEC_E_FILE_NOT_FOUND",
384 "RAIL_EXEC_E_SESSION_LOCKED"
387 void xf_process_rail_exec_result_event(xfInfo* xfi, rdpChannels* channels, RDP_EVENT* event)
389 RAIL_EXEC_RESULT_ORDER* exec_result;
391 exec_result = (RAIL_EXEC_RESULT_ORDER*) event->user_data;
393 if (exec_result->execResult != RAIL_EXEC_S_OK)
395 printf("RAIL exec error: execResult=%s NtError=0x%X\n",
396 error_code_names[exec_result->execResult], exec_result->rawResult);
397 xfi->disconnect = True;
401 xf_rail_enable_remoteapp_mode(xfi);
405 void xf_process_rail_server_sysparam_event(xfInfo* xfi, rdpChannels* channels, RDP_EVENT* event)
407 RAIL_SYSPARAM_ORDER* sysparam = (RAIL_SYSPARAM_ORDER*) event->user_data;
409 switch (sysparam->param)
411 case SPI_SET_SCREEN_SAVE_ACTIVE:
414 case SPI_SET_SCREEN_SAVE_SECURE:
419 void xf_process_rail_server_minmaxinfo_event(xfInfo* xfi, rdpChannels* channels, RDP_EVENT* event)
422 rdpWindow* rail_window = NULL;
423 RAIL_MINMAXINFO_ORDER* minmax = (RAIL_MINMAXINFO_ORDER*) event->user_data;
425 rail = ((rdpContext*) xfi->context)->rail;
426 rail_window = window_list_get_by_id(rail->list, minmax->windowId);
428 if (rail_window != NULL)
430 xfWindow * window = NULL;
431 window = (xfWindow *) rail_window->extra;
433 DEBUG_X11_LMS("windowId=0x%X maxWidth=%d maxHeight=%d maxPosX=%d maxPosY=%d "
434 "minTrackWidth=%d minTrackHeight=%d maxTrackWidth=%d maxTrackHeight=%d",
435 minmax->windowId, minmax->maxWidth, minmax->maxHeight,
436 (sint16)minmax->maxPosX, (sint16)minmax->maxPosY,
437 minmax->minTrackWidth, minmax->minTrackHeight,
438 minmax->maxTrackWidth, minmax->maxTrackHeight);
440 xf_SetWindowMinMaxInfo(xfi, window, minmax->maxWidth, minmax->maxHeight, minmax->maxPosX, minmax->maxPosY,
441 minmax->minTrackWidth, minmax->minTrackHeight, minmax->maxTrackWidth, minmax->maxTrackHeight);
445 const char* movetype_names[] =
452 "RAIL_WMSZ_TOPRIGHT",
454 "RAIL_WMSZ_BOTTOMLEFT",
455 "RAIL_WMSZ_BOTTOMRIGHT",
461 void xf_process_rail_server_localmovesize_event(xfInfo* xfi, rdpChannels* channels, RDP_EVENT* event)
464 rdpWindow* rail_window = NULL;
465 RAIL_LOCALMOVESIZE_ORDER* movesize = (RAIL_LOCALMOVESIZE_ORDER*) event->user_data;
470 rail = ((rdpContext*) xfi->context)->rail;
471 rail_window = window_list_get_by_id(rail->list, movesize->windowId);
473 if (rail_window != NULL)
475 xfWindow* xfw = NULL;
476 xfw = (xfWindow*) rail_window->extra;
478 DEBUG_X11_LMS("windowId=0x%X isMoveSizeStart=%d moveSizeType=%s PosX=%d PosY=%d",
479 movesize->windowId, movesize->isMoveSizeStart,
480 movetype_names[movesize->moveSizeType], (sint16) movesize->posX, (sint16) movesize->posY);
482 switch (movesize->moveSizeType)
484 case RAIL_WMSZ_LEFT: //0x1
485 direction = _NET_WM_MOVERESIZE_SIZE_LEFT;
489 case RAIL_WMSZ_RIGHT: //0x2
490 direction = _NET_WM_MOVERESIZE_SIZE_RIGHT;
494 case RAIL_WMSZ_TOP: //0x3
495 direction = _NET_WM_MOVERESIZE_SIZE_TOP;
499 case RAIL_WMSZ_TOPLEFT: //0x4
500 direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT;
504 case RAIL_WMSZ_TOPRIGHT: //0x5
505 direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
509 case RAIL_WMSZ_BOTTOM: //0x6
510 direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM;
514 case RAIL_WMSZ_BOTTOMLEFT: //0x7
515 direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
519 case RAIL_WMSZ_BOTTOMRIGHT: //0x8
520 direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
524 case RAIL_WMSZ_MOVE: //0x9
525 direction = _NET_WM_MOVERESIZE_MOVE;
526 XTranslateCoordinates(xfi->display, xfw->handle,
527 RootWindowOfScreen(xfi->screen),
528 movesize->posX, movesize->posY, &x, &y, &child_window);
530 case RAIL_WMSZ_KEYMOVE: //0xA
531 direction = _NET_WM_MOVERESIZE_MOVE_KEYBOARD;
535 case RAIL_WMSZ_KEYSIZE: //0xB
536 direction = _NET_WM_MOVERESIZE_SIZE_KEYBOARD;
542 if (movesize->isMoveSizeStart)
544 xf_StartLocalMoveSize(xfi, xfw, direction, x, y);
546 xf_EndLocalMoveSize(xfi, xfw);
551 void xf_process_rail_appid_resp_event(xfInfo* xfi, rdpChannels* channels, RDP_EVENT* event)
553 RAIL_GET_APPID_RESP_ORDER* appid_resp =
554 (RAIL_GET_APPID_RESP_ORDER*)event->user_data;
556 printf("Server Application ID Response PDU: windowId=0x%X "
557 "applicationId=(length=%d dump)\n",
558 appid_resp->windowId, appid_resp->applicationId.length);
560 freerdp_hexdump(appid_resp->applicationId.string, appid_resp->applicationId.length);
563 void xf_process_rail_langbarinfo_event(xfInfo* xfi, rdpChannels* channels, RDP_EVENT* event)
565 RAIL_LANGBAR_INFO_ORDER* langbar =
566 (RAIL_LANGBAR_INFO_ORDER*) event->user_data;
568 printf("Language Bar Information PDU: languageBarStatus=0x%X\n",
569 langbar->languageBarStatus);
572 void xf_process_rail_event(xfInfo* xfi, rdpChannels* channels, RDP_EVENT* event)
574 switch (event->event_type)
576 case RDP_EVENT_TYPE_RAIL_CHANNEL_GET_SYSPARAMS:
577 xf_process_rail_get_sysparams_event(xfi, channels, event);
580 case RDP_EVENT_TYPE_RAIL_CHANNEL_EXEC_RESULTS:
581 xf_process_rail_exec_result_event(xfi, channels, event);
584 case RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_SYSPARAM:
585 xf_process_rail_server_sysparam_event(xfi, channels, event);
588 case RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_MINMAXINFO:
589 xf_process_rail_server_minmaxinfo_event(xfi, channels, event);
592 case RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_LOCALMOVESIZE:
593 xf_process_rail_server_localmovesize_event(xfi, channels, event);
596 case RDP_EVENT_TYPE_RAIL_CHANNEL_APPID_RESP:
597 xf_process_rail_appid_resp_event(xfi, channels, event);
600 case RDP_EVENT_TYPE_RAIL_CHANNEL_LANGBARINFO:
601 xf_process_rail_langbarinfo_event(xfi, channels, event);