1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  *
4  * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
5  * Copyright 2017 Armin Novak <armin.novak@thincast.com>
6  * Copyright 2017 Thincast Technologies GmbH
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include <sys/ipc.h>
28 #include <sys/shm.h>
29 #include <sys/select.h>
30 #include <sys/signal.h>
31 
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34 
35 #include <winpr/crt.h>
36 #include <winpr/path.h>
37 #include <winpr/synch.h>
38 #include <winpr/image.h>
39 #include <winpr/sysinfo.h>
40 
41 #include <freerdp/log.h>
42 #include <freerdp/codec/color.h>
43 #include <freerdp/codec/region.h>
44 
45 #include "x11_shadow.h"
46 
47 #define TAG SERVER_TAG("shadow.x11")
48 
49 static UINT32 x11_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors);
50 
51 #ifdef WITH_PAM
52 
53 #include <security/pam_appl.h>
54 
55 struct _SHADOW_PAM_AUTH_DATA
56 {
57 	const char* user;
58 	const char* domain;
59 	const char* password;
60 };
61 typedef struct _SHADOW_PAM_AUTH_DATA SHADOW_PAM_AUTH_DATA;
62 
63 struct _SHADOW_PAM_AUTH_INFO
64 {
65 	char* service_name;
66 	pam_handle_t* handle;
67 	struct pam_conv pamc;
68 	SHADOW_PAM_AUTH_DATA appdata;
69 };
70 typedef struct _SHADOW_PAM_AUTH_INFO SHADOW_PAM_AUTH_INFO;
71 
x11_shadow_pam_conv(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)72 static int x11_shadow_pam_conv(int num_msg, const struct pam_message** msg,
73                                struct pam_response** resp, void* appdata_ptr)
74 {
75 	int index;
76 	int pam_status = PAM_CONV_ERR;
77 	SHADOW_PAM_AUTH_DATA* appdata;
78 	struct pam_response* response;
79 	appdata = (SHADOW_PAM_AUTH_DATA*)appdata_ptr;
80 
81 	if (!(response = (struct pam_response*)calloc(num_msg, sizeof(struct pam_response))))
82 		return PAM_BUF_ERR;
83 
84 	for (index = 0; index < num_msg; index++)
85 	{
86 		switch (msg[index]->msg_style)
87 		{
88 			case PAM_PROMPT_ECHO_ON:
89 				response[index].resp = _strdup(appdata->user);
90 
91 				if (!response[index].resp)
92 					goto out_fail;
93 
94 				response[index].resp_retcode = PAM_SUCCESS;
95 				break;
96 
97 			case PAM_PROMPT_ECHO_OFF:
98 				response[index].resp = _strdup(appdata->password);
99 
100 				if (!response[index].resp)
101 					goto out_fail;
102 
103 				response[index].resp_retcode = PAM_SUCCESS;
104 				break;
105 
106 			default:
107 				pam_status = PAM_CONV_ERR;
108 				goto out_fail;
109 		}
110 	}
111 
112 	*resp = response;
113 	return PAM_SUCCESS;
114 out_fail:
115 
116 	for (index = 0; index < num_msg; ++index)
117 	{
118 		if (response[index].resp)
119 		{
120 			memset(response[index].resp, 0, strlen(response[index].resp));
121 			free(response[index].resp);
122 		}
123 	}
124 
125 	memset(response, 0, sizeof(struct pam_response) * num_msg);
126 	free(response);
127 	*resp = NULL;
128 	return pam_status;
129 }
130 
x11_shadow_pam_get_service_name(SHADOW_PAM_AUTH_INFO * info)131 static BOOL x11_shadow_pam_get_service_name(SHADOW_PAM_AUTH_INFO* info)
132 {
133 	size_t x;
134 	const char* base = "/etc/pam.d";
135 	const char* hints[] = { "lightdm", "gdm", "xdm", "login", "sshd" };
136 
137 	for (x = 0; x < ARRAYSIZE(hints); x++)
138 	{
139 		char path[MAX_PATH];
140 		const char* hint = hints[x];
141 
142 		_snprintf(path, sizeof(path), "%s/%s", base, hint);
143 		if (winpr_PathFileExists(path))
144 		{
145 
146 			info->service_name = _strdup(hint);
147 			return info->service_name != NULL;
148 		}
149 	}
150 	WLog_WARN(TAG, "Could not determine PAM service name");
151 	return FALSE;
152 }
153 
x11_shadow_pam_authenticate(rdpShadowSubsystem * subsystem,rdpShadowClient * client,const char * user,const char * domain,const char * password)154 static int x11_shadow_pam_authenticate(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
155                                        const char* user, const char* domain, const char* password)
156 {
157 	int pam_status;
158 	SHADOW_PAM_AUTH_INFO info = { 0 };
159 	WINPR_UNUSED(subsystem);
160 	WINPR_UNUSED(client);
161 
162 	if (!x11_shadow_pam_get_service_name(&info))
163 		return -1;
164 
165 	info.appdata.user = user;
166 	info.appdata.domain = domain;
167 	info.appdata.password = password;
168 	info.pamc.conv = &x11_shadow_pam_conv;
169 	info.pamc.appdata_ptr = &info.appdata;
170 	pam_status = pam_start(info.service_name, 0, &info.pamc, &info.handle);
171 
172 	if (pam_status != PAM_SUCCESS)
173 	{
174 		WLog_ERR(TAG, "pam_start failure: %s", pam_strerror(info.handle, pam_status));
175 		return -1;
176 	}
177 
178 	pam_status = pam_authenticate(info.handle, 0);
179 
180 	if (pam_status != PAM_SUCCESS)
181 	{
182 		WLog_ERR(TAG, "pam_authenticate failure: %s", pam_strerror(info.handle, pam_status));
183 		return -1;
184 	}
185 
186 	pam_status = pam_acct_mgmt(info.handle, 0);
187 
188 	if (pam_status != PAM_SUCCESS)
189 	{
190 		WLog_ERR(TAG, "pam_acct_mgmt failure: %s", pam_strerror(info.handle, pam_status));
191 		return -1;
192 	}
193 
194 	return 1;
195 }
196 
197 #endif
198 
x11_shadow_input_synchronize_event(rdpShadowSubsystem * subsystem,rdpShadowClient * client,UINT32 flags)199 static BOOL x11_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
200                                                rdpShadowClient* client, UINT32 flags)
201 {
202 	/* TODO: Implement */
203 	WLog_WARN(TAG, "%s not implemented", __FUNCTION__);
204 	return TRUE;
205 }
206 
x11_shadow_input_keyboard_event(rdpShadowSubsystem * subsystem,rdpShadowClient * client,UINT16 flags,UINT16 code)207 static BOOL x11_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
208                                             UINT16 flags, UINT16 code)
209 {
210 #ifdef WITH_XTEST
211 	x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
212 	DWORD vkcode;
213 	DWORD keycode;
214 	BOOL extended = FALSE;
215 
216 	if (!client || !subsystem)
217 		return FALSE;
218 
219 	if (flags & KBD_FLAGS_EXTENDED)
220 		extended = TRUE;
221 
222 	if (extended)
223 		code |= KBDEXT;
224 
225 	vkcode = GetVirtualKeyCodeFromVirtualScanCode(code, 4);
226 
227 	if (extended)
228 		vkcode |= KBDEXT;
229 
230 	keycode = GetKeycodeFromVirtualKeyCode(vkcode, KEYCODE_TYPE_EVDEV);
231 
232 	if (keycode != 0)
233 	{
234 		XLockDisplay(x11->display);
235 		XTestGrabControl(x11->display, True);
236 
237 		if (flags & KBD_FLAGS_DOWN)
238 			XTestFakeKeyEvent(x11->display, keycode, True, CurrentTime);
239 		else if (flags & KBD_FLAGS_RELEASE)
240 			XTestFakeKeyEvent(x11->display, keycode, False, CurrentTime);
241 
242 		XTestGrabControl(x11->display, False);
243 		XFlush(x11->display);
244 		XUnlockDisplay(x11->display);
245 	}
246 
247 #endif
248 	return TRUE;
249 }
250 
x11_shadow_input_unicode_keyboard_event(rdpShadowSubsystem * subsystem,rdpShadowClient * client,UINT16 flags,UINT16 code)251 static BOOL x11_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
252                                                     rdpShadowClient* client, UINT16 flags,
253                                                     UINT16 code)
254 {
255 	/* TODO: Implement */
256 	WLog_WARN(TAG, "%s not implemented", __FUNCTION__);
257 	return TRUE;
258 }
259 
x11_shadow_input_mouse_event(rdpShadowSubsystem * subsystem,rdpShadowClient * client,UINT16 flags,UINT16 x,UINT16 y)260 static BOOL x11_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
261                                          UINT16 flags, UINT16 x, UINT16 y)
262 {
263 #ifdef WITH_XTEST
264 	x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
265 	int button = 0;
266 	BOOL down = FALSE;
267 	rdpShadowServer* server;
268 	rdpShadowSurface* surface;
269 
270 	if (!subsystem || !client)
271 		return FALSE;
272 
273 	server = subsystem->server;
274 
275 	if (!server)
276 		return FALSE;
277 
278 	surface = server->surface;
279 
280 	if (!surface)
281 		return FALSE;
282 
283 	x11->lastMouseClient = client;
284 	x += surface->x;
285 	y += surface->y;
286 	XLockDisplay(x11->display);
287 	XTestGrabControl(x11->display, True);
288 
289 	if (flags & PTR_FLAGS_WHEEL)
290 	{
291 		BOOL negative = FALSE;
292 
293 		if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
294 			negative = TRUE;
295 
296 		button = (negative) ? 5 : 4;
297 		XTestFakeButtonEvent(x11->display, button, True, CurrentTime);
298 		XTestFakeButtonEvent(x11->display, button, False, CurrentTime);
299 	}
300 	else
301 	{
302 		if (flags & PTR_FLAGS_MOVE)
303 			XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
304 
305 		if (flags & PTR_FLAGS_BUTTON1)
306 			button = 1;
307 		else if (flags & PTR_FLAGS_BUTTON2)
308 			button = 3;
309 		else if (flags & PTR_FLAGS_BUTTON3)
310 			button = 2;
311 
312 		if (flags & PTR_FLAGS_DOWN)
313 			down = TRUE;
314 
315 		if (button)
316 			XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
317 	}
318 
319 	XTestGrabControl(x11->display, False);
320 	XFlush(x11->display);
321 	XUnlockDisplay(x11->display);
322 #endif
323 	return TRUE;
324 }
325 
x11_shadow_input_extended_mouse_event(rdpShadowSubsystem * subsystem,rdpShadowClient * client,UINT16 flags,UINT16 x,UINT16 y)326 static BOOL x11_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
327                                                   rdpShadowClient* client, UINT16 flags, UINT16 x,
328                                                   UINT16 y)
329 {
330 #ifdef WITH_XTEST
331 	x11ShadowSubsystem* x11 = (x11ShadowSubsystem*)subsystem;
332 	int button = 0;
333 	BOOL down = FALSE;
334 	rdpShadowServer* server;
335 	rdpShadowSurface* surface;
336 
337 	if (!subsystem || !client)
338 		return FALSE;
339 
340 	server = subsystem->server;
341 
342 	if (!server)
343 		return FALSE;
344 
345 	surface = server->surface;
346 
347 	if (!surface)
348 		return FALSE;
349 
350 	x11->lastMouseClient = client;
351 	x += surface->x;
352 	y += surface->y;
353 	XLockDisplay(x11->display);
354 	XTestGrabControl(x11->display, True);
355 	XTestFakeMotionEvent(x11->display, 0, x, y, CurrentTime);
356 
357 	if (flags & PTR_XFLAGS_BUTTON1)
358 		button = 8;
359 	else if (flags & PTR_XFLAGS_BUTTON2)
360 		button = 9;
361 
362 	if (flags & PTR_XFLAGS_DOWN)
363 		down = TRUE;
364 
365 	if (button)
366 		XTestFakeButtonEvent(x11->display, button, down, CurrentTime);
367 
368 	XTestGrabControl(x11->display, False);
369 	XFlush(x11->display);
370 	XUnlockDisplay(x11->display);
371 #endif
372 	return TRUE;
373 }
374 
x11_shadow_message_free(UINT32 id,SHADOW_MSG_OUT * msg)375 static void x11_shadow_message_free(UINT32 id, SHADOW_MSG_OUT* msg)
376 {
377 	switch (id)
378 	{
379 		case SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID:
380 			free(msg);
381 			break;
382 
383 		case SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID:
384 			free(((SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)msg)->xorMaskData);
385 			free(((SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)msg)->andMaskData);
386 			free(msg);
387 			break;
388 
389 		default:
390 			WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", id);
391 			free(msg);
392 			break;
393 	}
394 }
395 
x11_shadow_pointer_position_update(x11ShadowSubsystem * subsystem)396 static int x11_shadow_pointer_position_update(x11ShadowSubsystem* subsystem)
397 {
398 	UINT32 msgId = SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID;
399 	rdpShadowServer* server;
400 	SHADOW_MSG_OUT_POINTER_POSITION_UPDATE templateMsg;
401 	int count = 0;
402 	int index = 0;
403 
404 	if (!subsystem || !subsystem->common.server || !subsystem->common.server->clients)
405 		return -1;
406 
407 	templateMsg.xPos = subsystem->common.pointerX;
408 	templateMsg.yPos = subsystem->common.pointerY;
409 	templateMsg.common.Free = x11_shadow_message_free;
410 	server = subsystem->common.server;
411 	ArrayList_Lock(server->clients);
412 
413 	for (index = 0; index < ArrayList_Count(server->clients); index++)
414 	{
415 		SHADOW_MSG_OUT_POINTER_POSITION_UPDATE* msg;
416 		rdpShadowClient* client = (rdpShadowClient*)ArrayList_GetItem(server->clients, index);
417 
418 		/* Skip the client which send us the latest mouse event */
419 		if (client == subsystem->lastMouseClient)
420 			continue;
421 
422 		msg = malloc(sizeof(templateMsg));
423 
424 		if (!msg)
425 		{
426 			count = -1;
427 			break;
428 		}
429 
430 		memcpy(msg, &templateMsg, sizeof(templateMsg));
431 
432 		if (shadow_client_post_msg(client, NULL, msgId, (SHADOW_MSG_OUT*)msg, NULL))
433 			count++;
434 	}
435 
436 	ArrayList_Unlock(server->clients);
437 	return count;
438 }
439 
x11_shadow_pointer_alpha_update(x11ShadowSubsystem * subsystem)440 static int x11_shadow_pointer_alpha_update(x11ShadowSubsystem* subsystem)
441 {
442 	SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* msg;
443 	UINT32 msgId = SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID;
444 	msg = (SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE*)calloc(1,
445 	                                                   sizeof(SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE));
446 
447 	if (!msg)
448 		return -1;
449 
450 	msg->xHot = subsystem->cursorHotX;
451 	msg->yHot = subsystem->cursorHotY;
452 	msg->width = subsystem->cursorWidth;
453 	msg->height = subsystem->cursorHeight;
454 
455 	if (shadow_subsystem_pointer_convert_alpha_pointer_data(subsystem->cursorPixels, TRUE,
456 	                                                        msg->width, msg->height, msg) < 0)
457 	{
458 		free(msg);
459 		return -1;
460 	}
461 
462 	msg->common.Free = x11_shadow_message_free;
463 	return shadow_client_boardcast_msg(subsystem->common.server, NULL, msgId, (SHADOW_MSG_OUT*)msg,
464 	                                   NULL)
465 	           ? 1
466 	           : -1;
467 }
468 
x11_shadow_query_cursor(x11ShadowSubsystem * subsystem,BOOL getImage)469 static int x11_shadow_query_cursor(x11ShadowSubsystem* subsystem, BOOL getImage)
470 {
471 	int x = 0, y = 0, n, k;
472 	rdpShadowServer* server;
473 	rdpShadowSurface* surface;
474 	server = subsystem->common.server;
475 	surface = server->surface;
476 
477 	if (getImage)
478 	{
479 #ifdef WITH_XFIXES
480 		UINT32* pDstPixel;
481 		XFixesCursorImage* ci;
482 		XLockDisplay(subsystem->display);
483 		ci = XFixesGetCursorImage(subsystem->display);
484 		XUnlockDisplay(subsystem->display);
485 
486 		if (!ci)
487 			return -1;
488 
489 		x = ci->x;
490 		y = ci->y;
491 
492 		if (ci->width > subsystem->cursorMaxWidth)
493 			return -1;
494 
495 		if (ci->height > subsystem->cursorMaxHeight)
496 			return -1;
497 
498 		subsystem->cursorHotX = ci->xhot;
499 		subsystem->cursorHotY = ci->yhot;
500 		subsystem->cursorWidth = ci->width;
501 		subsystem->cursorHeight = ci->height;
502 		subsystem->cursorId = ci->cursor_serial;
503 		n = ci->width * ci->height;
504 		pDstPixel = (UINT32*)subsystem->cursorPixels;
505 
506 		for (k = 0; k < n; k++)
507 		{
508 			/* XFixesCursorImage.pixels is in *unsigned long*, which may be 8 bytes */
509 			*pDstPixel++ = (UINT32)ci->pixels[k];
510 		}
511 
512 		XFree(ci);
513 		x11_shadow_pointer_alpha_update(subsystem);
514 #endif
515 	}
516 	else
517 	{
518 		UINT32 mask;
519 		int win_x, win_y;
520 		int root_x, root_y;
521 		Window root, child;
522 		XLockDisplay(subsystem->display);
523 
524 		if (!XQueryPointer(subsystem->display, subsystem->root_window, &root, &child, &root_x,
525 		                   &root_y, &win_x, &win_y, &mask))
526 		{
527 			XUnlockDisplay(subsystem->display);
528 			return -1;
529 		}
530 
531 		XUnlockDisplay(subsystem->display);
532 		x = root_x;
533 		y = root_y;
534 	}
535 
536 	/* Convert to offset based on current surface */
537 	if (surface)
538 	{
539 		x -= surface->x;
540 		y -= surface->y;
541 	}
542 
543 	if ((x != (INT64)subsystem->common.pointerX) || (y != (INT64)subsystem->common.pointerY))
544 	{
545 		subsystem->common.pointerX = x;
546 		subsystem->common.pointerY = y;
547 		x11_shadow_pointer_position_update(subsystem);
548 	}
549 
550 	return 1;
551 }
552 
x11_shadow_handle_xevent(x11ShadowSubsystem * subsystem,XEvent * xevent)553 static int x11_shadow_handle_xevent(x11ShadowSubsystem* subsystem, XEvent* xevent)
554 {
555 	if (xevent->type == MotionNotify)
556 	{
557 	}
558 
559 #ifdef WITH_XFIXES
560 	else if (xevent->type == subsystem->xfixes_cursor_notify_event)
561 	{
562 		x11_shadow_query_cursor(subsystem, TRUE);
563 	}
564 
565 #endif
566 	else
567 	{
568 	}
569 
570 	return 1;
571 }
572 
x11_shadow_validate_region(x11ShadowSubsystem * subsystem,int x,int y,int width,int height)573 static void x11_shadow_validate_region(x11ShadowSubsystem* subsystem, int x, int y, int width,
574                                        int height)
575 {
576 	XRectangle region;
577 
578 	if (!subsystem->use_xfixes || !subsystem->use_xdamage)
579 		return;
580 
581 	region.x = x;
582 	region.y = y;
583 	region.width = width;
584 	region.height = height;
585 #ifdef WITH_XFIXES
586 	XLockDisplay(subsystem->display);
587 	XFixesSetRegion(subsystem->display, subsystem->xdamage_region, &region, 1);
588 	XDamageSubtract(subsystem->display, subsystem->xdamage, subsystem->xdamage_region, None);
589 	XUnlockDisplay(subsystem->display);
590 #endif
591 }
592 
x11_shadow_blend_cursor(x11ShadowSubsystem * subsystem)593 static int x11_shadow_blend_cursor(x11ShadowSubsystem* subsystem)
594 {
595 	int x, y;
596 	int nXSrc;
597 	int nYSrc;
598 	int nXDst;
599 	int nYDst;
600 	int nWidth;
601 	int nHeight;
602 	int nSrcStep;
603 	int nDstStep;
604 	BYTE* pSrcData;
605 	BYTE* pDstData;
606 	BYTE A, R, G, B;
607 	rdpShadowSurface* surface;
608 
609 	if (!subsystem)
610 		return -1;
611 
612 	surface = subsystem->common.server->surface;
613 	nXSrc = 0;
614 	nYSrc = 0;
615 	nWidth = subsystem->cursorWidth;
616 	nHeight = subsystem->cursorHeight;
617 	nXDst = subsystem->common.pointerX - subsystem->cursorHotX;
618 	nYDst = subsystem->common.pointerY - subsystem->cursorHotY;
619 
620 	if (nXDst >= surface->width)
621 		return 1;
622 
623 	if (nXDst < 0)
624 	{
625 		nXDst *= -1;
626 
627 		if (nXDst >= nWidth)
628 			return 1;
629 
630 		nXSrc = nXDst;
631 		nWidth -= nXDst;
632 		nXDst = 0;
633 	}
634 
635 	if (nYDst >= surface->height)
636 		return 1;
637 
638 	if (nYDst < 0)
639 	{
640 		nYDst *= -1;
641 
642 		if (nYDst >= nHeight)
643 			return 1;
644 
645 		nYSrc = nYDst;
646 		nHeight -= nYDst;
647 		nYDst = 0;
648 	}
649 
650 	if ((nXDst + nWidth) > surface->width)
651 		nWidth = surface->width - nXDst;
652 
653 	if ((nYDst + nHeight) > surface->height)
654 		nHeight = surface->height - nYDst;
655 
656 	pSrcData = subsystem->cursorPixels;
657 	nSrcStep = subsystem->cursorWidth * 4;
658 	pDstData = surface->data;
659 	nDstStep = surface->scanline;
660 
661 	for (y = 0; y < nHeight; y++)
662 	{
663 		const BYTE* pSrcPixel = &pSrcData[((nYSrc + y) * nSrcStep) + (nXSrc * 4)];
664 		BYTE* pDstPixel = &pDstData[((nYDst + y) * nDstStep) + (nXDst * 4)];
665 
666 		for (x = 0; x < nWidth; x++)
667 		{
668 			B = *pSrcPixel++;
669 			G = *pSrcPixel++;
670 			R = *pSrcPixel++;
671 			A = *pSrcPixel++;
672 
673 			if (A == 0xFF)
674 			{
675 				pDstPixel[0] = B;
676 				pDstPixel[1] = G;
677 				pDstPixel[2] = R;
678 			}
679 			else
680 			{
681 				pDstPixel[0] = B + (pDstPixel[0] * (0xFF - A) + (0xFF / 2)) / 0xFF;
682 				pDstPixel[1] = G + (pDstPixel[1] * (0xFF - A) + (0xFF / 2)) / 0xFF;
683 				pDstPixel[2] = R + (pDstPixel[2] * (0xFF - A) + (0xFF / 2)) / 0xFF;
684 			}
685 
686 			pDstPixel[3] = 0xFF;
687 			pDstPixel += 4;
688 		}
689 	}
690 
691 	return 1;
692 }
693 
x11_shadow_check_resize(x11ShadowSubsystem * subsystem)694 static BOOL x11_shadow_check_resize(x11ShadowSubsystem* subsystem)
695 {
696 	MONITOR_DEF* virtualScreen;
697 	XWindowAttributes attr;
698 	XLockDisplay(subsystem->display);
699 	XGetWindowAttributes(subsystem->display, subsystem->root_window, &attr);
700 	XUnlockDisplay(subsystem->display);
701 
702 	if (attr.width != (INT64)subsystem->width || attr.height != (INT64)subsystem->height)
703 	{
704 		/* Screen size changed. Refresh monitor definitions and trigger screen resize */
705 		subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
706 		shadow_screen_resize(subsystem->common.server->screen);
707 		subsystem->width = attr.width;
708 		subsystem->height = attr.height;
709 		virtualScreen = &(subsystem->common.virtualScreen);
710 		virtualScreen->left = 0;
711 		virtualScreen->top = 0;
712 		virtualScreen->right = subsystem->width;
713 		virtualScreen->bottom = subsystem->height;
714 		virtualScreen->flags = 1;
715 		return TRUE;
716 	}
717 
718 	return FALSE;
719 }
720 
x11_shadow_error_handler_for_capture(Display * display,XErrorEvent * event)721 static int x11_shadow_error_handler_for_capture(Display* display, XErrorEvent* event)
722 {
723 	char msg[256];
724 	XGetErrorText(display, event->error_code, (char*)&msg, sizeof(msg));
725 	WLog_ERR(TAG, "X11 error: %s Error code: %x, request code: %x, minor code: %x", msg,
726 	         event->error_code, event->request_code, event->minor_code);
727 
728 	/* Ignore BAD MATCH error during image capture. Abort in other case */
729 	if (event->error_code != BadMatch)
730 	{
731 		abort();
732 	}
733 
734 	return 0;
735 }
736 
x11_shadow_screen_grab(x11ShadowSubsystem * subsystem)737 static int x11_shadow_screen_grab(x11ShadowSubsystem* subsystem)
738 {
739 	int rc = 0;
740 	int count;
741 	int status;
742 	int x, y;
743 	int width, height;
744 	XImage* image;
745 	rdpShadowServer* server;
746 	rdpShadowSurface* surface;
747 	RECTANGLE_16 invalidRect;
748 	RECTANGLE_16 surfaceRect;
749 	const RECTANGLE_16* extents;
750 	server = subsystem->common.server;
751 	surface = server->surface;
752 	count = ArrayList_Count(server->clients);
753 
754 	if (count < 1)
755 		return 1;
756 
757 	EnterCriticalSection(&surface->lock);
758 	surfaceRect.left = 0;
759 	surfaceRect.top = 0;
760 	surfaceRect.right = surface->width;
761 	surfaceRect.bottom = surface->height;
762 	LeaveCriticalSection(&surface->lock);
763 
764 	XLockDisplay(subsystem->display);
765 	/*
766 	 * Ignore BadMatch error during image capture. The screen size may be
767 	 * changed outside. We will resize to correct resolution at next frame
768 	 */
769 	XSetErrorHandler(x11_shadow_error_handler_for_capture);
770 
771 	if (subsystem->use_xshm)
772 	{
773 		image = subsystem->fb_image;
774 		XCopyArea(subsystem->display, subsystem->root_window, subsystem->fb_pixmap,
775 		          subsystem->xshm_gc, 0, 0, subsystem->width, subsystem->height, 0, 0);
776 
777 		EnterCriticalSection(&surface->lock);
778 		status = shadow_capture_compare(surface->data, surface->scanline, surface->width,
779 		                                surface->height, (BYTE*)&(image->data[surface->width * 4]),
780 		                                image->bytes_per_line, &invalidRect);
781 		LeaveCriticalSection(&surface->lock);
782 	}
783 	else
784 	{
785 		EnterCriticalSection(&surface->lock);
786 		image = XGetImage(subsystem->display, subsystem->root_window, surface->x, surface->y,
787 		                  surface->width, surface->height, AllPlanes, ZPixmap);
788 
789 		if (image)
790 		{
791 			status = shadow_capture_compare(surface->data, surface->scanline, surface->width,
792 			                                surface->height, (BYTE*)image->data,
793 			                                image->bytes_per_line, &invalidRect);
794 		}
795 		LeaveCriticalSection(&surface->lock);
796 		if (!image)
797 		{
798 			/*
799 			 * BadMatch error happened. The size may have been changed again.
800 			 * Give up this frame and we will resize again in next frame
801 			 */
802 			goto fail_capture;
803 		}
804 	}
805 
806 	/* Restore the default error handler */
807 	XSetErrorHandler(NULL);
808 	XSync(subsystem->display, False);
809 	XUnlockDisplay(subsystem->display);
810 
811 	if (status)
812 	{
813 		BOOL empty;
814 		EnterCriticalSection(&surface->lock);
815 		region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
816 		region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
817 		empty = region16_is_empty(&(surface->invalidRegion));
818 		LeaveCriticalSection(&surface->lock);
819 
820 		if (!empty)
821 		{
822 			BOOL success;
823 			EnterCriticalSection(&surface->lock);
824 			extents = region16_extents(&(surface->invalidRegion));
825 			x = extents->left;
826 			y = extents->top;
827 			width = extents->right - extents->left;
828 			height = extents->bottom - extents->top;
829 			success = freerdp_image_copy(surface->data, surface->format, surface->scanline, x, y,
830 			                             width, height, (BYTE*)image->data, PIXEL_FORMAT_BGRX32,
831 			                             image->bytes_per_line, x, y, NULL, FREERDP_FLIP_NONE);
832 			LeaveCriticalSection(&surface->lock);
833 			if (!success)
834 				goto fail_capture;
835 
836 			// x11_shadow_blend_cursor(subsystem);
837 			count = ArrayList_Count(server->clients);
838 			shadow_subsystem_frame_update(&subsystem->common);
839 
840 			if (count == 1)
841 			{
842 				rdpShadowClient* client;
843 				client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
844 
845 				if (client)
846 					subsystem->common.captureFrameRate =
847 					    shadow_encoder_preferred_fps(client->encoder);
848 			}
849 
850 			EnterCriticalSection(&surface->lock);
851 			region16_clear(&(surface->invalidRegion));
852 			LeaveCriticalSection(&surface->lock);
853 		}
854 	}
855 
856 	rc = 1;
857 fail_capture:
858 	if (!subsystem->use_xshm && image)
859 		XDestroyImage(image);
860 
861 	if (rc != 1)
862 	{
863 		XSetErrorHandler(NULL);
864 		XSync(subsystem->display, False);
865 		XUnlockDisplay(subsystem->display);
866 	}
867 
868 	return rc;
869 }
870 
x11_shadow_subsystem_process_message(x11ShadowSubsystem * subsystem,wMessage * message)871 static int x11_shadow_subsystem_process_message(x11ShadowSubsystem* subsystem, wMessage* message)
872 {
873 	switch (message->id)
874 	{
875 		case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
876 			shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
877 			break;
878 
879 		default:
880 			WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", message->id);
881 			break;
882 	}
883 
884 	if (message->Free)
885 		message->Free(message);
886 
887 	return 1;
888 }
889 
x11_shadow_subsystem_thread(LPVOID arg)890 static DWORD WINAPI x11_shadow_subsystem_thread(LPVOID arg)
891 {
892 	x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)arg;
893 	XEvent xevent;
894 	DWORD status;
895 	DWORD nCount;
896 	UINT64 cTime;
897 	DWORD dwTimeout;
898 	DWORD dwInterval;
899 	UINT64 frameTime;
900 	HANDLE events[32];
901 	wMessage message;
902 	wMessagePipe* MsgPipe;
903 	MsgPipe = subsystem->common.MsgPipe;
904 	nCount = 0;
905 	events[nCount++] = subsystem->common.event;
906 	events[nCount++] = MessageQueue_Event(MsgPipe->In);
907 	subsystem->common.captureFrameRate = 16;
908 	dwInterval = 1000 / subsystem->common.captureFrameRate;
909 	frameTime = GetTickCount64() + dwInterval;
910 
911 	while (1)
912 	{
913 		cTime = GetTickCount64();
914 		dwTimeout = (cTime > frameTime) ? 0 : frameTime - cTime;
915 		status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
916 
917 		if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
918 		{
919 			if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
920 			{
921 				if (message.id == WMQ_QUIT)
922 					break;
923 
924 				x11_shadow_subsystem_process_message(subsystem, &message);
925 			}
926 		}
927 
928 		if (WaitForSingleObject(subsystem->common.event, 0) == WAIT_OBJECT_0)
929 		{
930 			XLockDisplay(subsystem->display);
931 
932 			if (XEventsQueued(subsystem->display, QueuedAlready))
933 			{
934 				XNextEvent(subsystem->display, &xevent);
935 				x11_shadow_handle_xevent(subsystem, &xevent);
936 			}
937 
938 			XUnlockDisplay(subsystem->display);
939 		}
940 
941 		if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
942 		{
943 			x11_shadow_check_resize(subsystem);
944 			x11_shadow_screen_grab(subsystem);
945 			x11_shadow_query_cursor(subsystem, FALSE);
946 			dwInterval = 1000 / subsystem->common.captureFrameRate;
947 			frameTime += dwInterval;
948 		}
949 	}
950 
951 	ExitThread(0);
952 	return 0;
953 }
954 
x11_shadow_subsystem_base_init(x11ShadowSubsystem * subsystem)955 static int x11_shadow_subsystem_base_init(x11ShadowSubsystem* subsystem)
956 {
957 	if (subsystem->display)
958 		return 1; /* initialize once */
959 
960 	if (!getenv("DISPLAY"))
961 		setenv("DISPLAY", ":0", 1);
962 
963 	if (!XInitThreads())
964 		return -1;
965 
966 	subsystem->display = XOpenDisplay(NULL);
967 
968 	if (!subsystem->display)
969 	{
970 		WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
971 		return -1;
972 	}
973 
974 	subsystem->xfds = ConnectionNumber(subsystem->display);
975 	subsystem->number = DefaultScreen(subsystem->display);
976 	subsystem->screen = ScreenOfDisplay(subsystem->display, subsystem->number);
977 	subsystem->depth = DefaultDepthOfScreen(subsystem->screen);
978 	subsystem->width = WidthOfScreen(subsystem->screen);
979 	subsystem->height = HeightOfScreen(subsystem->screen);
980 	subsystem->root_window = RootWindow(subsystem->display, subsystem->number);
981 	return 1;
982 }
983 
x11_shadow_xfixes_init(x11ShadowSubsystem * subsystem)984 static int x11_shadow_xfixes_init(x11ShadowSubsystem* subsystem)
985 {
986 #ifdef WITH_XFIXES
987 	int xfixes_event;
988 	int xfixes_error;
989 	int major, minor;
990 
991 	if (!XFixesQueryExtension(subsystem->display, &xfixes_event, &xfixes_error))
992 		return -1;
993 
994 	if (!XFixesQueryVersion(subsystem->display, &major, &minor))
995 		return -1;
996 
997 	subsystem->xfixes_cursor_notify_event = xfixes_event + XFixesCursorNotify;
998 	XFixesSelectCursorInput(subsystem->display, subsystem->root_window,
999 	                        XFixesDisplayCursorNotifyMask);
1000 	return 1;
1001 #else
1002 	return -1;
1003 #endif
1004 }
1005 
x11_shadow_xinerama_init(x11ShadowSubsystem * subsystem)1006 static int x11_shadow_xinerama_init(x11ShadowSubsystem* subsystem)
1007 {
1008 #ifdef WITH_XINERAMA
1009 	int major, minor;
1010 	int xinerama_event;
1011 	int xinerama_error;
1012 	x11_shadow_subsystem_base_init(subsystem);
1013 
1014 	if (!XineramaQueryExtension(subsystem->display, &xinerama_event, &xinerama_error))
1015 		return -1;
1016 
1017 	if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1018 		return -1;
1019 
1020 	if (!XineramaIsActive(subsystem->display))
1021 		return -1;
1022 
1023 	return 1;
1024 #else
1025 	return -1;
1026 #endif
1027 }
1028 
x11_shadow_xdamage_init(x11ShadowSubsystem * subsystem)1029 static int x11_shadow_xdamage_init(x11ShadowSubsystem* subsystem)
1030 {
1031 #ifdef WITH_XDAMAGE
1032 	int major, minor;
1033 	int damage_event;
1034 	int damage_error;
1035 
1036 	if (!subsystem->use_xfixes)
1037 		return -1;
1038 
1039 	if (!XDamageQueryExtension(subsystem->display, &damage_event, &damage_error))
1040 		return -1;
1041 
1042 	if (!XDamageQueryVersion(subsystem->display, &major, &minor))
1043 		return -1;
1044 
1045 	if (major < 1)
1046 		return -1;
1047 
1048 	subsystem->xdamage_notify_event = damage_event + XDamageNotify;
1049 	subsystem->xdamage =
1050 	    XDamageCreate(subsystem->display, subsystem->root_window, XDamageReportDeltaRectangles);
1051 
1052 	if (!subsystem->xdamage)
1053 		return -1;
1054 
1055 #ifdef WITH_XFIXES
1056 	subsystem->xdamage_region = XFixesCreateRegion(subsystem->display, NULL, 0);
1057 
1058 	if (!subsystem->xdamage_region)
1059 		return -1;
1060 
1061 #endif
1062 	return 1;
1063 #else
1064 	return -1;
1065 #endif
1066 }
1067 
x11_shadow_xshm_init(x11ShadowSubsystem * subsystem)1068 static int x11_shadow_xshm_init(x11ShadowSubsystem* subsystem)
1069 {
1070 	Bool pixmaps;
1071 	int major, minor;
1072 	XGCValues values;
1073 
1074 	if (!XShmQueryExtension(subsystem->display))
1075 		return -1;
1076 
1077 	if (!XShmQueryVersion(subsystem->display, &major, &minor, &pixmaps))
1078 		return -1;
1079 
1080 	if (!pixmaps)
1081 		return -1;
1082 
1083 	subsystem->fb_shm_info.shmid = -1;
1084 	subsystem->fb_shm_info.shmaddr = (char*)-1;
1085 	subsystem->fb_shm_info.readOnly = False;
1086 	subsystem->fb_image =
1087 	    XShmCreateImage(subsystem->display, subsystem->visual, subsystem->depth, ZPixmap, NULL,
1088 	                    &(subsystem->fb_shm_info), subsystem->width, subsystem->height);
1089 
1090 	if (!subsystem->fb_image)
1091 	{
1092 		WLog_ERR(TAG, "XShmCreateImage failed");
1093 		return -1;
1094 	}
1095 
1096 	subsystem->fb_shm_info.shmid =
1097 	    shmget(IPC_PRIVATE, subsystem->fb_image->bytes_per_line * subsystem->fb_image->height,
1098 	           IPC_CREAT | 0600);
1099 
1100 	if (subsystem->fb_shm_info.shmid == -1)
1101 	{
1102 		WLog_ERR(TAG, "shmget failed");
1103 		return -1;
1104 	}
1105 
1106 	subsystem->fb_shm_info.shmaddr = shmat(subsystem->fb_shm_info.shmid, 0, 0);
1107 	subsystem->fb_image->data = subsystem->fb_shm_info.shmaddr;
1108 
1109 	if (subsystem->fb_shm_info.shmaddr == ((char*)-1))
1110 	{
1111 		WLog_ERR(TAG, "shmat failed");
1112 		return -1;
1113 	}
1114 
1115 	if (!XShmAttach(subsystem->display, &(subsystem->fb_shm_info)))
1116 		return -1;
1117 
1118 	XSync(subsystem->display, False);
1119 	shmctl(subsystem->fb_shm_info.shmid, IPC_RMID, 0);
1120 	subsystem->fb_pixmap =
1121 	    XShmCreatePixmap(subsystem->display, subsystem->root_window, subsystem->fb_image->data,
1122 	                     &(subsystem->fb_shm_info), subsystem->fb_image->width,
1123 	                     subsystem->fb_image->height, subsystem->fb_image->depth);
1124 	XSync(subsystem->display, False);
1125 
1126 	if (!subsystem->fb_pixmap)
1127 		return -1;
1128 
1129 	values.subwindow_mode = IncludeInferiors;
1130 	values.graphics_exposures = False;
1131 	subsystem->xshm_gc = XCreateGC(subsystem->display, subsystem->root_window,
1132 	                               GCSubwindowMode | GCGraphicsExposures, &values);
1133 	XSetFunction(subsystem->display, subsystem->xshm_gc, GXcopy);
1134 	XSync(subsystem->display, False);
1135 	return 1;
1136 }
1137 
x11_shadow_enum_monitors(MONITOR_DEF * monitors,UINT32 maxMonitors)1138 UINT32 x11_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
1139 {
1140 	int index;
1141 	Display* display;
1142 	int displayWidth;
1143 	int displayHeight;
1144 	int numMonitors = 0;
1145 	MONITOR_DEF* monitor;
1146 
1147 	if (!getenv("DISPLAY"))
1148 		setenv("DISPLAY", ":0", 1);
1149 
1150 	display = XOpenDisplay(NULL);
1151 
1152 	if (!display)
1153 	{
1154 		WLog_ERR(TAG, "failed to open display: %s", XDisplayName(NULL));
1155 		return -1;
1156 	}
1157 
1158 	displayWidth = WidthOfScreen(DefaultScreenOfDisplay(display));
1159 	displayHeight = HeightOfScreen(DefaultScreenOfDisplay(display));
1160 #ifdef WITH_XINERAMA
1161 	{
1162 		int major, minor;
1163 		int xinerama_event;
1164 		int xinerama_error;
1165 		XineramaScreenInfo* screen;
1166 		XineramaScreenInfo* screens;
1167 
1168 		if (XineramaQueryExtension(display, &xinerama_event, &xinerama_error) &&
1169 		    XDamageQueryVersion(display, &major, &minor) && XineramaIsActive(display))
1170 		{
1171 			screens = XineramaQueryScreens(display, &numMonitors);
1172 
1173 			if (numMonitors > (INT64)maxMonitors)
1174 				numMonitors = (int)maxMonitors;
1175 
1176 			if (screens && (numMonitors > 0))
1177 			{
1178 				for (index = 0; index < numMonitors; index++)
1179 				{
1180 					screen = &screens[index];
1181 					monitor = &monitors[index];
1182 					monitor->left = screen->x_org;
1183 					monitor->top = screen->y_org;
1184 					monitor->right = monitor->left + screen->width;
1185 					monitor->bottom = monitor->top + screen->height;
1186 					monitor->flags = (index == 0) ? 1 : 0;
1187 				}
1188 			}
1189 
1190 			XFree(screens);
1191 		}
1192 	}
1193 #endif
1194 	XCloseDisplay(display);
1195 
1196 	if (numMonitors < 1)
1197 	{
1198 		index = 0;
1199 		numMonitors = 1;
1200 		monitor = &monitors[index];
1201 		monitor->left = 0;
1202 		monitor->top = 0;
1203 		monitor->right = displayWidth;
1204 		monitor->bottom = displayHeight;
1205 		monitor->flags = 1;
1206 	}
1207 
1208 	errno = 0;
1209 	return numMonitors;
1210 }
1211 
x11_shadow_subsystem_init(rdpShadowSubsystem * sub)1212 static int x11_shadow_subsystem_init(rdpShadowSubsystem* sub)
1213 {
1214 	int i;
1215 	int pf_count;
1216 	int vi_count;
1217 	int nextensions;
1218 	char** extensions;
1219 	XVisualInfo* vi;
1220 	XVisualInfo* vis;
1221 	XVisualInfo template;
1222 	XPixmapFormatValues* pf;
1223 	XPixmapFormatValues* pfs;
1224 	MONITOR_DEF* virtualScreen;
1225 	x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1226 
1227 	if (!subsystem)
1228 		return -1;
1229 
1230 	subsystem->common.numMonitors = x11_shadow_enum_monitors(subsystem->common.monitors, 16);
1231 	x11_shadow_subsystem_base_init(subsystem);
1232 
1233 	if ((subsystem->depth != 24) && (subsystem->depth != 32))
1234 	{
1235 		WLog_ERR(TAG, "unsupported X11 server color depth: %d", subsystem->depth);
1236 		return -1;
1237 	}
1238 
1239 	extensions = XListExtensions(subsystem->display, &nextensions);
1240 
1241 	if (!extensions || (nextensions < 0))
1242 		return -1;
1243 
1244 	for (i = 0; i < nextensions; i++)
1245 	{
1246 		if (strcmp(extensions[i], "Composite") == 0)
1247 			subsystem->composite = TRUE;
1248 	}
1249 
1250 	XFreeExtensionList(extensions);
1251 
1252 	if (subsystem->composite)
1253 		subsystem->use_xdamage = FALSE;
1254 
1255 	pfs = XListPixmapFormats(subsystem->display, &pf_count);
1256 
1257 	if (!pfs)
1258 	{
1259 		WLog_ERR(TAG, "XListPixmapFormats failed");
1260 		return -1;
1261 	}
1262 
1263 	for (i = 0; i < pf_count; i++)
1264 	{
1265 		pf = pfs + i;
1266 
1267 		if (pf->depth == (INT64)subsystem->depth)
1268 		{
1269 			subsystem->bpp = pf->bits_per_pixel;
1270 			subsystem->scanline_pad = pf->scanline_pad;
1271 			break;
1272 		}
1273 	}
1274 
1275 	XFree(pfs);
1276 	ZeroMemory(&template, sizeof(template));
1277 	template.class = TrueColor;
1278 	template.screen = subsystem->number;
1279 	vis = XGetVisualInfo(subsystem->display, VisualClassMask | VisualScreenMask, &template,
1280 	                     &vi_count);
1281 
1282 	if (!vis)
1283 	{
1284 		WLog_ERR(TAG, "XGetVisualInfo failed");
1285 		return -1;
1286 	}
1287 
1288 	for (i = 0; i < vi_count; i++)
1289 	{
1290 		vi = vis + i;
1291 
1292 		if (vi->depth == (INT64)subsystem->depth)
1293 		{
1294 			subsystem->visual = vi->visual;
1295 			break;
1296 		}
1297 	}
1298 
1299 	XFree(vis);
1300 	XSelectInput(subsystem->display, subsystem->root_window, SubstructureNotifyMask);
1301 	subsystem->cursorMaxWidth = 256;
1302 	subsystem->cursorMaxHeight = 256;
1303 	subsystem->cursorPixels =
1304 	    _aligned_malloc(subsystem->cursorMaxWidth * subsystem->cursorMaxHeight * 4, 16);
1305 
1306 	if (!subsystem->cursorPixels)
1307 		return -1;
1308 
1309 	x11_shadow_query_cursor(subsystem, TRUE);
1310 
1311 	if (subsystem->use_xfixes)
1312 	{
1313 		if (x11_shadow_xfixes_init(subsystem) < 0)
1314 			subsystem->use_xfixes = FALSE;
1315 	}
1316 
1317 	if (subsystem->use_xinerama)
1318 	{
1319 		if (x11_shadow_xinerama_init(subsystem) < 0)
1320 			subsystem->use_xinerama = FALSE;
1321 	}
1322 
1323 	if (subsystem->use_xshm)
1324 	{
1325 		if (x11_shadow_xshm_init(subsystem) < 0)
1326 			subsystem->use_xshm = FALSE;
1327 	}
1328 
1329 	if (subsystem->use_xdamage)
1330 	{
1331 		if (x11_shadow_xdamage_init(subsystem) < 0)
1332 			subsystem->use_xdamage = FALSE;
1333 	}
1334 
1335 	if (!(subsystem->common.event =
1336 	          CreateFileDescriptorEvent(NULL, FALSE, FALSE, subsystem->xfds, WINPR_FD_READ)))
1337 		return -1;
1338 
1339 	virtualScreen = &(subsystem->common.virtualScreen);
1340 	virtualScreen->left = 0;
1341 	virtualScreen->top = 0;
1342 	virtualScreen->right = subsystem->width;
1343 	virtualScreen->bottom = subsystem->height;
1344 	virtualScreen->flags = 1;
1345 	WLog_INFO(TAG,
1346 	          "X11 Extensions: XFixes: %" PRId32 " Xinerama: %" PRId32 " XDamage: %" PRId32
1347 	          " XShm: %" PRId32 "",
1348 	          subsystem->use_xfixes, subsystem->use_xinerama, subsystem->use_xdamage,
1349 	          subsystem->use_xshm);
1350 	return 1;
1351 }
1352 
x11_shadow_subsystem_uninit(rdpShadowSubsystem * sub)1353 static int x11_shadow_subsystem_uninit(rdpShadowSubsystem* sub)
1354 {
1355 	x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1356 
1357 	if (!subsystem)
1358 		return -1;
1359 
1360 	if (subsystem->display)
1361 	{
1362 		XCloseDisplay(subsystem->display);
1363 		subsystem->display = NULL;
1364 	}
1365 
1366 	if (subsystem->common.event)
1367 	{
1368 		CloseHandle(subsystem->common.event);
1369 		subsystem->common.event = NULL;
1370 	}
1371 
1372 	if (subsystem->cursorPixels)
1373 	{
1374 		_aligned_free(subsystem->cursorPixels);
1375 		subsystem->cursorPixels = NULL;
1376 	}
1377 
1378 	return 1;
1379 }
1380 
x11_shadow_subsystem_start(rdpShadowSubsystem * sub)1381 static int x11_shadow_subsystem_start(rdpShadowSubsystem* sub)
1382 {
1383 	x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1384 
1385 	if (!subsystem)
1386 		return -1;
1387 
1388 	if (!(subsystem->thread =
1389 	          CreateThread(NULL, 0, x11_shadow_subsystem_thread, (void*)subsystem, 0, NULL)))
1390 	{
1391 		WLog_ERR(TAG, "Failed to create thread");
1392 		return -1;
1393 	}
1394 
1395 	return 1;
1396 }
1397 
x11_shadow_subsystem_stop(rdpShadowSubsystem * sub)1398 static int x11_shadow_subsystem_stop(rdpShadowSubsystem* sub)
1399 {
1400 	x11ShadowSubsystem* subsystem = (x11ShadowSubsystem*)sub;
1401 
1402 	if (!subsystem)
1403 		return -1;
1404 
1405 	if (subsystem->thread)
1406 	{
1407 		if (MessageQueue_PostQuit(subsystem->common.MsgPipe->In, 0))
1408 			WaitForSingleObject(subsystem->thread, INFINITE);
1409 
1410 		CloseHandle(subsystem->thread);
1411 		subsystem->thread = NULL;
1412 	}
1413 
1414 	return 1;
1415 }
1416 
x11_shadow_subsystem_new(void)1417 static rdpShadowSubsystem* x11_shadow_subsystem_new(void)
1418 {
1419 	x11ShadowSubsystem* subsystem;
1420 	subsystem = (x11ShadowSubsystem*)calloc(1, sizeof(x11ShadowSubsystem));
1421 
1422 	if (!subsystem)
1423 		return NULL;
1424 
1425 #ifdef WITH_PAM
1426 	subsystem->common.Authenticate = x11_shadow_pam_authenticate;
1427 #endif
1428 	subsystem->common.SynchronizeEvent = x11_shadow_input_synchronize_event;
1429 	subsystem->common.KeyboardEvent = x11_shadow_input_keyboard_event;
1430 	subsystem->common.UnicodeKeyboardEvent = x11_shadow_input_unicode_keyboard_event;
1431 	subsystem->common.MouseEvent = x11_shadow_input_mouse_event;
1432 	subsystem->common.ExtendedMouseEvent = x11_shadow_input_extended_mouse_event;
1433 	subsystem->composite = FALSE;
1434 	subsystem->use_xshm = FALSE; /* temporarily disabled */
1435 	subsystem->use_xfixes = TRUE;
1436 	subsystem->use_xdamage = FALSE;
1437 	subsystem->use_xinerama = TRUE;
1438 	return (rdpShadowSubsystem*)subsystem;
1439 }
1440 
x11_shadow_subsystem_free(rdpShadowSubsystem * subsystem)1441 static void x11_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
1442 {
1443 	if (!subsystem)
1444 		return;
1445 
1446 	x11_shadow_subsystem_uninit(subsystem);
1447 	free(subsystem);
1448 }
1449 
X11_ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS * pEntryPoints)1450 FREERDP_API int X11_ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
1451 {
1452 	if (!pEntryPoints)
1453 		return -1;
1454 
1455 	pEntryPoints->New = x11_shadow_subsystem_new;
1456 	pEntryPoints->Free = x11_shadow_subsystem_free;
1457 	pEntryPoints->Init = x11_shadow_subsystem_init;
1458 	pEntryPoints->Uninit = x11_shadow_subsystem_uninit;
1459 	pEntryPoints->Start = x11_shadow_subsystem_start;
1460 	pEntryPoints->Stop = x11_shadow_subsystem_stop;
1461 	pEntryPoints->EnumMonitors = x11_shadow_enum_monitors;
1462 	return 1;
1463 }
1464