1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * X11 Client Interface
4  *
5  * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  * Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
7  * Copyright 2014 Thincast Technologies GmbH
8  * Copyright 2014 Norbert Federa <norbert.federa@thincast.com>
9  * Copyright 2016 Armin Novak <armin.novak@thincast.com>
10  * Copyright 2016 Thincast Technologies GmbH
11  *
12  * Licensed under the Apache License, Version 2.0 (the "License");
13  * you may not use this file except in compliance with the License.
14  * You may obtain a copy of the License at
15  *
16  *     http://www.apache.org/licenses/LICENSE-2.0
17  *
18  * Unless required by applicable law or agreed to in writing, software
19  * distributed under the License is distributed on an "AS IS" BASIS,
20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  * See the License for the specific language governing permissions and
22  * limitations under the License.
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include <assert.h>
30 #include <float.h>
31 
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 #include <X11/Xatom.h>
35 
36 #ifdef WITH_XRENDER
37 #include <X11/extensions/Xrender.h>
38 #include <math.h>
39 #endif
40 
41 #ifdef WITH_XI
42 #include <X11/extensions/XInput.h>
43 #include <X11/extensions/XInput2.h>
44 #endif
45 
46 #ifdef WITH_XCURSOR
47 #include <X11/Xcursor/Xcursor.h>
48 #endif
49 
50 #ifdef WITH_XINERAMA
51 #include <X11/extensions/Xinerama.h>
52 #endif
53 
54 #include <X11/XKBlib.h>
55 
56 #include <errno.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <locale.h>
60 #include <unistd.h>
61 #include <string.h>
62 #include <termios.h>
63 #include <pthread.h>
64 #include <sys/wait.h>
65 #include <sys/types.h>
66 #include <sys/select.h>
67 
68 #include <freerdp/freerdp.h>
69 #include <freerdp/constants.h>
70 #include <freerdp/codec/nsc.h>
71 #include <freerdp/codec/rfx.h>
72 #include <freerdp/codec/color.h>
73 #include <freerdp/codec/bitmap.h>
74 
75 #include <freerdp/utils/signal.h>
76 #include <freerdp/utils/passphrase.h>
77 #include <freerdp/client/cliprdr.h>
78 #include <freerdp/client/channels.h>
79 
80 #include <freerdp/client/file.h>
81 #include <freerdp/client/cmdline.h>
82 
83 #include <winpr/crt.h>
84 #include <winpr/synch.h>
85 #include <winpr/file.h>
86 #include <winpr/print.h>
87 #include <winpr/sysinfo.h>
88 #include <X11/XKBlib.h>
89 
90 #include "xf_gdi.h"
91 #include "xf_rail.h"
92 #if defined(CHANNEL_TSMF_CLIENT)
93 #include "xf_tsmf.h"
94 #endif
95 #include "xf_event.h"
96 #include "xf_input.h"
97 #include "xf_cliprdr.h"
98 #include "xf_disp.h"
99 #include "xf_video.h"
100 #include "xf_monitor.h"
101 #include "xf_graphics.h"
102 #include "xf_keyboard.h"
103 #include "xf_input.h"
104 #include "xf_channels.h"
105 #include "xfreerdp.h"
106 
107 #include <freerdp/log.h>
108 #define TAG CLIENT_TAG("x11")
109 
110 #define MIN_PIXEL_DIFF 0.001
111 
112 static int (*_def_error_handler)(Display*, XErrorEvent*);
113 static int _xf_error_handler(Display* d, XErrorEvent* ev);
114 static void xf_check_extensions(xfContext* context);
115 static void xf_window_free(xfContext* xfc);
116 static BOOL xf_get_pixmap_info(xfContext* xfc);
117 
118 #ifdef WITH_XRENDER
xf_draw_screen_scaled(xfContext * xfc,int x,int y,int w,int h)119 static void xf_draw_screen_scaled(xfContext* xfc, int x, int y, int w, int h)
120 {
121 	XTransform transform;
122 	Picture windowPicture;
123 	Picture primaryPicture;
124 	XRenderPictureAttributes pa;
125 	XRenderPictFormat* picFormat;
126 	double xScalingFactor;
127 	double yScalingFactor;
128 	int x2;
129 	int y2;
130 	const char* filter;
131 	rdpSettings* settings = xfc->context.settings;
132 
133 	if (xfc->scaledWidth <= 0 || xfc->scaledHeight <= 0)
134 	{
135 		WLog_ERR(TAG, "the current window dimensions are invalid");
136 		return;
137 	}
138 
139 	if (settings->DesktopWidth <= 0 || settings->DesktopHeight <= 0)
140 	{
141 		WLog_ERR(TAG, "the window dimensions are invalid");
142 		return;
143 	}
144 
145 	xScalingFactor = settings->DesktopWidth / (double)xfc->scaledWidth;
146 	yScalingFactor = settings->DesktopHeight / (double)xfc->scaledHeight;
147 	XSetFillStyle(xfc->display, xfc->gc, FillSolid);
148 	XSetForeground(xfc->display, xfc->gc, 0);
149 	/* Black out possible space between desktop and window borders */
150 	{
151 		XRectangle box1 = { 0, 0, xfc->window->width, xfc->window->height };
152 		XRectangle box2 = { xfc->offset_x, xfc->offset_y, xfc->scaledWidth, xfc->scaledHeight };
153 		Region reg1 = XCreateRegion();
154 		Region reg2 = XCreateRegion();
155 		XUnionRectWithRegion(&box1, reg1, reg1);
156 		XUnionRectWithRegion(&box2, reg2, reg2);
157 
158 		if (XSubtractRegion(reg1, reg2, reg1) && !XEmptyRegion(reg1))
159 		{
160 			XSetRegion(xfc->display, xfc->gc, reg1);
161 			XFillRectangle(xfc->display, xfc->window->handle, xfc->gc, 0, 0, xfc->window->width,
162 			               xfc->window->height);
163 			XSetClipMask(xfc->display, xfc->gc, None);
164 		}
165 
166 		XDestroyRegion(reg1);
167 		XDestroyRegion(reg2);
168 	}
169 	picFormat = XRenderFindVisualFormat(xfc->display, xfc->visual);
170 	pa.subwindow_mode = IncludeInferiors;
171 	primaryPicture =
172 	    XRenderCreatePicture(xfc->display, xfc->primary, picFormat, CPSubwindowMode, &pa);
173 	windowPicture =
174 	    XRenderCreatePicture(xfc->display, xfc->window->handle, picFormat, CPSubwindowMode, &pa);
175 	/* avoid blurry filter when scaling factor is 2x, 3x, etc
176 	 * useful when the client has high-dpi monitor */
177 	filter = FilterBilinear;
178 	if (fabs(xScalingFactor - yScalingFactor) < MIN_PIXEL_DIFF)
179 	{
180 		const double inverseX = 1.0 / xScalingFactor;
181 		const double inverseRoundedX = round(inverseX);
182 		const double absInverse = fabs(inverseX - inverseRoundedX);
183 
184 		if (absInverse < MIN_PIXEL_DIFF)
185 			filter = FilterNearest;
186 	}
187 	XRenderSetPictureFilter(xfc->display, primaryPicture, filter, 0, 0);
188 	transform.matrix[0][0] = XDoubleToFixed(xScalingFactor);
189 	transform.matrix[0][1] = XDoubleToFixed(0.0);
190 	transform.matrix[0][2] = XDoubleToFixed(0.0);
191 	transform.matrix[1][0] = XDoubleToFixed(0.0);
192 	transform.matrix[1][1] = XDoubleToFixed(yScalingFactor);
193 	transform.matrix[1][2] = XDoubleToFixed(0.0);
194 	transform.matrix[2][0] = XDoubleToFixed(0.0);
195 	transform.matrix[2][1] = XDoubleToFixed(0.0);
196 	transform.matrix[2][2] = XDoubleToFixed(1.0);
197 	/* calculate and fix up scaled coordinates */
198 	x2 = x + w;
199 	y2 = y + h;
200 	x = floor(x / xScalingFactor) - 1;
201 	y = floor(y / yScalingFactor) - 1;
202 	w = ceil(x2 / xScalingFactor) + 1 - x;
203 	h = ceil(y2 / yScalingFactor) + 1 - y;
204 	XRenderSetPictureTransform(xfc->display, primaryPicture, &transform);
205 	XRenderComposite(xfc->display, PictOpSrc, primaryPicture, 0, windowPicture, x, y, 0, 0,
206 	                 xfc->offset_x + x, xfc->offset_y + y, w, h);
207 	XRenderFreePicture(xfc->display, primaryPicture);
208 	XRenderFreePicture(xfc->display, windowPicture);
209 }
210 
xf_picture_transform_required(xfContext * xfc)211 BOOL xf_picture_transform_required(xfContext* xfc)
212 {
213 	rdpSettings* settings = xfc->context.settings;
214 
215 	if ((xfc->offset_x != 0) || (xfc->offset_y != 0) ||
216 	    (xfc->scaledWidth != (INT64)settings->DesktopWidth) ||
217 	    (xfc->scaledHeight != (INT64)settings->DesktopHeight))
218 	{
219 		return TRUE;
220 	}
221 
222 	return FALSE;
223 }
224 #endif /* WITH_XRENDER defined */
225 
xf_draw_screen_(xfContext * xfc,int x,int y,int w,int h,const char * fkt,const char * file,int line)226 void xf_draw_screen_(xfContext* xfc, int x, int y, int w, int h, const char* fkt, const char* file,
227                      int line)
228 {
229 	if (!xfc)
230 	{
231 		WLog_DBG(TAG, "[%s] called from [%s] xfc=%p", __FUNCTION__, fkt, xfc);
232 		return;
233 	}
234 
235 	if (w == 0 || h == 0)
236 	{
237 		WLog_WARN(TAG, "invalid width and/or height specified: w=%d h=%d", w, h);
238 		return;
239 	}
240 
241 #ifdef WITH_XRENDER
242 
243 	if (xf_picture_transform_required(xfc))
244 	{
245 		xf_draw_screen_scaled(xfc, x, y, w, h);
246 		return;
247 	}
248 
249 #endif
250 	XCopyArea(xfc->display, xfc->primary, xfc->window->handle, xfc->gc, x, y, w, h, x, y);
251 }
252 
xf_desktop_resize(rdpContext * context)253 static BOOL xf_desktop_resize(rdpContext* context)
254 {
255 	rdpSettings* settings;
256 	xfContext* xfc = (xfContext*)context;
257 	settings = context->settings;
258 
259 	if (xfc->primary)
260 	{
261 		BOOL same = (xfc->primary == xfc->drawing) ? TRUE : FALSE;
262 		XFreePixmap(xfc->display, xfc->primary);
263 
264 		if (!(xfc->primary = XCreatePixmap(xfc->display, xfc->drawable, settings->DesktopWidth,
265 		                                   settings->DesktopHeight, xfc->depth)))
266 			return FALSE;
267 
268 		if (same)
269 			xfc->drawing = xfc->primary;
270 	}
271 
272 #ifdef WITH_XRENDER
273 
274 	if (!xfc->context.settings->SmartSizing)
275 	{
276 		xfc->scaledWidth = settings->DesktopWidth;
277 		xfc->scaledHeight = settings->DesktopHeight;
278 	}
279 
280 #endif
281 
282 	if (!xfc->fullscreen)
283 	{
284 		xf_ResizeDesktopWindow(xfc, xfc->window, settings->DesktopWidth, settings->DesktopHeight);
285 	}
286 	else
287 	{
288 #ifdef WITH_XRENDER
289 
290 		if (!xfc->context.settings->SmartSizing)
291 #endif
292 		{
293 			/* Update the saved width and height values the window will be
294 			 * resized to when toggling out of fullscreen */
295 			xfc->savedWidth = settings->DesktopWidth;
296 			xfc->savedHeight = settings->DesktopHeight;
297 		}
298 
299 		XSetFunction(xfc->display, xfc->gc, GXcopy);
300 		XSetFillStyle(xfc->display, xfc->gc, FillSolid);
301 		XSetForeground(xfc->display, xfc->gc, 0);
302 		XFillRectangle(xfc->display, xfc->drawable, xfc->gc, 0, 0, settings->DesktopWidth,
303 		               settings->DesktopHeight);
304 	}
305 
306 	return TRUE;
307 }
308 
xf_sw_end_paint(rdpContext * context)309 static BOOL xf_sw_end_paint(rdpContext* context)
310 {
311 	int i;
312 	INT32 x, y;
313 	UINT32 w, h;
314 	int ninvalid;
315 	HGDI_RGN cinvalid;
316 	xfContext* xfc = (xfContext*)context;
317 	rdpGdi* gdi = context->gdi;
318 
319 	if (gdi->suppressOutput)
320 		return TRUE;
321 
322 	x = gdi->primary->hdc->hwnd->invalid->x;
323 	y = gdi->primary->hdc->hwnd->invalid->y;
324 	w = gdi->primary->hdc->hwnd->invalid->w;
325 	h = gdi->primary->hdc->hwnd->invalid->h;
326 	ninvalid = gdi->primary->hdc->hwnd->ninvalid;
327 	cinvalid = gdi->primary->hdc->hwnd->cinvalid;
328 
329 	if (!xfc->remote_app)
330 	{
331 		if (!xfc->complex_regions)
332 		{
333 			if (gdi->primary->hdc->hwnd->invalid->null)
334 				return TRUE;
335 
336 			xf_lock_x11(xfc);
337 			XPutImage(xfc->display, xfc->primary, xfc->gc, xfc->image, x, y, x, y, w, h);
338 			xf_draw_screen(xfc, x, y, w, h);
339 			xf_unlock_x11(xfc);
340 		}
341 		else
342 		{
343 			if (gdi->primary->hdc->hwnd->ninvalid < 1)
344 				return TRUE;
345 
346 			xf_lock_x11(xfc);
347 
348 			for (i = 0; i < ninvalid; i++)
349 			{
350 				x = cinvalid[i].x;
351 				y = cinvalid[i].y;
352 				w = cinvalid[i].w;
353 				h = cinvalid[i].h;
354 				XPutImage(xfc->display, xfc->primary, xfc->gc, xfc->image, x, y, x, y, w, h);
355 				xf_draw_screen(xfc, x, y, w, h);
356 			}
357 
358 			XFlush(xfc->display);
359 			xf_unlock_x11(xfc);
360 		}
361 	}
362 	else
363 	{
364 		if (gdi->primary->hdc->hwnd->invalid->null)
365 			return TRUE;
366 
367 		xf_lock_x11(xfc);
368 		xf_rail_paint(xfc, x, y, x + w, y + h);
369 		xf_unlock_x11(xfc);
370 	}
371 
372 	gdi->primary->hdc->hwnd->invalid->null = TRUE;
373 	gdi->primary->hdc->hwnd->ninvalid = 0;
374 	return TRUE;
375 }
376 
xf_sw_desktop_resize(rdpContext * context)377 static BOOL xf_sw_desktop_resize(rdpContext* context)
378 {
379 	rdpGdi* gdi = context->gdi;
380 	xfContext* xfc = (xfContext*)context;
381 	rdpSettings* settings = context->settings;
382 	BOOL ret = FALSE;
383 	xf_lock_x11(xfc);
384 
385 	if (!gdi_resize(gdi, settings->DesktopWidth, settings->DesktopHeight))
386 		goto out;
387 
388 	if (xfc->image)
389 	{
390 		xfc->image->data = NULL;
391 		XDestroyImage(xfc->image);
392 	}
393 
394 	if (!(xfc->image = XCreateImage(xfc->display, xfc->visual, xfc->depth, ZPixmap, 0,
395 	                                (char*)gdi->primary_buffer, gdi->width, gdi->height,
396 	                                xfc->scanline_pad, gdi->stride)))
397 	{
398 		goto out;
399 	}
400 
401 	xfc->image->byte_order = LSBFirst;
402 	xfc->image->bitmap_bit_order = LSBFirst;
403 	ret = xf_desktop_resize(context);
404 out:
405 	xf_unlock_x11(xfc);
406 	return ret;
407 }
408 
xf_hw_end_paint(rdpContext * context)409 static BOOL xf_hw_end_paint(rdpContext* context)
410 {
411 	INT32 x, y;
412 	UINT32 w, h;
413 	xfContext* xfc = (xfContext*)context;
414 
415 	if (xfc->context.gdi->suppressOutput)
416 		return TRUE;
417 
418 	if (!xfc->remote_app)
419 	{
420 		if (!xfc->complex_regions)
421 		{
422 			if (xfc->hdc->hwnd->invalid->null)
423 				return TRUE;
424 
425 			x = xfc->hdc->hwnd->invalid->x;
426 			y = xfc->hdc->hwnd->invalid->y;
427 			w = xfc->hdc->hwnd->invalid->w;
428 			h = xfc->hdc->hwnd->invalid->h;
429 			xf_lock_x11(xfc);
430 			xf_draw_screen(xfc, x, y, w, h);
431 			xf_unlock_x11(xfc);
432 		}
433 		else
434 		{
435 			int i;
436 			int ninvalid;
437 			HGDI_RGN cinvalid;
438 
439 			if (xfc->hdc->hwnd->ninvalid < 1)
440 				return TRUE;
441 
442 			ninvalid = xfc->hdc->hwnd->ninvalid;
443 			cinvalid = xfc->hdc->hwnd->cinvalid;
444 			xf_lock_x11(xfc);
445 
446 			for (i = 0; i < ninvalid; i++)
447 			{
448 				x = cinvalid[i].x;
449 				y = cinvalid[i].y;
450 				w = cinvalid[i].w;
451 				h = cinvalid[i].h;
452 				xf_draw_screen(xfc, x, y, w, h);
453 			}
454 
455 			XFlush(xfc->display);
456 			xf_unlock_x11(xfc);
457 		}
458 	}
459 	else
460 	{
461 		if (xfc->hdc->hwnd->invalid->null)
462 			return TRUE;
463 
464 		x = xfc->hdc->hwnd->invalid->x;
465 		y = xfc->hdc->hwnd->invalid->y;
466 		w = xfc->hdc->hwnd->invalid->w;
467 		h = xfc->hdc->hwnd->invalid->h;
468 		xf_lock_x11(xfc);
469 		xf_rail_paint(xfc, x, y, x + w, y + h);
470 		xf_unlock_x11(xfc);
471 	}
472 
473 	xfc->hdc->hwnd->invalid->null = TRUE;
474 	xfc->hdc->hwnd->ninvalid = 0;
475 	return TRUE;
476 }
477 
xf_hw_desktop_resize(rdpContext * context)478 static BOOL xf_hw_desktop_resize(rdpContext* context)
479 {
480 	rdpGdi* gdi = context->gdi;
481 	xfContext* xfc = (xfContext*)context;
482 	rdpSettings* settings = context->settings;
483 	BOOL ret = FALSE;
484 	xf_lock_x11(xfc);
485 
486 	if (!gdi_resize(gdi, settings->DesktopWidth, settings->DesktopHeight))
487 		goto out;
488 
489 	ret = xf_desktop_resize(context);
490 out:
491 	xf_unlock_x11(xfc);
492 	return ret;
493 }
494 
xf_process_x_events(freerdp * instance)495 static BOOL xf_process_x_events(freerdp* instance)
496 {
497 	BOOL status;
498 	XEvent xevent;
499 	int pending_status;
500 	xfContext* xfc = (xfContext*)instance->context;
501 	status = TRUE;
502 	pending_status = TRUE;
503 
504 	while (pending_status)
505 	{
506 		xf_lock_x11(xfc);
507 		pending_status = XPending(xfc->display);
508 
509 		if (pending_status)
510 		{
511 			ZeroMemory(&xevent, sizeof(xevent));
512 			XNextEvent(xfc->display, &xevent);
513 			status = xf_event_process(instance, &xevent);
514 		}
515 		xf_unlock_x11(xfc);
516 		if (!status)
517 			break;
518 	}
519 
520 	return status;
521 }
522 
xf_window_get_title(rdpSettings * settings)523 static char* xf_window_get_title(rdpSettings* settings)
524 {
525 	BOOL port;
526 	char* windowTitle;
527 	size_t size;
528 	char* name;
529 	const char* prefix = "FreeRDP:";
530 
531 	if (!settings)
532 		return NULL;
533 
534 	name = settings->ServerHostname;
535 
536 	if (settings->WindowTitle)
537 		return _strdup(settings->WindowTitle);
538 
539 	port = (settings->ServerPort != 3389);
540 	/* Just assume a window title is never longer than a filename... */
541 	size = strnlen(name, MAX_PATH) + 16;
542 	windowTitle = calloc(size, sizeof(char));
543 
544 	if (!windowTitle)
545 		return NULL;
546 
547 	if (!port)
548 		sprintf_s(windowTitle, size, "%s %s", prefix, name);
549 	else
550 		sprintf_s(windowTitle, size, "%s %s:%i", prefix, name, settings->ServerPort);
551 
552 	return windowTitle;
553 }
554 
xf_create_window(xfContext * xfc)555 BOOL xf_create_window(xfContext* xfc)
556 {
557 	XGCValues gcv;
558 	XEvent xevent;
559 	int width, height;
560 	char* windowTitle;
561 	rdpGdi* gdi;
562 	rdpSettings* settings;
563 	settings = xfc->context.settings;
564 	gdi = xfc->context.gdi;
565 	ZeroMemory(&xevent, sizeof(xevent));
566 	width = settings->DesktopWidth;
567 	height = settings->DesktopHeight;
568 
569 	if (!xfc->hdc)
570 		if (!(xfc->hdc = gdi_CreateDC(gdi->dstFormat)))
571 			return FALSE;
572 
573 	if (!xfc->remote_app)
574 	{
575 		xfc->attribs.background_pixel = BlackPixelOfScreen(xfc->screen);
576 		xfc->attribs.border_pixel = WhitePixelOfScreen(xfc->screen);
577 		xfc->attribs.backing_store = xfc->primary ? NotUseful : Always;
578 		xfc->attribs.override_redirect = False;
579 		xfc->attribs.colormap = xfc->colormap;
580 		xfc->attribs.bit_gravity = NorthWestGravity;
581 		xfc->attribs.win_gravity = NorthWestGravity;
582 #ifdef WITH_XRENDER
583 		xfc->offset_x = 0;
584 		xfc->offset_y = 0;
585 #endif
586 		windowTitle = xf_window_get_title(settings);
587 
588 		if (!windowTitle)
589 			return FALSE;
590 
591 #ifdef WITH_XRENDER
592 
593 		if (settings->SmartSizing && !xfc->fullscreen)
594 		{
595 			if (settings->SmartSizingWidth)
596 				width = settings->SmartSizingWidth;
597 
598 			if (settings->SmartSizingHeight)
599 				height = settings->SmartSizingHeight;
600 
601 			xfc->scaledWidth = width;
602 			xfc->scaledHeight = height;
603 		}
604 
605 #endif
606 		xfc->window = xf_CreateDesktopWindow(xfc, windowTitle, width, height);
607 		free(windowTitle);
608 
609 		if (xfc->fullscreen)
610 			xf_SetWindowFullscreen(xfc, xfc->window, xfc->fullscreen);
611 
612 		xfc->unobscured = (xevent.xvisibility.state == VisibilityUnobscured);
613 		XSetWMProtocols(xfc->display, xfc->window->handle, &(xfc->WM_DELETE_WINDOW), 1);
614 		xfc->drawable = xfc->window->handle;
615 	}
616 	else
617 	{
618 		xfc->drawable = xf_CreateDummyWindow(xfc);
619 	}
620 
621 	ZeroMemory(&gcv, sizeof(gcv));
622 
623 	if (xfc->modifierMap)
624 		XFreeModifiermap(xfc->modifierMap);
625 
626 	xfc->modifierMap = XGetModifierMapping(xfc->display);
627 
628 	if (!xfc->gc)
629 		xfc->gc = XCreateGC(xfc->display, xfc->drawable, GCGraphicsExposures, &gcv);
630 
631 	if (!xfc->primary)
632 		xfc->primary = XCreatePixmap(xfc->display, xfc->drawable, settings->DesktopWidth,
633 		                             settings->DesktopHeight, xfc->depth);
634 
635 	xfc->drawing = xfc->primary;
636 
637 	if (!xfc->bitmap_mono)
638 		xfc->bitmap_mono = XCreatePixmap(xfc->display, xfc->drawable, 8, 8, 1);
639 
640 	if (!xfc->gc_mono)
641 		xfc->gc_mono = XCreateGC(xfc->display, xfc->bitmap_mono, GCGraphicsExposures, &gcv);
642 
643 	XSetFunction(xfc->display, xfc->gc, GXcopy);
644 	XSetFillStyle(xfc->display, xfc->gc, FillSolid);
645 	XSetForeground(xfc->display, xfc->gc, BlackPixelOfScreen(xfc->screen));
646 	XFillRectangle(xfc->display, xfc->primary, xfc->gc, 0, 0, settings->DesktopWidth,
647 	               settings->DesktopHeight);
648 	XFlush(xfc->display);
649 
650 	if (!xfc->image)
651 	{
652 		rdpGdi* gdi = xfc->context.gdi;
653 		xfc->image = XCreateImage(xfc->display, xfc->visual, xfc->depth, ZPixmap, 0,
654 		                          (char*)gdi->primary_buffer, settings->DesktopWidth,
655 		                          settings->DesktopHeight, xfc->scanline_pad, gdi->stride);
656 		xfc->image->byte_order = LSBFirst;
657 		xfc->image->bitmap_bit_order = LSBFirst;
658 	}
659 
660 	return TRUE;
661 }
662 
xf_window_free(xfContext * xfc)663 static void xf_window_free(xfContext* xfc)
664 {
665 	if (xfc->window)
666 	{
667 		xf_DestroyDesktopWindow(xfc, xfc->window);
668 		xfc->window = NULL;
669 	}
670 
671 	if (xfc->hdc)
672 	{
673 		gdi_DeleteDC(xfc->hdc);
674 		xfc->hdc = NULL;
675 	}
676 
677 #if defined(CHANNEL_TSMF_CLIENT)
678 	if (xfc->xv_context)
679 	{
680 		xf_tsmf_uninit(xfc, NULL);
681 		xfc->xv_context = NULL;
682 	}
683 #endif
684 
685 	if (xfc->image)
686 	{
687 		xfc->image->data = NULL;
688 		XDestroyImage(xfc->image);
689 		xfc->image = NULL;
690 	}
691 
692 	if (xfc->bitmap_mono)
693 	{
694 		XFreePixmap(xfc->display, xfc->bitmap_mono);
695 		xfc->bitmap_mono = 0;
696 	}
697 
698 	if (xfc->gc_mono)
699 	{
700 		XFreeGC(xfc->display, xfc->gc_mono);
701 		xfc->gc_mono = 0;
702 	}
703 
704 	if (xfc->primary)
705 	{
706 		XFreePixmap(xfc->display, xfc->primary);
707 		xfc->primary = 0;
708 	}
709 
710 	if (xfc->gc)
711 	{
712 		XFreeGC(xfc->display, xfc->gc);
713 		xfc->gc = 0;
714 	}
715 
716 	if (xfc->modifierMap)
717 	{
718 		XFreeModifiermap(xfc->modifierMap);
719 		xfc->modifierMap = NULL;
720 	}
721 }
722 
xf_toggle_fullscreen(xfContext * xfc)723 void xf_toggle_fullscreen(xfContext* xfc)
724 {
725 	WindowStateChangeEventArgs e;
726 	rdpContext* context = (rdpContext*)xfc;
727 	rdpSettings* settings = context->settings;
728 
729 	/*
730 	  when debugging, ungrab keyboard when toggling fullscreen
731 	  to allow keyboard usage on the debugger
732 	*/
733 	if (xfc->debug)
734 	{
735 		XUngrabKeyboard(xfc->display, CurrentTime);
736 	}
737 
738 	xfc->fullscreen = (xfc->fullscreen) ? FALSE : TRUE;
739 	xfc->decorations = (xfc->fullscreen) ? FALSE : settings->Decorations;
740 	xf_SetWindowFullscreen(xfc, xfc->window, xfc->fullscreen);
741 	EventArgsInit(&e, "xfreerdp");
742 	e.state = xfc->fullscreen ? FREERDP_WINDOW_STATE_FULLSCREEN : 0;
743 	PubSub_OnWindowStateChange(context->pubSub, context, &e);
744 }
745 
xf_toggle_control(xfContext * xfc)746 BOOL xf_toggle_control(xfContext* xfc)
747 {
748 	EncomspClientContext* encomsp;
749 	ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu;
750 	encomsp = xfc->encomsp;
751 
752 	if (!encomsp)
753 		return FALSE;
754 
755 	pdu.ParticipantId = 0;
756 	pdu.Flags = ENCOMSP_REQUEST_VIEW;
757 
758 	if (!xfc->controlToggle)
759 		pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
760 
761 	encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
762 	xfc->controlToggle = !xfc->controlToggle;
763 	return TRUE;
764 }
765 
766 /**
767  * Function description
768  *
769  * @return 0 on success, otherwise a Win32 error code
770  */
771 static UINT
xf_encomsp_participant_created(EncomspClientContext * context,const ENCOMSP_PARTICIPANT_CREATED_PDU * participantCreated)772 xf_encomsp_participant_created(EncomspClientContext* context,
773                                const ENCOMSP_PARTICIPANT_CREATED_PDU* participantCreated)
774 {
775 	xfContext* xfc;
776 	rdpSettings* settings;
777 	BOOL request;
778 
779 	if (!context || !context->custom || !participantCreated)
780 		return ERROR_INVALID_PARAMETER;
781 
782 	xfc = context->custom;
783 	settings = xfc->context.settings;
784 
785 	if (!settings)
786 		return ERROR_INVALID_PARAMETER;
787 
788 	request = freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceRequestControl);
789 	if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
790 	    !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
791 		xf_toggle_control(xfc);
792 
793 	return CHANNEL_RC_OK;
794 }
795 
xf_encomsp_init(xfContext * xfc,EncomspClientContext * encomsp)796 void xf_encomsp_init(xfContext* xfc, EncomspClientContext* encomsp)
797 {
798 	xfc->encomsp = encomsp;
799 	encomsp->custom = (void*)xfc;
800 	encomsp->ParticipantCreated = xf_encomsp_participant_created;
801 }
802 
xf_encomsp_uninit(xfContext * xfc,EncomspClientContext * encomsp)803 void xf_encomsp_uninit(xfContext* xfc, EncomspClientContext* encomsp)
804 {
805 	WINPR_UNUSED(encomsp);
806 	xfc->encomsp = NULL;
807 }
808 
xf_lock_x11_(xfContext * xfc,const char * fkt)809 void xf_lock_x11_(xfContext* xfc, const char* fkt)
810 {
811 
812 	if (!xfc->UseXThreads)
813 		WaitForSingleObject(xfc->mutex, INFINITE);
814 	else
815 		XLockDisplay(xfc->display);
816 
817 	if (xfc->locked)
818 		WLog_WARN(TAG, "%s:\t[%" PRIu32 "] recursive lock from %s", __FUNCTION__, xfc->locked, fkt);
819 	xfc->locked++;
820 	WLog_VRB(TAG, "%s:\t[%" PRIu32 "] from %s", __FUNCTION__, xfc->locked, fkt);
821 }
822 
xf_unlock_x11_(xfContext * xfc,const char * fkt)823 void xf_unlock_x11_(xfContext* xfc, const char* fkt)
824 {
825 	if (xfc->locked == 0)
826 		WLog_WARN(TAG, "X11: trying to unlock although not locked!");
827 
828 	WLog_VRB(TAG, "%s:\t[%" PRIu32 "] from %s", __FUNCTION__, xfc->locked - 1, fkt);
829 	if (!xfc->UseXThreads)
830 		ReleaseMutex(xfc->mutex);
831 	else
832 		XUnlockDisplay(xfc->display);
833 	xfc->locked--;
834 }
835 
xf_get_pixmap_info(xfContext * xfc)836 static BOOL xf_get_pixmap_info(xfContext* xfc)
837 {
838 	int i;
839 	int vi_count;
840 	int pf_count;
841 	XVisualInfo* vi;
842 	XVisualInfo* vis;
843 	XVisualInfo tpl;
844 	XPixmapFormatValues* pf;
845 	XPixmapFormatValues* pfs;
846 	XWindowAttributes window_attributes;
847 	assert(xfc->display);
848 	pfs = XListPixmapFormats(xfc->display, &pf_count);
849 
850 	if (!pfs)
851 	{
852 		WLog_ERR(TAG, "XListPixmapFormats failed");
853 		return 1;
854 	}
855 
856 	for (i = 0; i < pf_count; i++)
857 	{
858 		pf = pfs + i;
859 
860 		if (pf->depth == xfc->depth)
861 		{
862 			xfc->scanline_pad = pf->scanline_pad;
863 			break;
864 		}
865 	}
866 
867 	XFree(pfs);
868 	ZeroMemory(&tpl, sizeof(tpl));
869 	tpl.class = TrueColor;
870 	tpl.screen = xfc->screen_number;
871 
872 	if (XGetWindowAttributes(xfc->display, RootWindowOfScreen(xfc->screen), &window_attributes) ==
873 	    0)
874 	{
875 		WLog_ERR(TAG, "XGetWindowAttributes failed");
876 		return FALSE;
877 	}
878 
879 	vis = XGetVisualInfo(xfc->display, VisualClassMask | VisualScreenMask, &tpl, &vi_count);
880 
881 	if (!vis)
882 	{
883 		WLog_ERR(TAG, "XGetVisualInfo failed");
884 		return FALSE;
885 	}
886 
887 	vi = vis;
888 
889 	for (i = 0; i < vi_count; i++)
890 	{
891 		vi = vis + i;
892 
893 		if (vi->visual == window_attributes.visual)
894 		{
895 			xfc->visual = vi->visual;
896 			break;
897 		}
898 	}
899 
900 	if (xfc->visual)
901 	{
902 		/*
903 		 * Detect if the server visual has an inverted colormap
904 		 * (BGR vs RGB, or red being the least significant byte)
905 		 */
906 		if (vi->red_mask & 0xFF)
907 		{
908 			xfc->invert = FALSE;
909 		}
910 	}
911 
912 	XFree(vis);
913 
914 	if ((xfc->visual == NULL) || (xfc->scanline_pad == 0))
915 	{
916 		return FALSE;
917 	}
918 
919 	return TRUE;
920 }
921 
xf_error_handler(Display * d,XErrorEvent * ev)922 static int xf_error_handler(Display* d, XErrorEvent* ev)
923 {
924 	char buf[256];
925 	int do_abort = TRUE;
926 	XGetErrorText(d, ev->error_code, buf, sizeof(buf));
927 	WLog_ERR(TAG, "%s", buf);
928 
929 	if (do_abort)
930 		abort();
931 
932 	_def_error_handler(d, ev);
933 	return FALSE;
934 }
935 
_xf_error_handler(Display * d,XErrorEvent * ev)936 static int _xf_error_handler(Display* d, XErrorEvent* ev)
937 {
938 	/*
939 	 * ungrab the keyboard, in case a debugger is running in
940 	 * another window. This make xf_error_handler() a potential
941 	 * debugger breakpoint.
942 	 */
943 
944 	XUngrabKeyboard(d, CurrentTime);
945 	return xf_error_handler(d, ev);
946 }
947 
xf_play_sound(rdpContext * context,const PLAY_SOUND_UPDATE * play_sound)948 static BOOL xf_play_sound(rdpContext* context, const PLAY_SOUND_UPDATE* play_sound)
949 {
950 	xfContext* xfc = (xfContext*)context;
951 	WINPR_UNUSED(play_sound);
952 	XkbBell(xfc->display, None, 100, 0);
953 	return TRUE;
954 }
955 
xf_check_extensions(xfContext * context)956 static void xf_check_extensions(xfContext* context)
957 {
958 	int xkb_opcode, xkb_event, xkb_error;
959 	int xkb_major = XkbMajorVersion;
960 	int xkb_minor = XkbMinorVersion;
961 
962 	if (XkbLibraryVersion(&xkb_major, &xkb_minor) &&
963 	    XkbQueryExtension(context->display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major,
964 	                      &xkb_minor))
965 	{
966 		context->xkbAvailable = TRUE;
967 	}
968 
969 #ifdef WITH_XRENDER
970 	{
971 		int xrender_event_base;
972 		int xrender_error_base;
973 
974 		if (XRenderQueryExtension(context->display, &xrender_event_base, &xrender_error_base))
975 		{
976 			context->xrenderAvailable = TRUE;
977 		}
978 	}
979 #endif
980 }
981 
982 #ifdef WITH_XI
983 /* Input device which does NOT have the correct mapping. We must disregard */
984 /* this device when trying to find the input device which is the pointer.  */
985 static const char TEST_PTR_STR[] = "Virtual core XTEST pointer";
986 static const size_t TEST_PTR_LEN = sizeof(TEST_PTR_STR) / sizeof(char);
987 #endif /* WITH_XI */
988 
xf_get_x11_button_map(xfContext * xfc,unsigned char * x11_map)989 static void xf_get_x11_button_map(xfContext* xfc, unsigned char* x11_map)
990 {
991 #ifdef WITH_XI
992 	int opcode, event, error;
993 	XDevice* ptr_dev;
994 	XExtensionVersion* version;
995 	XDeviceInfo* devices1;
996 	XIDeviceInfo* devices2;
997 	int i, num_devices;
998 
999 	if (XQueryExtension(xfc->display, "XInputExtension", &opcode, &event, &error))
1000 	{
1001 		WLog_DBG(TAG, "Searching for XInput pointer device");
1002 		ptr_dev = NULL;
1003 		/* loop through every device, looking for a pointer */
1004 		version = XGetExtensionVersion(xfc->display, INAME);
1005 
1006 		if (version->major_version >= 2)
1007 		{
1008 			/* XID of pointer device using XInput version 2 */
1009 			devices2 = XIQueryDevice(xfc->display, XIAllDevices, &num_devices);
1010 
1011 			if (devices2)
1012 			{
1013 				for (i = 0; i < num_devices; ++i)
1014 				{
1015 					if ((devices2[i].use == XISlavePointer) &&
1016 					    (strncmp(devices2[i].name, TEST_PTR_STR, TEST_PTR_LEN) != 0))
1017 					{
1018 						ptr_dev = XOpenDevice(xfc->display, devices2[i].deviceid);
1019 						if (ptr_dev)
1020 							break;
1021 					}
1022 				}
1023 
1024 				XIFreeDeviceInfo(devices2);
1025 			}
1026 		}
1027 		else
1028 		{
1029 			/* XID of pointer device using XInput version 1 */
1030 			devices1 = XListInputDevices(xfc->display, &num_devices);
1031 
1032 			if (devices1)
1033 			{
1034 				for (i = 0; i < num_devices; ++i)
1035 				{
1036 					if ((devices1[i].use == IsXExtensionPointer) &&
1037 					    (strncmp(devices1[i].name, TEST_PTR_STR, TEST_PTR_LEN) != 0))
1038 					{
1039 						ptr_dev = XOpenDevice(xfc->display, devices1[i].id);
1040 						if (ptr_dev)
1041 							break;
1042 					}
1043 				}
1044 
1045 				XFreeDeviceList(devices1);
1046 			}
1047 		}
1048 
1049 		XFree(version);
1050 
1051 		/* get button mapping from input extension if there is a pointer device; */
1052 		/* otherwise leave unchanged.                                            */
1053 		if (ptr_dev)
1054 		{
1055 			WLog_DBG(TAG, "Pointer device: %d", ptr_dev->device_id);
1056 			XGetDeviceButtonMapping(xfc->display, ptr_dev, x11_map, NUM_BUTTONS_MAPPED);
1057 			XCloseDevice(xfc->display, ptr_dev);
1058 		}
1059 		else
1060 		{
1061 			WLog_DBG(TAG, "No pointer device found!");
1062 		}
1063 	}
1064 	else
1065 #endif /* WITH_XI */
1066 	{
1067 		WLog_DBG(TAG, "Get global pointer mapping (no XInput)");
1068 		XGetPointerMapping(xfc->display, x11_map, NUM_BUTTONS_MAPPED);
1069 	}
1070 }
1071 
1072 /* Assignment of physical (not logical) mouse buttons to wire flags. */
1073 /* Notice that the middle button is 2 in X11, but 3 in RDP.          */
1074 static const button_map xf_button_flags[NUM_BUTTONS_MAPPED] = {
1075 	{ Button1, PTR_FLAGS_BUTTON1 },
1076 	{ Button2, PTR_FLAGS_BUTTON3 },
1077 	{ Button3, PTR_FLAGS_BUTTON2 },
1078 	{ Button4, PTR_FLAGS_WHEEL | 0x78 },
1079 	/* Negative value is 9bit twos complement */
1080 	{ Button5, PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | (0x100 - 0x78) },
1081 	{ 6, PTR_FLAGS_HWHEEL | PTR_FLAGS_WHEEL_NEGATIVE | (0x100 - 0x78) },
1082 	{ 7, PTR_FLAGS_HWHEEL | 0x78 },
1083 	{ 8, PTR_XFLAGS_BUTTON1 },
1084 	{ 9, PTR_XFLAGS_BUTTON2 },
1085 	{ 97, PTR_XFLAGS_BUTTON1 },
1086 	{ 112, PTR_XFLAGS_BUTTON2 }
1087 };
1088 
get_flags_for_button(int button)1089 static UINT16 get_flags_for_button(int button)
1090 {
1091 	size_t x;
1092 
1093 	for (x = 0; x < ARRAYSIZE(xf_button_flags); x++)
1094 	{
1095 		const button_map* map = &xf_button_flags[x];
1096 
1097 		if (map->button == button)
1098 			return map->flags;
1099 	}
1100 
1101 	return 0;
1102 }
1103 
xf_button_map_init(xfContext * xfc)1104 static void xf_button_map_init(xfContext* xfc)
1105 {
1106 	size_t pos = 0;
1107 	/* loop counter for array initialization */
1108 	size_t physical;
1109 	/* logical mouse button which is used for each physical mouse  */
1110 	/* button (indexed from zero). This is the default map.        */
1111 	unsigned char x11_map[112] = { 0 };
1112 	x11_map[0] = Button1;
1113 	x11_map[1] = Button2;
1114 	x11_map[2] = Button3;
1115 	x11_map[3] = Button4;
1116 	x11_map[4] = Button5;
1117 	x11_map[5] = 6;
1118 	x11_map[6] = 7;
1119 	x11_map[7] = 8;
1120 	x11_map[8] = 9;
1121 	x11_map[96] = 97;
1122 	x11_map[111] = 112;
1123 
1124 	/* query system for actual remapping */
1125 	if (xfc->context.settings->UnmapButtons)
1126 	{
1127 		xf_get_x11_button_map(xfc, x11_map);
1128 	}
1129 
1130 	/* iterate over all (mapped) physical buttons; for each of them */
1131 	/* find the logical button in X11, and assign to this the       */
1132 	/* appropriate value to send over the RDP wire.                 */
1133 	for (physical = 0; physical < ARRAYSIZE(x11_map); ++physical)
1134 	{
1135 		const unsigned char logical = x11_map[physical];
1136 		const UINT16 flags = get_flags_for_button(logical);
1137 
1138 		if ((logical != 0) && (flags != 0))
1139 		{
1140 			if (pos >= NUM_BUTTONS_MAPPED)
1141 			{
1142 				WLog_ERR(TAG, "Failed to map mouse button to RDP button, no space");
1143 			}
1144 			else
1145 			{
1146 				button_map* map = &xfc->button_map[pos++];
1147 				map->button = logical;
1148 				map->flags = get_flags_for_button(physical + Button1);
1149 			}
1150 		}
1151 	}
1152 }
1153 
1154 /**
1155  * Callback given to freerdp_connect() to process the pre-connect operations.
1156  * It will fill the rdp_freerdp structure (instance) with the appropriate options to use for the
1157  * connection.
1158  *
1159  * @param instance - pointer to the rdp_freerdp structure that contains the connection's parameters,
1160  * and will be filled with the appropriate informations.
1161  *
1162  * @return TRUE if successful. FALSE otherwise.
1163  * Can exit with error code XF_EXIT_PARSE_ARGUMENTS if there is an error in the parameters.
1164  */
xf_pre_connect(freerdp * instance)1165 static BOOL xf_pre_connect(freerdp* instance)
1166 {
1167 	rdpChannels* channels;
1168 	rdpSettings* settings;
1169 	rdpContext* context = instance->context;
1170 	xfContext* xfc = (xfContext*)instance->context;
1171 	UINT32 maxWidth = 0;
1172 	UINT32 maxHeight = 0;
1173 	settings = instance->settings;
1174 	channels = context->channels;
1175 	settings->OsMajorType = OSMAJORTYPE_UNIX;
1176 	settings->OsMinorType = OSMINORTYPE_NATIVE_XSERVER;
1177 	PubSub_SubscribeChannelConnected(instance->context->pubSub, xf_OnChannelConnectedEventHandler);
1178 	PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
1179 	                                    xf_OnChannelDisconnectedEventHandler);
1180 
1181 	if (!freerdp_client_load_addins(channels, instance->settings))
1182 		return FALSE;
1183 
1184 	if (!settings->Username && !settings->CredentialsFromStdin && !settings->SmartcardLogon)
1185 	{
1186 		int rc;
1187 		char login_name[MAX_PATH] = { 0 };
1188 
1189 #ifdef HAVE_GETLOGIN_R
1190 		rc = getlogin_r(login_name, sizeof(login_name));
1191 #else
1192 		strncpy(login_name, getlogin(), sizeof(login_name));
1193 		rc = 0;
1194 #endif
1195 		if (rc == 0)
1196 		{
1197 			settings->Username = _strdup(login_name);
1198 
1199 			if (!settings->Username)
1200 				return FALSE;
1201 
1202 			WLog_INFO(TAG, "No user name set. - Using login name: %s", settings->Username);
1203 		}
1204 	}
1205 
1206 	if (settings->AuthenticationOnly)
1207 	{
1208 		/* Check +auth-only has a username and password. */
1209 		if (!settings->Password)
1210 		{
1211 			WLog_INFO(TAG, "auth-only, but no password set. Please provide one.");
1212 			return FALSE;
1213 		}
1214 
1215 		WLog_INFO(TAG, "Authentication only. Don't connect to X.");
1216 	}
1217 
1218 	if (!xf_keyboard_init(xfc))
1219 		return FALSE;
1220 
1221 	xf_detect_monitors(xfc, &maxWidth, &maxHeight);
1222 
1223 	if (maxWidth && maxHeight)
1224 	{
1225 		settings->DesktopWidth = maxWidth;
1226 		settings->DesktopHeight = maxHeight;
1227 	}
1228 
1229 #ifdef WITH_XRENDER
1230 
1231 	/**
1232 	 * If /f is specified in combination with /smart-sizing:widthxheight then
1233 	 * we run the session in the /smart-sizing dimensions scaled to full screen
1234 	 */
1235 	if (settings->Fullscreen && settings->SmartSizing && settings->SmartSizingWidth &&
1236 	    settings->SmartSizingHeight)
1237 	{
1238 		settings->DesktopWidth = settings->SmartSizingWidth;
1239 		settings->DesktopHeight = settings->SmartSizingHeight;
1240 	}
1241 
1242 #endif
1243 	xfc->fullscreen = settings->Fullscreen;
1244 	xfc->decorations = settings->Decorations;
1245 	xfc->grab_keyboard = settings->GrabKeyboard;
1246 	xfc->fullscreen_toggle = settings->ToggleFullscreen;
1247 	xf_button_map_init(xfc);
1248 	return TRUE;
1249 }
1250 
1251 /**
1252  * Callback given to freerdp_connect() to perform post-connection operations.
1253  * It will be called only if the connection was initialized properly, and will continue the
1254  * initialization based on the newly created connection.
1255  */
xf_post_connect(freerdp * instance)1256 static BOOL xf_post_connect(freerdp* instance)
1257 {
1258 	rdpUpdate* update;
1259 	rdpContext* context;
1260 	rdpSettings* settings;
1261 	ResizeWindowEventArgs e;
1262 	xfContext* xfc = (xfContext*)instance->context;
1263 	context = instance->context;
1264 	settings = instance->settings;
1265 	update = context->update;
1266 
1267 	if (!gdi_init(instance, xf_get_local_color_format(xfc, TRUE)))
1268 		return FALSE;
1269 
1270 	if (!xf_register_pointer(context->graphics))
1271 		return FALSE;
1272 
1273 	if (!settings->SoftwareGdi)
1274 	{
1275 		if (!xf_register_graphics(context->graphics))
1276 		{
1277 			WLog_ERR(TAG, "failed to register graphics");
1278 			return FALSE;
1279 		}
1280 
1281 		xf_gdi_register_update_callbacks(update);
1282 		brush_cache_register_callbacks(instance->update);
1283 		glyph_cache_register_callbacks(instance->update);
1284 		bitmap_cache_register_callbacks(instance->update);
1285 		offscreen_cache_register_callbacks(instance->update);
1286 		palette_cache_register_callbacks(instance->update);
1287 	}
1288 
1289 #ifdef WITH_XRENDER
1290 	xfc->scaledWidth = settings->DesktopWidth;
1291 	xfc->scaledHeight = settings->DesktopHeight;
1292 	xfc->offset_x = 0;
1293 	xfc->offset_y = 0;
1294 #endif
1295 
1296 	if (!xfc->xrenderAvailable)
1297 	{
1298 		if (settings->SmartSizing)
1299 		{
1300 			WLog_ERR(TAG, "XRender not available: disabling smart-sizing");
1301 			settings->SmartSizing = FALSE;
1302 		}
1303 
1304 		if (settings->MultiTouchGestures)
1305 		{
1306 			WLog_ERR(TAG, "XRender not available: disabling local multi-touch gestures");
1307 			settings->MultiTouchGestures = FALSE;
1308 		}
1309 	}
1310 
1311 	if (settings->RemoteApplicationMode)
1312 		xfc->remote_app = TRUE;
1313 
1314 	if (!xf_create_window(xfc))
1315 	{
1316 		WLog_ERR(TAG, "xf_create_window failed");
1317 		return FALSE;
1318 	}
1319 
1320 	if (settings->SoftwareGdi)
1321 	{
1322 		update->EndPaint = xf_sw_end_paint;
1323 		update->DesktopResize = xf_sw_desktop_resize;
1324 	}
1325 	else
1326 	{
1327 		update->EndPaint = xf_hw_end_paint;
1328 		update->DesktopResize = xf_hw_desktop_resize;
1329 	}
1330 
1331 	update->PlaySound = xf_play_sound;
1332 	update->SetKeyboardIndicators = xf_keyboard_set_indicators;
1333 	update->SetKeyboardImeStatus = xf_keyboard_set_ime_status;
1334 
1335 	if (!(xfc->clipboard = xf_clipboard_new(xfc)))
1336 		return FALSE;
1337 
1338 	if (!(xfc->xfDisp = xf_disp_new(xfc)))
1339 	{
1340 		xf_clipboard_free(xfc->clipboard);
1341 		return FALSE;
1342 	}
1343 
1344 	EventArgsInit(&e, "xfreerdp");
1345 	e.width = settings->DesktopWidth;
1346 	e.height = settings->DesktopHeight;
1347 	PubSub_OnResizeWindow(context->pubSub, xfc, &e);
1348 	return TRUE;
1349 }
1350 
xf_post_disconnect(freerdp * instance)1351 static void xf_post_disconnect(freerdp* instance)
1352 {
1353 	xfContext* xfc;
1354 	rdpContext* context;
1355 
1356 	if (!instance || !instance->context)
1357 		return;
1358 
1359 	context = instance->context;
1360 	xfc = (xfContext*)context;
1361 	PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
1362 	                                   xf_OnChannelConnectedEventHandler);
1363 	PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
1364 	                                      xf_OnChannelDisconnectedEventHandler);
1365 	gdi_free(instance);
1366 
1367 	if (xfc->clipboard)
1368 	{
1369 		xf_clipboard_free(xfc->clipboard);
1370 		xfc->clipboard = NULL;
1371 	}
1372 
1373 	if (xfc->xfDisp)
1374 	{
1375 		xf_disp_free(xfc->xfDisp);
1376 		xfc->xfDisp = NULL;
1377 	}
1378 
1379 	if ((xfc->window != NULL) && (xfc->drawable == xfc->window->handle))
1380 		xfc->drawable = 0;
1381 	else
1382 		xf_DestroyDummyWindow(xfc, xfc->drawable);
1383 
1384 	xf_window_free(xfc);
1385 	xf_keyboard_free(xfc);
1386 }
1387 
xf_logon_error_info(freerdp * instance,UINT32 data,UINT32 type)1388 static int xf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
1389 {
1390 	xfContext* xfc = (xfContext*)instance->context;
1391 	const char* str_data = freerdp_get_logon_error_info_data(data);
1392 	const char* str_type = freerdp_get_logon_error_info_type(type);
1393 	WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
1394 	xf_rail_disable_remoteapp_mode(xfc);
1395 	return 1;
1396 }
1397 
xf_input_thread(LPVOID arg)1398 static DWORD WINAPI xf_input_thread(LPVOID arg)
1399 {
1400 	BOOL running = TRUE;
1401 	DWORD status;
1402 	DWORD nCount;
1403 	HANDLE events[3];
1404 	wMessage msg;
1405 	wMessageQueue* queue;
1406 	freerdp* instance = (freerdp*)arg;
1407 	xfContext* xfc = (xfContext*)instance->context;
1408 	queue = freerdp_get_message_queue(instance, FREERDP_INPUT_MESSAGE_QUEUE);
1409 	nCount = 0;
1410 	events[nCount++] = MessageQueue_Event(queue);
1411 	events[nCount++] = xfc->x11event;
1412 	events[nCount++] = instance->context->abortEvent;
1413 
1414 	while (running)
1415 	{
1416 		status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
1417 
1418 		switch (status)
1419 		{
1420 			case WAIT_OBJECT_0:
1421 			case WAIT_OBJECT_0 + 1:
1422 			case WAIT_OBJECT_0 + 2:
1423 				if (WaitForSingleObject(events[0], 0) == WAIT_OBJECT_0)
1424 				{
1425 					if (MessageQueue_Peek(queue, &msg, FALSE))
1426 					{
1427 						if (msg.id == WMQ_QUIT)
1428 							running = FALSE;
1429 					}
1430 				}
1431 
1432 				if (WaitForSingleObject(events[1], 0) == WAIT_OBJECT_0)
1433 				{
1434 					if (!xf_process_x_events(xfc->context.instance))
1435 					{
1436 						running = FALSE;
1437 						break;
1438 					}
1439 				}
1440 
1441 				if (WaitForSingleObject(events[2], 0) == WAIT_OBJECT_0)
1442 					running = FALSE;
1443 
1444 				break;
1445 
1446 			default:
1447 				running = FALSE;
1448 				break;
1449 		}
1450 	}
1451 
1452 	MessageQueue_PostQuit(queue, 0);
1453 	freerdp_abort_connect(xfc->context.instance);
1454 	ExitThread(0);
1455 	return 0;
1456 }
1457 
handle_window_events(freerdp * instance)1458 static BOOL handle_window_events(freerdp* instance)
1459 {
1460 	rdpSettings* settings;
1461 
1462 	if (!instance || !instance->settings)
1463 		return FALSE;
1464 
1465 	settings = instance->settings;
1466 
1467 	if (!settings->AsyncInput)
1468 	{
1469 		if (!xf_process_x_events(instance))
1470 		{
1471 			WLog_INFO(TAG, "Closed from X11");
1472 			return FALSE;
1473 		}
1474 	}
1475 
1476 	return TRUE;
1477 }
1478 
1479 /** Main loop for the rdp connection.
1480  *  It will be run from the thread's entry point (thread_func()).
1481  *  It initiates the connection, and will continue to run until the session ends,
1482  *  processing events as they are received.
1483  *  @param instance - pointer to the rdp_freerdp structure that contains the session's settings
1484  *  @return A code from the enum XF_EXIT_CODE (0 if successful)
1485  */
xf_client_thread(LPVOID param)1486 static DWORD WINAPI xf_client_thread(LPVOID param)
1487 {
1488 	BOOL status;
1489 	DWORD exit_code = 0;
1490 	DWORD nCount;
1491 	DWORD waitStatus;
1492 	HANDLE handles[64];
1493 	xfContext* xfc;
1494 	freerdp* instance;
1495 	rdpContext* context;
1496 	HANDLE inputEvent = NULL;
1497 	HANDLE inputThread = NULL;
1498 	HANDLE timer = NULL;
1499 	LARGE_INTEGER due;
1500 	rdpSettings* settings;
1501 	TimerEventArgs timerEvent;
1502 	EventArgsInit(&timerEvent, "xfreerdp");
1503 	instance = (freerdp*)param;
1504 	context = instance->context;
1505 	status = freerdp_connect(instance);
1506 	xfc = (xfContext*)instance->context;
1507 
1508 	if (!status)
1509 	{
1510 		if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_AUTHENTICATION_FAILED)
1511 			exit_code = XF_EXIT_AUTH_FAILURE;
1512 		else if (freerdp_get_last_error(instance->context) ==
1513 		         FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED)
1514 			exit_code = XF_EXIT_NEGO_FAILURE;
1515  		else if (freerdp_get_last_error(instance->context) ==
1516  				 FREERDP_ERROR_CONNECT_LOGON_FAILURE)
1517  			exit_code = XF_EXIT_LOGON_FAILURE;
1518  		else if (freerdp_get_last_error(instance->context) ==
1519  				 FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT)
1520  			exit_code = XF_EXIT_ACCOUNT_LOCKED_OUT;
1521  		else if (freerdp_get_last_error(instance->context) ==
1522  				 FREERDP_ERROR_PRE_CONNECT_FAILED)
1523  			exit_code = XF_EXIT_PRE_CONNECT_FAILED;
1524  		else if (freerdp_get_last_error(instance->context) ==
1525  				 FREERDP_ERROR_CONNECT_UNDEFINED)
1526  			exit_code = XF_EXIT_CONNECT_UNDEFINED;
1527  		else if (freerdp_get_last_error(instance->context) ==
1528  				 FREERDP_ERROR_POST_CONNECT_FAILED)
1529  			exit_code = XF_EXIT_POST_CONNECT_FAILED;
1530  		else if (freerdp_get_last_error(instance->context) ==
1531  				 FREERDP_ERROR_DNS_ERROR)
1532  			exit_code = XF_EXIT_DNS_ERROR;
1533  		else if (freerdp_get_last_error(instance->context) ==
1534  				 FREERDP_ERROR_DNS_NAME_NOT_FOUND)
1535  			exit_code = XF_EXIT_DNS_NAME_NOT_FOUND;
1536  		else if (freerdp_get_last_error(instance->context) ==
1537  				 FREERDP_ERROR_CONNECT_FAILED)
1538  			exit_code = XF_EXIT_CONNECT_FAILED;
1539  		else if (freerdp_get_last_error(instance->context) ==
1540  				 FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR)
1541  			exit_code = XF_EXIT_MCS_CONNECT_INITIAL_ERROR;
1542  		else if (freerdp_get_last_error(instance->context) ==
1543  				 FREERDP_ERROR_TLS_CONNECT_FAILED)
1544  			exit_code = XF_EXIT_TLS_CONNECT_FAILED;
1545  		else if (freerdp_get_last_error(instance->context) ==
1546  				 FREERDP_ERROR_INSUFFICIENT_PRIVILEGES)
1547  			exit_code = XF_EXIT_INSUFFICIENT_PRIVILEGES;
1548  		else if (freerdp_get_last_error(instance->context) ==
1549  				 FREERDP_ERROR_CONNECT_CANCELLED)
1550  			exit_code = XF_EXIT_CONNECT_CANCELLED;
1551  		else if (freerdp_get_last_error(instance->context) ==
1552  				 FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED)
1553  			exit_code = XF_EXIT_SECURITY_NEGO_CONNECT_FAILED;
1554  		else if (freerdp_get_last_error(instance->context) ==
1555  				 FREERDP_ERROR_CONNECT_TRANSPORT_FAILED)
1556  			exit_code = XF_EXIT_CONNECT_TRANSPORT_FAILED;
1557  		else if (freerdp_get_last_error(instance->context) ==
1558  				 FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED)
1559  			exit_code = XF_EXIT_CONNECT_PASSWORD_EXPIRED;
1560  		else if (freerdp_get_last_error(instance->context) ==
1561  				 FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE)
1562  			exit_code = XF_EXIT_CONNECT_PASSWORD_MUST_CHANGE;
1563  		else if (freerdp_get_last_error(instance->context) ==
1564  				 FREERDP_ERROR_CONNECT_KDC_UNREACHABLE)
1565  			exit_code = XF_EXIT_CONNECT_KDC_UNREACHABLE;
1566  		else if (freerdp_get_last_error(instance->context) ==
1567  				 FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED)
1568  			exit_code = XF_EXIT_CONNECT_ACCOUNT_DISABLED;
1569  		else if (freerdp_get_last_error(instance->context) ==
1570  				 FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED)
1571  			exit_code = XF_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED;
1572  		else if (freerdp_get_last_error(instance->context) ==
1573  				 FREERDP_ERROR_CONNECT_CLIENT_REVOKED)
1574  			exit_code = XF_EXIT_CONNECT_CLIENT_REVOKED;
1575  		else if (freerdp_get_last_error(instance->context) ==
1576  				 FREERDP_ERROR_CONNECT_WRONG_PASSWORD)
1577  			exit_code = XF_EXIT_CONNECT_WRONG_PASSWORD;
1578  		else if (freerdp_get_last_error(instance->context) ==
1579  				 FREERDP_ERROR_CONNECT_ACCESS_DENIED)
1580  			exit_code = XF_EXIT_CONNECT_ACCESS_DENIED;
1581  		else if (freerdp_get_last_error(instance->context) ==
1582  				 FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION)
1583  			exit_code = XF_EXIT_CONNECT_ACCOUNT_RESTRICTION;
1584  		else if (freerdp_get_last_error(instance->context) ==
1585  				 FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED)
1586  			exit_code = XF_EXIT_CONNECT_ACCOUNT_EXPIRED;
1587  		else if (freerdp_get_last_error(instance->context) ==
1588  				 FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED)
1589  			exit_code = XF_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED;
1590  		else if (freerdp_get_last_error(instance->context) ==
1591  				 FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS)
1592  			exit_code = XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS;
1593 		else
1594 			exit_code = XF_EXIT_CONN_FAILED;
1595 	}
1596 	else
1597 		exit_code = XF_EXIT_SUCCESS;
1598 
1599 	if (!status)
1600 		goto end;
1601 
1602 	/* --authonly ? */
1603 	if (instance->settings->AuthenticationOnly)
1604 	{
1605 		WLog_ERR(TAG, "Authentication only, exit status %" PRId32 "", !status);
1606 		goto disconnect;
1607 	}
1608 
1609 	if (!status)
1610 	{
1611 		WLog_ERR(TAG, "Freerdp connect error exit status %" PRId32 "", !status);
1612 		exit_code = freerdp_error_info(instance);
1613 
1614 		if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_AUTHENTICATION_FAILED)
1615 			exit_code = XF_EXIT_AUTH_FAILURE;
1616 		else if (exit_code == ERRINFO_SUCCESS)
1617 			exit_code = XF_EXIT_CONN_FAILED;
1618 
1619 		goto disconnect;
1620 	}
1621 
1622 	settings = context->settings;
1623 	timer = CreateWaitableTimerA(NULL, FALSE, "mainloop-periodic-timer");
1624 
1625 	if (!timer)
1626 	{
1627 		WLog_ERR(TAG, "failed to create timer");
1628 		goto disconnect;
1629 	}
1630 
1631 	due.QuadPart = 0;
1632 
1633 	if (!SetWaitableTimer(timer, &due, 20, NULL, NULL, FALSE))
1634 	{
1635 		goto disconnect;
1636 	}
1637 
1638 	if (!settings->AsyncInput)
1639 	{
1640 		inputEvent = xfc->x11event;
1641 	}
1642 	else
1643 	{
1644 		if (!(inputThread = CreateThread(NULL, 0, xf_input_thread, instance, 0, NULL)))
1645 		{
1646 			WLog_ERR(TAG, "async input: failed to create input thread");
1647 			exit_code = XF_EXIT_UNKNOWN;
1648 			goto disconnect;
1649 		}
1650 	}
1651 
1652 	while (!freerdp_shall_disconnect(instance))
1653 	{
1654 		nCount = 0;
1655 		handles[nCount++] = timer;
1656 
1657 		if (!settings->AsyncInput)
1658 			handles[nCount++] = inputEvent;
1659 
1660 		/*
1661 		 * win8 and server 2k12 seem to have some timing issue/race condition
1662 		 * when a initial sync request is send to sync the keyboard indicators
1663 		 * sending the sync event twice fixed this problem
1664 		 */
1665 		if (freerdp_focus_required(instance))
1666 		{
1667 			xf_keyboard_focus_in(xfc);
1668 			xf_keyboard_focus_in(xfc);
1669 		}
1670 
1671 		{
1672 			DWORD tmp =
1673 			    freerdp_get_event_handles(context, &handles[nCount], ARRAYSIZE(handles) - nCount);
1674 
1675 			if (tmp == 0)
1676 			{
1677 				WLog_ERR(TAG, "freerdp_get_event_handles failed");
1678 				break;
1679 			}
1680 
1681 			nCount += tmp;
1682 		}
1683 
1684 		if (xfc->window)
1685 			xf_floatbar_hide_and_show(xfc->window->floatbar);
1686 
1687 		waitStatus = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
1688 
1689 		if (waitStatus == WAIT_FAILED)
1690 			break;
1691 
1692 		{
1693 			if (!freerdp_check_event_handles(context))
1694 			{
1695 				if (client_auto_reconnect_ex(instance, handle_window_events))
1696 					continue;
1697 				else
1698 				{
1699 					/*
1700 					 * Indicate an unsuccessful connection attempt if reconnect
1701 					 * did not succeed and no other error was specified.
1702 					 */
1703 					if (freerdp_error_info(instance) == 0)
1704 						exit_code = XF_EXIT_CONN_FAILED;
1705 				}
1706 
1707 				if (freerdp_get_last_error(context) == FREERDP_ERROR_SUCCESS)
1708 					WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
1709 
1710 				break;
1711 			}
1712 		}
1713 
1714 		if (!handle_window_events(instance))
1715 			break;
1716 
1717 		if ((status != WAIT_TIMEOUT) && (waitStatus == WAIT_OBJECT_0))
1718 		{
1719 			timerEvent.now = GetTickCount64();
1720 			PubSub_OnTimer(context->pubSub, context, &timerEvent);
1721 		}
1722 	}
1723 
1724 	if (settings->AsyncInput)
1725 	{
1726 		WaitForSingleObject(inputThread, INFINITE);
1727 		CloseHandle(inputThread);
1728 	}
1729 
1730 	if (!exit_code)
1731 	{
1732 		exit_code = freerdp_error_info(instance);
1733 
1734 		if (exit_code == XF_EXIT_DISCONNECT &&
1735 		    freerdp_get_disconnect_ultimatum(context) == Disconnect_Ultimatum_user_requested)
1736 		{
1737 			/* This situation might be limited to Windows XP. */
1738 			WLog_INFO(TAG, "Error info says user did not initiate but disconnect ultimatum says "
1739 			               "they did; treat this as a user logoff");
1740 			exit_code = XF_EXIT_LOGOFF;
1741 		}
1742 	}
1743 
1744 disconnect:
1745 
1746 	if (timer)
1747 		CloseHandle(timer);
1748 
1749 	freerdp_disconnect(instance);
1750 end:
1751 	ExitThread(exit_code);
1752 	return exit_code;
1753 }
1754 
xf_exit_code_from_disconnect_reason(DWORD reason)1755 DWORD xf_exit_code_from_disconnect_reason(DWORD reason)
1756 {
1757 	if (reason == 0 || (reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS))
1758 		return reason;
1759 	/* License error set */
1760 	else if (reason >= 0x100 && reason <= 0x10A)
1761 		reason -= 0x100 + XF_EXIT_LICENSE_INTERNAL;
1762 	/* RDP protocol error set */
1763 	else if (reason >= 0x10c9 && reason <= 0x1193)
1764 		reason = XF_EXIT_RDP;
1765 	/* There's no need to test protocol-independent codes: they match */
1766 	else if (!(reason <= 0xC))
1767 		reason = XF_EXIT_UNKNOWN;
1768 
1769 	return reason;
1770 }
1771 
xf_TerminateEventHandler(void * context,TerminateEventArgs * e)1772 static void xf_TerminateEventHandler(void* context, TerminateEventArgs* e)
1773 {
1774 	rdpContext* ctx = (rdpContext*)context;
1775 	WINPR_UNUSED(e);
1776 	freerdp_abort_connect(ctx->instance);
1777 }
1778 
1779 #ifdef WITH_XRENDER
xf_ZoomingChangeEventHandler(void * context,ZoomingChangeEventArgs * e)1780 static void xf_ZoomingChangeEventHandler(void* context, ZoomingChangeEventArgs* e)
1781 {
1782 	xfContext* xfc = (xfContext*)context;
1783 	rdpSettings* settings = xfc->context.settings;
1784 	int w = xfc->scaledWidth + e->dx;
1785 	int h = xfc->scaledHeight + e->dy;
1786 
1787 	if (e->dx == 0 && e->dy == 0)
1788 		return;
1789 
1790 	if (w < 10)
1791 		w = 10;
1792 
1793 	if (h < 10)
1794 		h = 10;
1795 
1796 	if (w == xfc->scaledWidth && h == xfc->scaledHeight)
1797 		return;
1798 
1799 	xfc->scaledWidth = w;
1800 	xfc->scaledHeight = h;
1801 	xf_draw_screen(xfc, 0, 0, settings->DesktopWidth, settings->DesktopHeight);
1802 }
1803 
xf_PanningChangeEventHandler(void * context,PanningChangeEventArgs * e)1804 static void xf_PanningChangeEventHandler(void* context, PanningChangeEventArgs* e)
1805 {
1806 	xfContext* xfc = (xfContext*)context;
1807 	rdpSettings* settings = xfc->context.settings;
1808 
1809 	if (e->dx == 0 && e->dy == 0)
1810 		return;
1811 
1812 	xfc->offset_x += e->dx;
1813 	xfc->offset_y += e->dy;
1814 	xf_draw_screen(xfc, 0, 0, settings->DesktopWidth, settings->DesktopHeight);
1815 }
1816 #endif
1817 
1818 /**
1819  * Client Interface
1820  */
1821 
xfreerdp_client_global_init()1822 static BOOL xfreerdp_client_global_init()
1823 {
1824 	setlocale(LC_ALL, "");
1825 
1826 	if (freerdp_handle_signals() != 0)
1827 		return FALSE;
1828 
1829 	return TRUE;
1830 }
1831 
xfreerdp_client_global_uninit()1832 static void xfreerdp_client_global_uninit()
1833 {
1834 }
1835 
xfreerdp_client_start(rdpContext * context)1836 static int xfreerdp_client_start(rdpContext* context)
1837 {
1838 	xfContext* xfc = (xfContext*)context;
1839 	rdpSettings* settings = context->settings;
1840 
1841 	if (!settings->ServerHostname)
1842 	{
1843 		WLog_ERR(TAG, "error: server hostname was not specified with /v:<server>[:port]");
1844 		return -1;
1845 	}
1846 
1847 	if (!(xfc->thread = CreateThread(NULL, 0, xf_client_thread, context->instance, 0, NULL)))
1848 	{
1849 		WLog_ERR(TAG, "failed to create client thread");
1850 		return -1;
1851 	}
1852 
1853 	return 0;
1854 }
1855 
xfreerdp_client_stop(rdpContext * context)1856 static int xfreerdp_client_stop(rdpContext* context)
1857 {
1858 	xfContext* xfc = (xfContext*)context;
1859 	freerdp_abort_connect(context->instance);
1860 
1861 	if (xfc->thread)
1862 	{
1863 		WaitForSingleObject(xfc->thread, INFINITE);
1864 		CloseHandle(xfc->thread);
1865 		xfc->thread = NULL;
1866 	}
1867 
1868 	return 0;
1869 }
1870 
get_supported_atom(xfContext * xfc,const char * atomName)1871 static Atom get_supported_atom(xfContext* xfc, const char* atomName)
1872 {
1873 	unsigned long i;
1874 	const Atom atom = XInternAtom(xfc->display, atomName, False);
1875 
1876 	for (i = 0; i < xfc->supportedAtomCount; i++)
1877 	{
1878 		if (xfc->supportedAtoms[i] == atom)
1879 			return atom;
1880 	}
1881 
1882 	return None;
1883 }
xfreerdp_client_new(freerdp * instance,rdpContext * context)1884 static BOOL xfreerdp_client_new(freerdp* instance, rdpContext* context)
1885 {
1886 	xfContext* xfc = (xfContext*)instance->context;
1887 	assert(context);
1888 	assert(xfc);
1889 	assert(!xfc->display);
1890 	assert(!xfc->mutex);
1891 	assert(!xfc->x11event);
1892 	instance->PreConnect = xf_pre_connect;
1893 	instance->PostConnect = xf_post_connect;
1894 	instance->PostDisconnect = xf_post_disconnect;
1895 	instance->Authenticate = client_cli_authenticate;
1896 	instance->GatewayAuthenticate = client_cli_gw_authenticate;
1897 	instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
1898 	instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
1899 	instance->PresentGatewayMessage = client_cli_present_gateway_message;
1900 	instance->LogonErrorInfo = xf_logon_error_info;
1901 	PubSub_SubscribeTerminate(context->pubSub, xf_TerminateEventHandler);
1902 #ifdef WITH_XRENDER
1903 	PubSub_SubscribeZoomingChange(context->pubSub, xf_ZoomingChangeEventHandler);
1904 	PubSub_SubscribePanningChange(context->pubSub, xf_PanningChangeEventHandler);
1905 #endif
1906 	xfc->UseXThreads = TRUE;
1907 	/* uncomment below if debugging to prevent keyboard grap */
1908 	/* xfc->debug = TRUE; */
1909 
1910 	if (xfc->UseXThreads)
1911 	{
1912 		if (!XInitThreads())
1913 		{
1914 			WLog_WARN(TAG, "XInitThreads() failure");
1915 			xfc->UseXThreads = FALSE;
1916 		}
1917 	}
1918 
1919 	xfc->display = XOpenDisplay(NULL);
1920 
1921 	if (!xfc->display)
1922 	{
1923 		WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
1924 		WLog_ERR(TAG, "Please check that the $DISPLAY environment variable is properly set.");
1925 		goto fail_open_display;
1926 	}
1927 
1928 	xfc->mutex = CreateMutex(NULL, FALSE, NULL);
1929 
1930 	if (!xfc->mutex)
1931 	{
1932 		WLog_ERR(TAG, "Could not create mutex!");
1933 		goto fail_create_mutex;
1934 	}
1935 
1936 	xfc->xfds = ConnectionNumber(xfc->display);
1937 	xfc->screen_number = DefaultScreen(xfc->display);
1938 	xfc->screen = ScreenOfDisplay(xfc->display, xfc->screen_number);
1939 	xfc->depth = DefaultDepthOfScreen(xfc->screen);
1940 	xfc->big_endian = (ImageByteOrder(xfc->display) == MSBFirst);
1941 	xfc->invert = TRUE;
1942 	xfc->complex_regions = TRUE;
1943 	xfc->_NET_SUPPORTED = XInternAtom(xfc->display, "_NET_SUPPORTED", True);
1944 	xfc->_NET_SUPPORTING_WM_CHECK = XInternAtom(xfc->display, "_NET_SUPPORTING_WM_CHECK", True);
1945 
1946 	if ((xfc->_NET_SUPPORTED != None) && (xfc->_NET_SUPPORTING_WM_CHECK != None))
1947 	{
1948 		Atom actual_type = 0;
1949 		int actual_format = 0;
1950 		unsigned long nitems = 0, after = 0;
1951 		unsigned char* data = NULL;
1952 		int status = XGetWindowProperty(xfc->display, RootWindowOfScreen(xfc->screen),
1953 		                                xfc->_NET_SUPPORTED, 0, 1024, False, XA_ATOM, &actual_type,
1954 		                                &actual_format, &nitems, &after, &data);
1955 
1956 		if ((status == Success) && (actual_type == XA_ATOM) && (actual_format == 32))
1957 		{
1958 			xfc->supportedAtomCount = nitems;
1959 			xfc->supportedAtoms = calloc(nitems, sizeof(Atom));
1960 			memcpy(xfc->supportedAtoms, data, nitems * sizeof(Atom));
1961 		}
1962 
1963 		if (data)
1964 			XFree(data);
1965 	}
1966 
1967 	xfc->_XWAYLAND_MAY_GRAB_KEYBOARD =
1968 	    XInternAtom(xfc->display, "_XWAYLAND_MAY_GRAB_KEYBOARD", False);
1969 	xfc->_NET_WM_ICON = XInternAtom(xfc->display, "_NET_WM_ICON", False);
1970 	xfc->_MOTIF_WM_HINTS = XInternAtom(xfc->display, "_MOTIF_WM_HINTS", False);
1971 	xfc->_NET_CURRENT_DESKTOP = XInternAtom(xfc->display, "_NET_CURRENT_DESKTOP", False);
1972 	xfc->_NET_WORKAREA = XInternAtom(xfc->display, "_NET_WORKAREA", False);
1973 	xfc->_NET_WM_STATE = get_supported_atom(xfc, "_NET_WM_STATE");
1974 	xfc->_NET_WM_STATE_FULLSCREEN = get_supported_atom(xfc, "_NET_WM_STATE_FULLSCREEN");
1975 	xfc->_NET_WM_STATE_MAXIMIZED_HORZ =
1976 	    XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
1977 	xfc->_NET_WM_STATE_MAXIMIZED_VERT =
1978 	    XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
1979 	xfc->_NET_WM_FULLSCREEN_MONITORS = get_supported_atom(xfc, "_NET_WM_FULLSCREEN_MONITORS");
1980 	xfc->_NET_WM_NAME = XInternAtom(xfc->display, "_NET_WM_NAME", False);
1981 	xfc->_NET_WM_PID = XInternAtom(xfc->display, "_NET_WM_PID", False);
1982 	xfc->_NET_WM_WINDOW_TYPE = XInternAtom(xfc->display, "_NET_WM_WINDOW_TYPE", False);
1983 	xfc->_NET_WM_WINDOW_TYPE_NORMAL =
1984 	    XInternAtom(xfc->display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
1985 	xfc->_NET_WM_WINDOW_TYPE_DIALOG =
1986 	    XInternAtom(xfc->display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
1987 	xfc->_NET_WM_WINDOW_TYPE_POPUP = XInternAtom(xfc->display, "_NET_WM_WINDOW_TYPE_POPUP", False);
1988 	xfc->_NET_WM_WINDOW_TYPE_POPUP_MENU =
1989 	    XInternAtom(xfc->display, "_NET_WM_WINDOW_TYPE_POPUP_MENU", False);
1990 	xfc->_NET_WM_WINDOW_TYPE_UTILITY =
1991 	    XInternAtom(xfc->display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
1992 	xfc->_NET_WM_WINDOW_TYPE_DROPDOWN_MENU =
1993 	    XInternAtom(xfc->display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False);
1994 	xfc->_NET_WM_STATE_SKIP_TASKBAR =
1995 	    XInternAtom(xfc->display, "_NET_WM_STATE_SKIP_TASKBAR", False);
1996 	xfc->_NET_WM_STATE_SKIP_PAGER = XInternAtom(xfc->display, "_NET_WM_STATE_SKIP_PAGER", False);
1997 	xfc->_NET_WM_MOVERESIZE = XInternAtom(xfc->display, "_NET_WM_MOVERESIZE", False);
1998 	xfc->_NET_MOVERESIZE_WINDOW = XInternAtom(xfc->display, "_NET_MOVERESIZE_WINDOW", False);
1999 	xfc->UTF8_STRING = XInternAtom(xfc->display, "UTF8_STRING", FALSE);
2000 	xfc->WM_PROTOCOLS = XInternAtom(xfc->display, "WM_PROTOCOLS", False);
2001 	xfc->WM_DELETE_WINDOW = XInternAtom(xfc->display, "WM_DELETE_WINDOW", False);
2002 	xfc->WM_STATE = XInternAtom(xfc->display, "WM_STATE", False);
2003 	xfc->x11event = CreateFileDescriptorEvent(NULL, FALSE, FALSE, xfc->xfds, WINPR_FD_READ);
2004 
2005 	if (!xfc->x11event)
2006 	{
2007 		WLog_ERR(TAG, "Could not create xfds event");
2008 		goto fail_xfds_event;
2009 	}
2010 
2011 	xfc->colormap = DefaultColormap(xfc->display, xfc->screen_number);
2012 
2013 	if (xfc->debug)
2014 	{
2015 		WLog_INFO(TAG, "Enabling X11 debug mode.");
2016 		XSynchronize(xfc->display, TRUE);
2017 		_def_error_handler = XSetErrorHandler(_xf_error_handler);
2018 	}
2019 
2020 	xf_check_extensions(xfc);
2021 
2022 	if (!xf_get_pixmap_info(xfc))
2023 	{
2024 		WLog_ERR(TAG, "Failed to get pixmap info");
2025 		goto fail_pixmap_info;
2026 	}
2027 
2028 	xfc->vscreen.monitors = calloc(16, sizeof(MONITOR_INFO));
2029 
2030 	if (!xfc->vscreen.monitors)
2031 		goto fail_vscreen_monitors;
2032 
2033 	return TRUE;
2034 fail_vscreen_monitors:
2035 fail_pixmap_info:
2036 	CloseHandle(xfc->x11event);
2037 	xfc->x11event = NULL;
2038 fail_xfds_event:
2039 	CloseHandle(xfc->mutex);
2040 	xfc->mutex = NULL;
2041 fail_create_mutex:
2042 	XCloseDisplay(xfc->display);
2043 	xfc->display = NULL;
2044 fail_open_display:
2045 	return FALSE;
2046 }
2047 
xfreerdp_client_free(freerdp * instance,rdpContext * context)2048 static void xfreerdp_client_free(freerdp* instance, rdpContext* context)
2049 {
2050 	xfContext* xfc = (xfContext*)instance->context;
2051 
2052 	if (!context)
2053 		return;
2054 
2055 	PubSub_UnsubscribeTerminate(context->pubSub, xf_TerminateEventHandler);
2056 #ifdef WITH_XRENDER
2057 	PubSub_UnsubscribeZoomingChange(context->pubSub, xf_ZoomingChangeEventHandler);
2058 	PubSub_UnsubscribePanningChange(context->pubSub, xf_PanningChangeEventHandler);
2059 #endif
2060 
2061 	if (xfc->display)
2062 	{
2063 		XCloseDisplay(xfc->display);
2064 		xfc->display = NULL;
2065 	}
2066 
2067 	if (xfc->x11event)
2068 	{
2069 		CloseHandle(xfc->x11event);
2070 		xfc->x11event = NULL;
2071 	}
2072 
2073 	if (xfc->mutex)
2074 	{
2075 		CloseHandle(xfc->mutex);
2076 		xfc->mutex = NULL;
2077 	}
2078 
2079 	if (xfc->vscreen.monitors)
2080 	{
2081 		free(xfc->vscreen.monitors);
2082 		xfc->vscreen.monitors = NULL;
2083 	}
2084 
2085 	free(xfc->supportedAtoms);
2086 }
2087 
RdpClientEntry(RDP_CLIENT_ENTRY_POINTS * pEntryPoints)2088 int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
2089 {
2090 	pEntryPoints->Version = 1;
2091 	pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
2092 	pEntryPoints->GlobalInit = xfreerdp_client_global_init;
2093 	pEntryPoints->GlobalUninit = xfreerdp_client_global_uninit;
2094 	pEntryPoints->ContextSize = sizeof(xfContext);
2095 	pEntryPoints->ClientNew = xfreerdp_client_new;
2096 	pEntryPoints->ClientFree = xfreerdp_client_free;
2097 	pEntryPoints->ClientStart = xfreerdp_client_start;
2098 	pEntryPoints->ClientStop = xfreerdp_client_stop;
2099 	return 0;
2100 }
2101