• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2011 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include "config.h"
27 
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <inttypes.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <sys/socket.h>
34 #include <sys/un.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #include <signal.h>
39 #include <limits.h>
40 #include <assert.h>
41 #include <X11/Xcursor/Xcursor.h>
42 #include <linux/input.h>
43 
44 #include <libweston/libweston.h>
45 #include "xwayland.h"
46 #include "xwayland-internal-interface.h"
47 
48 #include "shared/cairo-util.h"
49 #include "hash.h"
50 #include "shared/helpers.h"
51 
52 struct wm_size_hints {
53 	uint32_t flags;
54 	int32_t x, y;
55 	int32_t width, height;	/* should set so old wm's don't mess up */
56 	int32_t min_width, min_height;
57 	int32_t max_width, max_height;
58 	int32_t width_inc, height_inc;
59 	struct {
60 		int32_t x;
61 		int32_t y;
62 	} min_aspect, max_aspect;
63 	int32_t base_width, base_height;
64 	int32_t win_gravity;
65 };
66 
67 #define USPosition	(1L << 0)
68 #define USSize		(1L << 1)
69 #define PPosition	(1L << 2)
70 #define PSize		(1L << 3)
71 #define PMinSize	(1L << 4)
72 #define PMaxSize	(1L << 5)
73 #define PResizeInc	(1L << 6)
74 #define PAspect		(1L << 7)
75 #define PBaseSize	(1L << 8)
76 #define PWinGravity	(1L << 9)
77 
78 struct motif_wm_hints {
79 	uint32_t flags;
80 	uint32_t functions;
81 	uint32_t decorations;
82 	int32_t input_mode;
83 	uint32_t status;
84 };
85 
86 #define MWM_HINTS_FUNCTIONS     (1L << 0)
87 #define MWM_HINTS_DECORATIONS   (1L << 1)
88 #define MWM_HINTS_INPUT_MODE    (1L << 2)
89 #define MWM_HINTS_STATUS        (1L << 3)
90 
91 #define MWM_FUNC_ALL            (1L << 0)
92 #define MWM_FUNC_RESIZE         (1L << 1)
93 #define MWM_FUNC_MOVE           (1L << 2)
94 #define MWM_FUNC_MINIMIZE       (1L << 3)
95 #define MWM_FUNC_MAXIMIZE       (1L << 4)
96 #define MWM_FUNC_CLOSE          (1L << 5)
97 
98 #define MWM_DECOR_ALL           (1L << 0)
99 #define MWM_DECOR_BORDER        (1L << 1)
100 #define MWM_DECOR_RESIZEH       (1L << 2)
101 #define MWM_DECOR_TITLE         (1L << 3)
102 #define MWM_DECOR_MENU          (1L << 4)
103 #define MWM_DECOR_MINIMIZE      (1L << 5)
104 #define MWM_DECOR_MAXIMIZE      (1L << 6)
105 
106 #define MWM_DECOR_EVERYTHING \
107 	(MWM_DECOR_BORDER | MWM_DECOR_RESIZEH | MWM_DECOR_TITLE | \
108 	 MWM_DECOR_MENU | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE)
109 
110 #define MWM_INPUT_MODELESS 0
111 #define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
112 #define MWM_INPUT_SYSTEM_MODAL 2
113 #define MWM_INPUT_FULL_APPLICATION_MODAL 3
114 #define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL
115 
116 #define MWM_TEAROFF_WINDOW      (1L<<0)
117 
118 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
119 #define _NET_WM_MOVERESIZE_SIZE_TOP          1
120 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
121 #define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
122 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
123 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
124 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
125 #define _NET_WM_MOVERESIZE_SIZE_LEFT         7
126 #define _NET_WM_MOVERESIZE_MOVE              8   /* movement only */
127 #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9   /* size via keyboard */
128 #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10   /* move via keyboard */
129 #define _NET_WM_MOVERESIZE_CANCEL           11   /* cancel operation */
130 
131 struct weston_output_weak_ref {
132 	struct weston_output *output;
133 	struct wl_listener destroy_listener;
134 };
135 
136 struct weston_wm_window {
137 	struct weston_wm *wm;
138 	xcb_window_t id;
139 	xcb_window_t frame_id;
140 	struct frame *frame;
141 	cairo_surface_t *cairo_surface;
142 	uint32_t surface_id;
143 	struct weston_surface *surface;
144 	struct weston_desktop_xwayland_surface *shsurf;
145 	struct wl_listener surface_destroy_listener;
146 	struct wl_event_source *repaint_source;
147 	struct wl_event_source *configure_source;
148 	int properties_dirty;
149 	int pid;
150 	char *machine;
151 	char *class;
152 	char *name;
153 	struct weston_wm_window *transient_for;
154 	uint32_t protocols;
155 	xcb_atom_t type;
156 	int width, height;
157 	int x;
158 	int y;
159 	bool pos_dirty;
160 	int map_request_x;
161 	int map_request_y;
162 	struct weston_output_weak_ref legacy_fullscreen_output;
163 	int saved_width, saved_height;
164 	int decorate;
165 	uint32_t last_button_time;
166 	int did_double;
167 	int override_redirect;
168 	int fullscreen;
169 	int has_alpha;
170 	int delete_window;
171 	int maximized_vert;
172 	int maximized_horz;
173 	struct wm_size_hints size_hints;
174 	struct motif_wm_hints motif_hints;
175 	struct wl_list link;
176 };
177 
178 static void
179 weston_wm_window_set_allow_commits(struct weston_wm_window *window, bool allow);
180 
181 static struct weston_wm_window *
182 get_wm_window(struct weston_surface *surface);
183 
184 static void
185 weston_wm_set_net_active_window(struct weston_wm *wm, xcb_window_t window);
186 
187 static void
188 weston_wm_window_schedule_repaint(struct weston_wm_window *window);
189 
190 static int
191 legacy_fullscreen(struct weston_wm *wm,
192 		  struct weston_wm_window *window,
193 		  struct weston_output **output_ret);
194 
195 static void
196 xserver_map_shell_surface(struct weston_wm_window *window,
197 			  struct weston_surface *surface);
198 
199 static bool
wm_debug_is_enabled(struct weston_wm * wm)200 wm_debug_is_enabled(struct weston_wm *wm)
201 {
202 	return weston_log_scope_is_enabled(wm->server->wm_debug);
203 }
204 
205 static void __attribute__ ((format (printf, 2, 3)))
wm_printf(struct weston_wm * wm,const char * fmt,...)206 wm_printf(struct weston_wm *wm, const char *fmt, ...)
207 {
208 	va_list ap;
209 	char timestr[128];
210 
211 	if (wm_debug_is_enabled(wm))
212 		weston_log_scope_printf(wm->server->wm_debug, "%s ",
213 				weston_log_scope_timestamp(wm->server->wm_debug,
214 				timestr, sizeof timestr));
215 
216 	va_start(ap, fmt);
217 	weston_log_scope_vprintf(wm->server->wm_debug, fmt, ap);
218 	va_end(ap);
219 }
220 static void
weston_output_weak_ref_init(struct weston_output_weak_ref * ref)221 weston_output_weak_ref_init(struct weston_output_weak_ref *ref)
222 {
223 	ref->output = NULL;
224 }
225 
226 static void
weston_output_weak_ref_clear(struct weston_output_weak_ref * ref)227 weston_output_weak_ref_clear(struct weston_output_weak_ref *ref)
228 {
229 	if (!ref->output)
230 		return;
231 
232 	wl_list_remove(&ref->destroy_listener.link);
233 	ref->output = NULL;
234 }
235 
236 static void
weston_output_weak_ref_handle_destroy(struct wl_listener * listener,void * data)237 weston_output_weak_ref_handle_destroy(struct wl_listener *listener, void *data)
238 {
239 	struct weston_output_weak_ref *ref;
240 
241 	ref = wl_container_of(listener, ref, destroy_listener);
242 	assert(ref->output == data);
243 
244 	weston_output_weak_ref_clear(ref);
245 }
246 
247 static void
weston_output_weak_ref_set(struct weston_output_weak_ref * ref,struct weston_output * output)248 weston_output_weak_ref_set(struct weston_output_weak_ref *ref,
249 			   struct weston_output *output)
250 {
251 	weston_output_weak_ref_clear(ref);
252 
253 	if (!output)
254 		return;
255 
256 	ref->destroy_listener.notify = weston_output_weak_ref_handle_destroy;
257 	wl_signal_add(&output->destroy_signal, &ref->destroy_listener);
258 	ref->output = output;
259 }
260 
261 static bool __attribute__ ((warn_unused_result))
wm_lookup_window(struct weston_wm * wm,xcb_window_t hash,struct weston_wm_window ** window)262 wm_lookup_window(struct weston_wm *wm, xcb_window_t hash,
263 		 struct weston_wm_window **window)
264 {
265 	*window = hash_table_lookup(wm->window_hash, hash);
266 	if (*window)
267 		return true;
268 	return false;
269 }
270 
271 const char *
get_atom_name(xcb_connection_t * c,xcb_atom_t atom)272 get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
273 {
274 	xcb_get_atom_name_cookie_t cookie;
275 	xcb_get_atom_name_reply_t *reply;
276 	xcb_generic_error_t *e;
277 	static char buffer[64];
278 
279 	if (atom == XCB_ATOM_NONE)
280 		return "None";
281 
282 	cookie = xcb_get_atom_name (c, atom);
283 	reply = xcb_get_atom_name_reply (c, cookie, &e);
284 
285 	if (reply) {
286 		snprintf(buffer, sizeof buffer, "%.*s",
287 			 xcb_get_atom_name_name_length (reply),
288 			 xcb_get_atom_name_name (reply));
289 	} else {
290 		snprintf(buffer, sizeof buffer, "(atom %u)", atom);
291 	}
292 
293 	free(reply);
294 
295 	return buffer;
296 }
297 
298 static xcb_cursor_t
xcb_cursor_image_load_cursor(struct weston_wm * wm,const XcursorImage * img)299 xcb_cursor_image_load_cursor(struct weston_wm *wm, const XcursorImage *img)
300 {
301 	xcb_connection_t *c = wm->conn;
302 	xcb_screen_iterator_t s = xcb_setup_roots_iterator(xcb_get_setup(c));
303 	xcb_screen_t *screen = s.data;
304 	xcb_gcontext_t gc;
305 	xcb_pixmap_t pix;
306 	xcb_render_picture_t pic;
307 	xcb_cursor_t cursor;
308 	int stride = img->width * 4;
309 
310 	pix = xcb_generate_id(c);
311 	xcb_create_pixmap(c, 32, pix, screen->root, img->width, img->height);
312 
313 	pic = xcb_generate_id(c);
314 	xcb_render_create_picture(c, pic, pix, wm->format_rgba.id, 0, 0);
315 
316 	gc = xcb_generate_id(c);
317 	xcb_create_gc(c, gc, pix, 0, 0);
318 
319 	xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc,
320 		      img->width, img->height, 0, 0, 0, 32,
321 		      stride * img->height, (uint8_t *) img->pixels);
322 	xcb_free_gc(c, gc);
323 
324 	cursor = xcb_generate_id(c);
325 	xcb_render_create_cursor(c, cursor, pic, img->xhot, img->yhot);
326 
327 	xcb_render_free_picture(c, pic);
328 	xcb_free_pixmap(c, pix);
329 
330 	return cursor;
331 }
332 
333 static xcb_cursor_t
xcb_cursor_images_load_cursor(struct weston_wm * wm,const XcursorImages * images)334 xcb_cursor_images_load_cursor(struct weston_wm *wm, const XcursorImages *images)
335 {
336 	/* TODO: treat animated cursors as well */
337 	if (images->nimage != 1)
338 		return -1;
339 
340 	return xcb_cursor_image_load_cursor(wm, images->images[0]);
341 }
342 
343 static xcb_cursor_t
xcb_cursor_library_load_cursor(struct weston_wm * wm,const char * file)344 xcb_cursor_library_load_cursor(struct weston_wm *wm, const char *file)
345 {
346 	xcb_cursor_t cursor;
347 	XcursorImages *images;
348 	char *v = NULL;
349 	int size = 0;
350 
351 	if (!file)
352 		return 0;
353 
354 	v = getenv ("XCURSOR_SIZE");
355 	if (v)
356 		size = atoi(v);
357 
358 	if (!size)
359 		size = 32;
360 
361 	images = XcursorLibraryLoadImages (file, NULL, size);
362 	if (!images)
363 		return -1;
364 
365 	cursor = xcb_cursor_images_load_cursor (wm, images);
366 	XcursorImagesDestroy (images);
367 
368 	return cursor;
369 }
370 
371 static unsigned
dump_cardinal_array_elem(FILE * fp,unsigned format,void * arr,unsigned len,unsigned ind)372 dump_cardinal_array_elem(FILE *fp, unsigned format,
373 			 void *arr, unsigned len, unsigned ind)
374 {
375 	const char *comma;
376 
377 	/* If more than 16 elements, print 0-14, ..., last */
378 	if (ind > 14 && ind < len - 1) {
379 		fprintf(fp, ", ...");
380 		return len - 1;
381 	}
382 
383 	comma = ind ? ", " : "";
384 
385 	switch (format) {
386 	case 32:
387 		fprintf(fp, "%s%" PRIu32, comma, ((uint32_t *)arr)[ind]);
388 		break;
389 	case 16:
390 		fprintf(fp, "%s%" PRIu16, comma, ((uint16_t *)arr)[ind]);
391 		break;
392 	case 8:
393 		fprintf(fp, "%s%" PRIu8, comma, ((uint8_t *)arr)[ind]);
394 		break;
395 	default:
396 		fprintf(fp, "%s???", comma);
397 	}
398 
399 	return ind + 1;
400 }
401 
402 static void
dump_cardinal_array(FILE * fp,xcb_get_property_reply_t * reply)403 dump_cardinal_array(FILE *fp, xcb_get_property_reply_t *reply)
404 {
405 	unsigned i = 0;
406 	void *arr;
407 
408 	assert(reply->type == XCB_ATOM_CARDINAL);
409 
410 	arr = xcb_get_property_value(reply);
411 
412 	fprintf(fp, "[");
413 	while (i < reply->value_len)
414 		i = dump_cardinal_array_elem(fp, reply->format,
415 					     arr, reply->value_len, i);
416 	fprintf(fp, "]");
417 }
418 
419 void
dump_property(FILE * fp,struct weston_wm * wm,xcb_atom_t property,xcb_get_property_reply_t * reply)420 dump_property(FILE *fp, struct weston_wm *wm,
421 	      xcb_atom_t property, xcb_get_property_reply_t *reply)
422 {
423 	int32_t *incr_value;
424 	const char *text_value, *name;
425 	xcb_atom_t *atom_value;
426 	xcb_window_t *window_value;
427 	int width, len;
428 	uint32_t i;
429 
430 	width = fprintf(fp, "%s: ", get_atom_name(wm->conn, property));
431 	if (reply == NULL) {
432 		fprintf(fp, "(no reply)\n");
433 		return;
434 	}
435 
436 	width += fprintf(fp, "%s/%d, length %d (value_len %d): ",
437 			 get_atom_name(wm->conn, reply->type),
438 			 reply->format,
439 			 xcb_get_property_value_length(reply),
440 			 reply->value_len);
441 
442 	if (reply->type == wm->atom.incr) {
443 		incr_value = xcb_get_property_value(reply);
444 		fprintf(fp, "%d\n", *incr_value);
445 	} else if (reply->type == wm->atom.utf8_string ||
446 	           reply->type == wm->atom.string) {
447 		text_value = xcb_get_property_value(reply);
448 		if (reply->value_len > 40)
449 			len = 40;
450 		else
451 			len = reply->value_len;
452 		fprintf(fp, "\"%.*s\"\n", len, text_value);
453 	} else if (reply->type == XCB_ATOM_ATOM) {
454 		atom_value = xcb_get_property_value(reply);
455 		for (i = 0; i < reply->value_len; i++) {
456 			name = get_atom_name(wm->conn, atom_value[i]);
457 			if (width + strlen(name) + 2 > 78) {
458 				fprintf(fp, "\n    ");
459 				width = 4;
460 			} else if (i > 0) {
461 				width +=  fprintf(fp, ", ");
462 			}
463 
464 			width +=  fprintf(fp, "%s", name);
465 		}
466 		fprintf(fp, "\n");
467 	} else if (reply->type == XCB_ATOM_CARDINAL) {
468 		dump_cardinal_array(fp, reply);
469 		fprintf(fp, "\n");
470 	} else if (reply->type == XCB_ATOM_WINDOW && reply->format == 32) {
471 		window_value = xcb_get_property_value(reply);
472 		fprintf(fp, "win %u\n", *window_value);
473 	} else {
474 		fprintf(fp, "huh?\n");
475 	}
476 }
477 
478 static void
read_and_dump_property(FILE * fp,struct weston_wm * wm,xcb_window_t window,xcb_atom_t property)479 read_and_dump_property(FILE *fp, struct weston_wm *wm,
480 		       xcb_window_t window, xcb_atom_t property)
481 {
482 	xcb_get_property_reply_t *reply;
483 	xcb_get_property_cookie_t cookie;
484 
485 	cookie = xcb_get_property(wm->conn, 0, window,
486 				  property, XCB_ATOM_ANY, 0, 2048);
487 	reply = xcb_get_property_reply(wm->conn, cookie, NULL);
488 
489 	dump_property(fp, wm, property, reply);
490 
491 	free(reply);
492 }
493 
494 /* We reuse some predefined, but otherwise useles atoms
495  * as local type placeholders that never touch the X11 server,
496  * to make weston_wm_window_read_properties() less exceptional.
497  */
498 #define TYPE_WM_PROTOCOLS	XCB_ATOM_CUT_BUFFER0
499 #define TYPE_MOTIF_WM_HINTS	XCB_ATOM_CUT_BUFFER1
500 #define TYPE_NET_WM_STATE	XCB_ATOM_CUT_BUFFER2
501 #define TYPE_WM_NORMAL_HINTS	XCB_ATOM_CUT_BUFFER3
502 
503 static void
weston_wm_window_read_properties(struct weston_wm_window * window)504 weston_wm_window_read_properties(struct weston_wm_window *window)
505 {
506 	struct weston_wm *wm = window->wm;
507 
508 #define F(field) (&window->field)
509 	const struct {
510 		xcb_atom_t atom;
511 		xcb_atom_t type;
512 		void *ptr;
513 	} props[] = {
514 		{ XCB_ATOM_WM_CLASS,           XCB_ATOM_STRING,            F(class) },
515 		{ XCB_ATOM_WM_NAME,            XCB_ATOM_STRING,            F(name) },
516 		{ XCB_ATOM_WM_TRANSIENT_FOR,   XCB_ATOM_WINDOW,            F(transient_for) },
517 		{ wm->atom.wm_protocols,       TYPE_WM_PROTOCOLS,          NULL },
518 		{ wm->atom.wm_normal_hints,    TYPE_WM_NORMAL_HINTS,       NULL },
519 		{ wm->atom.net_wm_state,       TYPE_NET_WM_STATE,          NULL },
520 		{ wm->atom.net_wm_window_type, XCB_ATOM_ATOM,              F(type) },
521 		{ wm->atom.net_wm_name,        XCB_ATOM_STRING,            F(name) },
522 		{ wm->atom.net_wm_pid,         XCB_ATOM_CARDINAL,          F(pid) },
523 		{ wm->atom.motif_wm_hints,     TYPE_MOTIF_WM_HINTS,        NULL },
524 		{ wm->atom.wm_client_machine,  XCB_ATOM_WM_CLIENT_MACHINE, F(machine) },
525 	};
526 #undef F
527 
528 	xcb_get_property_cookie_t cookie[ARRAY_LENGTH(props)];
529 	xcb_get_property_reply_t *reply;
530 	void *p;
531 	uint32_t *xid;
532 	xcb_atom_t *atom;
533 	uint32_t i;
534 	char name[1024];
535 
536 	if (!window->properties_dirty)
537 		return;
538 	window->properties_dirty = 0;
539 
540 	for (i = 0; i < ARRAY_LENGTH(props); i++)
541 		cookie[i] = xcb_get_property(wm->conn,
542 					     0, /* delete */
543 					     window->id,
544 					     props[i].atom,
545 					     XCB_ATOM_ANY, 0, 2048);
546 
547 	window->decorate = window->override_redirect ? 0 : MWM_DECOR_EVERYTHING;
548 	window->size_hints.flags = 0;
549 	window->motif_hints.flags = 0;
550 	window->delete_window = 0;
551 
552 	for (i = 0; i < ARRAY_LENGTH(props); i++)  {
553 		reply = xcb_get_property_reply(wm->conn, cookie[i], NULL);
554 		if (!reply)
555 			/* Bad window, typically */
556 			continue;
557 		if (reply->type == XCB_ATOM_NONE) {
558 			/* No such property */
559 			free(reply);
560 			continue;
561 		}
562 
563 		p = props[i].ptr;
564 
565 		switch (props[i].type) {
566 		case XCB_ATOM_WM_CLIENT_MACHINE:
567 		case XCB_ATOM_STRING:
568 			/* FIXME: We're using this for both string and
569 			   utf8_string */
570 			if (*(char **) p)
571 				free(*(char **) p);
572 
573 			*(char **) p =
574 				strndup(xcb_get_property_value(reply),
575 					xcb_get_property_value_length(reply));
576 			break;
577 		case XCB_ATOM_WINDOW:
578 			xid = xcb_get_property_value(reply);
579 			if (!wm_lookup_window(wm, *xid, p))
580 				weston_log("XCB_ATOM_WINDOW contains window"
581 					   " id not found in hash table.\n");
582 			break;
583 		case XCB_ATOM_CARDINAL:
584 		case XCB_ATOM_ATOM:
585 			atom = xcb_get_property_value(reply);
586 			*(xcb_atom_t *) p = *atom;
587 			break;
588 		case TYPE_WM_PROTOCOLS:
589 			atom = xcb_get_property_value(reply);
590 			for (i = 0; i < reply->value_len; i++)
591 				if (atom[i] == wm->atom.wm_delete_window) {
592 					window->delete_window = 1;
593 					break;
594 				}
595 			break;
596 		case TYPE_WM_NORMAL_HINTS:
597 			memcpy(&window->size_hints,
598 			       xcb_get_property_value(reply),
599 			       sizeof window->size_hints);
600 			break;
601 		case TYPE_NET_WM_STATE:
602 			window->fullscreen = 0;
603 			atom = xcb_get_property_value(reply);
604 			for (i = 0; i < reply->value_len; i++) {
605 				if (atom[i] == wm->atom.net_wm_state_fullscreen)
606 					window->fullscreen = 1;
607 				if (atom[i] == wm->atom.net_wm_state_maximized_vert)
608 					window->maximized_vert = 1;
609 				if (atom[i] == wm->atom.net_wm_state_maximized_horz)
610 					window->maximized_horz = 1;
611 			}
612 			break;
613 		case TYPE_MOTIF_WM_HINTS:
614 			memcpy(&window->motif_hints,
615 			       xcb_get_property_value(reply),
616 			       sizeof window->motif_hints);
617 			if (window->motif_hints.flags & MWM_HINTS_DECORATIONS) {
618 				if (window->motif_hints.decorations & MWM_DECOR_ALL)
619 					/* MWM_DECOR_ALL means all except the other values listed. */
620 					window->decorate =
621 						MWM_DECOR_EVERYTHING & (~window->motif_hints.decorations);
622 				else
623 					window->decorate =
624 						window->motif_hints.decorations;
625 			}
626 			break;
627 		default:
628 			break;
629 		}
630 		free(reply);
631 	}
632 
633 	if (window->pid > 0) {
634 		gethostname(name, sizeof(name));
635 		for (i = 0; i < sizeof(name); i++) {
636 			if (name[i] == '\0')
637 				break;
638 		}
639 		if (i == sizeof(name))
640 			name[0] = '\0'; /* ignore stupid hostnames */
641 
642 		/* this is only one heuristic to guess the PID of a client is
643 		* valid, assuming it's compliant with icccm and ewmh.
644 		* Non-compliants and remote applications of course fail. */
645 		if (!window->machine || strcmp(window->machine, name))
646 			window->pid = 0;
647 	}
648 }
649 
650 #undef TYPE_WM_PROTOCOLS
651 #undef TYPE_MOTIF_WM_HINTS
652 #undef TYPE_NET_WM_STATE
653 #undef TYPE_WM_NORMAL_HINTS
654 
655 static void
weston_wm_window_get_frame_size(struct weston_wm_window * window,int * width,int * height)656 weston_wm_window_get_frame_size(struct weston_wm_window *window,
657 				int *width, int *height)
658 {
659 	struct theme *t = window->wm->theme;
660 
661 	if (window->fullscreen) {
662 		*width = window->width;
663 		*height = window->height;
664 	} else if (window->decorate && window->frame) {
665 		*width = frame_width(window->frame);
666 		*height = frame_height(window->frame);
667 	} else {
668 		*width = window->width + t->margin * 2;
669 		*height = window->height + t->margin * 2;
670 	}
671 }
672 
673 static void
weston_wm_window_get_child_position(struct weston_wm_window * window,int * x,int * y)674 weston_wm_window_get_child_position(struct weston_wm_window *window,
675 				    int *x, int *y)
676 {
677 	struct theme *t = window->wm->theme;
678 
679 	if (window->fullscreen) {
680 		*x = 0;
681 		*y = 0;
682 	} else if (window->decorate && window->frame) {
683 		frame_interior(window->frame, x, y, NULL, NULL);
684 	} else {
685 		*x = t->margin;
686 		*y = t->margin;
687 	}
688 }
689 
690 static void
weston_wm_window_send_configure_notify(struct weston_wm_window * window)691 weston_wm_window_send_configure_notify(struct weston_wm_window *window)
692 {
693 	xcb_configure_notify_event_t configure_notify;
694 	struct weston_wm *wm = window->wm;
695 	int x, y;
696 
697 	weston_wm_window_get_child_position(window, &x, &y);
698 	configure_notify.response_type = XCB_CONFIGURE_NOTIFY;
699 	configure_notify.pad0 = 0;
700 	configure_notify.event = window->id;
701 	configure_notify.window = window->id;
702 	configure_notify.above_sibling = XCB_WINDOW_NONE;
703 	configure_notify.x = x;
704 	configure_notify.y = y;
705 	configure_notify.width = window->width;
706 	configure_notify.height = window->height;
707 	configure_notify.border_width = 0;
708 	configure_notify.override_redirect = 0;
709 	configure_notify.pad1 = 0;
710 
711 	xcb_send_event(wm->conn, 0, window->id,
712 		       XCB_EVENT_MASK_STRUCTURE_NOTIFY,
713 		       (char *) &configure_notify);
714 }
715 
716 static void
weston_wm_configure_window(struct weston_wm * wm,xcb_window_t window_id,uint16_t mask,const uint32_t * values)717 weston_wm_configure_window(struct weston_wm *wm, xcb_window_t window_id,
718 			   uint16_t mask, const uint32_t *values)
719 {
720 	static const struct {
721 		xcb_config_window_t bitmask;
722 		const char *name;
723 	} names[] = {
724 		{ XCB_CONFIG_WINDOW_X, "x" },
725 		{ XCB_CONFIG_WINDOW_Y, "y" },
726 		{ XCB_CONFIG_WINDOW_WIDTH, "width" },
727 		{ XCB_CONFIG_WINDOW_HEIGHT, "height" },
728 		{ XCB_CONFIG_WINDOW_BORDER_WIDTH, "border_width" },
729 		{ XCB_CONFIG_WINDOW_SIBLING, "sibling" },
730 		{ XCB_CONFIG_WINDOW_STACK_MODE, "stack_mode" },
731 	};
732 	char *buf = NULL;
733 	size_t sz = 0;
734 	FILE *fp;
735 	unsigned i, v;
736 
737 	xcb_configure_window(wm->conn, window_id, mask, values);
738 
739 	if (!wm_debug_is_enabled(wm))
740 		return;
741 
742 	fp = open_memstream(&buf, &sz);
743 	if (!fp)
744 		return;
745 
746 	fprintf(fp, "XWM: configure window %d:", window_id);
747 	for (i = 0, v = 0; i < ARRAY_LENGTH(names); i++) {
748 		if (mask & names[i].bitmask)
749 			fprintf(fp, " %s=%d", names[i].name, values[v++]);
750 	}
751 	fclose(fp);
752 
753 	wm_printf(wm, "%s\n", buf);
754 	free(buf);
755 }
756 
757 static void
weston_wm_window_configure_frame(struct weston_wm_window * window)758 weston_wm_window_configure_frame(struct weston_wm_window *window)
759 {
760 	uint16_t mask;
761 	uint32_t values[2];
762 	int width, height;
763 
764 	if (!window->frame_id)
765 		return;
766 
767 	weston_wm_window_get_frame_size(window, &width, &height);
768 	values[0] = width;
769 	values[1] = height;
770 	mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
771 	weston_wm_configure_window(window->wm, window->frame_id, mask, values);
772 }
773 
774 static void
weston_wm_handle_configure_request(struct weston_wm * wm,xcb_generic_event_t * event)775 weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event)
776 {
777 	xcb_configure_request_event_t *configure_request =
778 		(xcb_configure_request_event_t *) event;
779 	struct weston_wm_window *window;
780 	uint32_t values[16];
781 	uint16_t mask;
782 	int x, y;
783 	int i = 0;
784 
785 	wm_printf(wm, "XCB_CONFIGURE_REQUEST (window %d) %d,%d @ %dx%d\n",
786 		  configure_request->window,
787 		  configure_request->x, configure_request->y,
788 		  configure_request->width, configure_request->height);
789 
790 	if (!wm_lookup_window(wm, configure_request->window, &window))
791 		return;
792 
793 	if (window->fullscreen) {
794 		weston_wm_window_send_configure_notify(window);
795 		return;
796 	}
797 
798 	if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
799 		window->width = configure_request->width;
800 	if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
801 		window->height = configure_request->height;
802 
803 	if (window->frame) {
804 		weston_wm_window_set_allow_commits(window, false);
805 		frame_resize_inside(window->frame, window->width, window->height);
806 	}
807 
808 	weston_wm_window_get_child_position(window, &x, &y);
809 	values[i++] = x;
810 	values[i++] = y;
811 	values[i++] = window->width;
812 	values[i++] = window->height;
813 	values[i++] = 0;
814 	mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
815 		XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
816 		XCB_CONFIG_WINDOW_BORDER_WIDTH;
817 	if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
818 		values[i++] = configure_request->sibling;
819 		mask |= XCB_CONFIG_WINDOW_SIBLING;
820 	}
821 	if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
822 		values[i++] = configure_request->stack_mode;
823 		mask |= XCB_CONFIG_WINDOW_STACK_MODE;
824 	}
825 
826 	weston_wm_configure_window(wm, window->id, mask, values);
827 	weston_wm_window_configure_frame(window);
828 	weston_wm_window_schedule_repaint(window);
829 }
830 
831 static int
our_resource(struct weston_wm * wm,uint32_t id)832 our_resource(struct weston_wm *wm, uint32_t id)
833 {
834 	const xcb_setup_t *setup;
835 
836 	setup = xcb_get_setup(wm->conn);
837 
838 	return (id & ~setup->resource_id_mask) == setup->resource_id_base;
839 }
840 
841 static void
weston_wm_handle_configure_notify(struct weston_wm * wm,xcb_generic_event_t * event)842 weston_wm_handle_configure_notify(struct weston_wm *wm, xcb_generic_event_t *event)
843 {
844 	xcb_configure_notify_event_t *configure_notify =
845 		(xcb_configure_notify_event_t *) event;
846 	const struct weston_desktop_xwayland_interface *xwayland_api =
847 		wm->server->compositor->xwayland_interface;
848 	struct weston_wm_window *window;
849 
850 	wm_printf(wm, "XCB_CONFIGURE_NOTIFY (window %d) %d,%d @ %dx%d%s\n",
851 		  configure_notify->window,
852 		  configure_notify->x, configure_notify->y,
853 		  configure_notify->width, configure_notify->height,
854 		  configure_notify->override_redirect ? ", override" : "");
855 
856 	if (!wm_lookup_window(wm, configure_notify->window, &window))
857 		return;
858 
859 	window->x = configure_notify->x;
860 	window->y = configure_notify->y;
861 	window->pos_dirty = false;
862 
863 	if (window->override_redirect) {
864 		window->width = configure_notify->width;
865 		window->height = configure_notify->height;
866 		if (window->frame)
867 			frame_resize_inside(window->frame,
868 					    window->width, window->height);
869 
870 		/* We should check if shsurf has been created because sometimes
871 		 * there are races
872 		 * (configure_notify is sent before xserver_map_surface) */
873 		if (window->shsurf)
874 			xwayland_api->set_xwayland(window->shsurf,
875 						   window->x, window->y);
876 	}
877 }
878 
879 static void
weston_wm_kill_client(struct wl_listener * listener,void * data)880 weston_wm_kill_client(struct wl_listener *listener, void *data)
881 {
882 	struct weston_surface *surface = data;
883 	struct weston_wm_window *window = get_wm_window(surface);
884 	if (!window)
885 		return;
886 
887 	if (window->pid > 0)
888 		kill(window->pid, SIGKILL);
889 }
890 
891 static void
weston_wm_create_surface(struct wl_listener * listener,void * data)892 weston_wm_create_surface(struct wl_listener *listener, void *data)
893 {
894 	struct weston_surface *surface = data;
895 	struct weston_wm *wm =
896 		container_of(listener,
897 			     struct weston_wm, create_surface_listener);
898 	struct weston_wm_window *window;
899 
900 	if (wl_resource_get_client(surface->resource) != wm->server->client)
901 		return;
902 
903 	wm_printf(wm, "XWM: create weston_surface %p\n", surface);
904 
905 	wl_list_for_each(window, &wm->unpaired_window_list, link)
906 		if (window->surface_id ==
907 		    wl_resource_get_id(surface->resource)) {
908 			xserver_map_shell_surface(window, surface);
909 			window->surface_id = 0;
910 			wl_list_remove(&window->link);
911 			break;
912 		}
913 }
914 
915 static void
weston_wm_send_focus_window(struct weston_wm * wm,struct weston_wm_window * window)916 weston_wm_send_focus_window(struct weston_wm *wm,
917 			    struct weston_wm_window *window)
918 {
919 	xcb_client_message_event_t client_message;
920 
921 	if (window) {
922 		uint32_t values[1];
923 
924 		if (window->override_redirect)
925 			return;
926 
927 		client_message.response_type = XCB_CLIENT_MESSAGE;
928 		client_message.format = 32;
929 		client_message.window = window->id;
930 		client_message.type = wm->atom.wm_protocols;
931 		client_message.data.data32[0] = wm->atom.wm_take_focus;
932 		client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
933 
934 		xcb_send_event(wm->conn, 0, window->id,
935 			       XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
936 			       (char *) &client_message);
937 
938 		xcb_set_input_focus (wm->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
939 				     window->id, XCB_TIME_CURRENT_TIME);
940 
941 		values[0] = XCB_STACK_MODE_ABOVE;
942 		weston_wm_configure_window(wm, window->frame_id,
943 					   XCB_CONFIG_WINDOW_STACK_MODE,
944 					   values);
945 	} else {
946 		xcb_set_input_focus (wm->conn,
947 				     XCB_INPUT_FOCUS_POINTER_ROOT,
948 				     XCB_NONE,
949 				     XCB_TIME_CURRENT_TIME);
950 	}
951 }
952 
953 static void
weston_wm_window_activate(struct wl_listener * listener,void * data)954 weston_wm_window_activate(struct wl_listener *listener, void *data)
955 {
956 	struct weston_surface_activation_data *activation_data = data;
957 	struct weston_surface *surface = activation_data->surface;
958 	struct weston_wm_window *window = NULL;
959 	struct weston_wm *wm =
960 		container_of(listener, struct weston_wm, activate_listener);
961 
962 	if (surface) {
963 		window = get_wm_window(surface);
964 	}
965 
966 	if (window) {
967 		weston_wm_set_net_active_window(wm, window->id);
968 	} else {
969 		weston_wm_set_net_active_window(wm, XCB_WINDOW_NONE);
970 	}
971 
972 	weston_wm_send_focus_window(wm, window);
973 
974 	if (wm->focus_window) {
975 		if (wm->focus_window->frame)
976 			frame_unset_flag(wm->focus_window->frame, FRAME_FLAG_ACTIVE);
977 		weston_wm_window_schedule_repaint(wm->focus_window);
978 	}
979 	wm->focus_window = window;
980 	if (wm->focus_window) {
981 		if (wm->focus_window->frame)
982 			frame_set_flag(wm->focus_window->frame, FRAME_FLAG_ACTIVE);
983 		weston_wm_window_schedule_repaint(wm->focus_window);
984 	}
985 
986 	xcb_flush(wm->conn);
987 
988 }
989 
990 /** Control Xwayland wl_surface.commit behaviour
991  *
992  * This function sets the "_XWAYLAND_ALLOW_COMMITS" property of the frame window
993  * (not the content window!) to \p allow.
994  *
995  * If the property is set to \c true, Xwayland will commit whenever it likes.
996  * If the property is set to \c false, Xwayland will not commit.
997  * If the property is not set at all, Xwayland assumes it is \c true.
998  *
999  * \param window The XWM window to control.
1000  * \param allow Whether Xwayland is allowed to wl_surface.commit for the window.
1001  */
1002 static void
weston_wm_window_set_allow_commits(struct weston_wm_window * window,bool allow)1003 weston_wm_window_set_allow_commits(struct weston_wm_window *window, bool allow)
1004 {
1005 	struct weston_wm *wm = window->wm;
1006 	uint32_t property[1];
1007 
1008 	assert(window->frame_id != XCB_WINDOW_NONE);
1009 
1010 	wm_printf(wm, "XWM: window %d set _XWAYLAND_ALLOW_COMMITS = %s\n",
1011 		  window->id, allow ? "true" : "false");
1012 
1013 	property[0] = allow ? 1 : 0;
1014 
1015 	xcb_change_property(wm->conn,
1016 			    XCB_PROP_MODE_REPLACE,
1017 			    window->frame_id,
1018 			    wm->atom.allow_commits,
1019 			    XCB_ATOM_CARDINAL,
1020 			    32, /* format */
1021 			    1, property);
1022 	xcb_flush(wm->conn);
1023 }
1024 
1025 #define ICCCM_WITHDRAWN_STATE	0
1026 #define ICCCM_NORMAL_STATE	1
1027 #define ICCCM_ICONIC_STATE	3
1028 
1029 static void
weston_wm_window_set_wm_state(struct weston_wm_window * window,int32_t state)1030 weston_wm_window_set_wm_state(struct weston_wm_window *window, int32_t state)
1031 {
1032 	struct weston_wm *wm = window->wm;
1033 	uint32_t property[2];
1034 
1035 	property[0] = state;
1036 	property[1] = XCB_WINDOW_NONE;
1037 
1038 	xcb_change_property(wm->conn,
1039 			    XCB_PROP_MODE_REPLACE,
1040 			    window->id,
1041 			    wm->atom.wm_state,
1042 			    wm->atom.wm_state,
1043 			    32, /* format */
1044 			    2, property);
1045 }
1046 
1047 static void
weston_wm_window_set_net_wm_state(struct weston_wm_window * window)1048 weston_wm_window_set_net_wm_state(struct weston_wm_window *window)
1049 {
1050 	struct weston_wm *wm = window->wm;
1051 	uint32_t property[3];
1052 	int i;
1053 
1054 	i = 0;
1055 	if (window->fullscreen)
1056 		property[i++] = wm->atom.net_wm_state_fullscreen;
1057 	if (window->maximized_vert)
1058 		property[i++] = wm->atom.net_wm_state_maximized_vert;
1059 	if (window->maximized_horz)
1060 		property[i++] = wm->atom.net_wm_state_maximized_horz;
1061 
1062 	xcb_change_property(wm->conn,
1063 			    XCB_PROP_MODE_REPLACE,
1064 			    window->id,
1065 			    wm->atom.net_wm_state,
1066 			    XCB_ATOM_ATOM,
1067 			    32, /* format */
1068 			    i, property);
1069 }
1070 
1071 static void
weston_wm_window_create_frame(struct weston_wm_window * window)1072 weston_wm_window_create_frame(struct weston_wm_window *window)
1073 {
1074 	struct weston_wm *wm = window->wm;
1075 	uint32_t values[3];
1076 	int x, y, width, height;
1077 	int buttons = FRAME_BUTTON_CLOSE;
1078 
1079 	if (window->decorate & MWM_DECOR_MAXIMIZE)
1080 		buttons |= FRAME_BUTTON_MAXIMIZE;
1081 
1082 	window->frame = frame_create(window->wm->theme,
1083 				     window->width, window->height,
1084 				     buttons, window->name, NULL);
1085 
1086 	if (!window->frame)
1087 		return;
1088 
1089 	frame_resize_inside(window->frame, window->width, window->height);
1090 
1091 	weston_wm_window_get_frame_size(window, &width, &height);
1092 	weston_wm_window_get_child_position(window, &x, &y);
1093 
1094 	values[0] = wm->screen->black_pixel;
1095 	values[1] =
1096 		XCB_EVENT_MASK_KEY_PRESS |
1097 		XCB_EVENT_MASK_KEY_RELEASE |
1098 		XCB_EVENT_MASK_BUTTON_PRESS |
1099 		XCB_EVENT_MASK_BUTTON_RELEASE |
1100 		XCB_EVENT_MASK_POINTER_MOTION |
1101 		XCB_EVENT_MASK_ENTER_WINDOW |
1102 		XCB_EVENT_MASK_LEAVE_WINDOW |
1103 		XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
1104 		XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
1105 	values[2] = wm->colormap;
1106 
1107 	window->frame_id = xcb_generate_id(wm->conn);
1108 	xcb_create_window(wm->conn,
1109 			  32,
1110 			  window->frame_id,
1111 			  wm->screen->root,
1112 			  0, 0,
1113 			  width, height,
1114 			  0,
1115 			  XCB_WINDOW_CLASS_INPUT_OUTPUT,
1116 			  wm->visual_id,
1117 			  XCB_CW_BORDER_PIXEL |
1118 			  XCB_CW_EVENT_MASK |
1119 			  XCB_CW_COLORMAP, values);
1120 
1121 	xcb_reparent_window(wm->conn, window->id, window->frame_id, x, y);
1122 
1123 	values[0] = 0;
1124 	weston_wm_configure_window(wm, window->id,
1125 				   XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
1126 
1127 	window->cairo_surface =
1128 		cairo_xcb_surface_create_with_xrender_format(wm->conn,
1129 							     wm->screen,
1130 							     window->frame_id,
1131 							     &wm->format_rgba,
1132 							     width, height);
1133 
1134 	hash_table_insert(wm->window_hash, window->frame_id, window);
1135 }
1136 
1137 /*
1138  * Sets the _NET_WM_DESKTOP property for the window to 'desktop'.
1139  * Passing a <0 desktop value deletes the property.
1140  */
1141 static void
weston_wm_window_set_virtual_desktop(struct weston_wm_window * window,int desktop)1142 weston_wm_window_set_virtual_desktop(struct weston_wm_window *window,
1143 				     int desktop)
1144 {
1145 	if (desktop >= 0) {
1146 		xcb_change_property(window->wm->conn,
1147 		                    XCB_PROP_MODE_REPLACE,
1148 		                    window->id,
1149 		                    window->wm->atom.net_wm_desktop,
1150 		                    XCB_ATOM_CARDINAL,
1151 		                    32, /* format */
1152 		                    1, &desktop);
1153 	} else {
1154 		xcb_delete_property(window->wm->conn,
1155 		                    window->id,
1156 		                    window->wm->atom.net_wm_desktop);
1157 	}
1158 }
1159 
1160 static void
weston_wm_handle_map_request(struct weston_wm * wm,xcb_generic_event_t * event)1161 weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
1162 {
1163 	xcb_map_request_event_t *map_request =
1164 		(xcb_map_request_event_t *) event;
1165 	struct weston_wm_window *window;
1166 	struct weston_output *output;
1167 
1168 	if (our_resource(wm, map_request->window)) {
1169 		wm_printf(wm, "XCB_MAP_REQUEST (window %d, ours)\n",
1170 			  map_request->window);
1171 		return;
1172 	}
1173 
1174 	if (!wm_lookup_window(wm, map_request->window, &window))
1175 		return;
1176 
1177 	weston_wm_window_read_properties(window);
1178 
1179 	/* For a new Window, MapRequest happens before the Window is realized
1180 	 * in Xwayland. We do the real xcb_map_window() here as a response to
1181 	 * MapRequest. The Window will get realized (wl_surface created in
1182 	 * Wayland and WL_SURFACE_ID sent in X11) when it has been mapped for
1183 	 * real.
1184 	 *
1185 	 * MapRequest only happens for (X11) unmapped Windows. On UnmapNotify,
1186 	 * we reset shsurf to NULL, so even if X11 connection races far ahead
1187 	 * of the Wayland connection and the X11 client is repeatedly mapping
1188 	 * and unmapping, we will never have shsurf set on MapRequest.
1189 	 */
1190 	assert(!window->shsurf);
1191 
1192 	window->map_request_x = window->x;
1193 	window->map_request_y = window->y;
1194 
1195 	if (window->frame_id == XCB_WINDOW_NONE)
1196 		weston_wm_window_create_frame(window); /* sets frame_id */
1197 	assert(window->frame_id != XCB_WINDOW_NONE);
1198 
1199 	wm_printf(wm, "XCB_MAP_REQUEST (window %d, %p, frame %d, %dx%d @ %d,%d)\n",
1200 		  window->id, window, window->frame_id,
1201 		  window->width, window->height,
1202 		  window->map_request_x, window->map_request_y);
1203 
1204 	weston_wm_window_set_allow_commits(window, false);
1205 	weston_wm_window_set_wm_state(window, ICCCM_NORMAL_STATE);
1206 	weston_wm_window_set_net_wm_state(window);
1207 	weston_wm_window_set_virtual_desktop(window, 0);
1208 
1209 	if (legacy_fullscreen(wm, window, &output)) {
1210 		window->fullscreen = 1;
1211 		weston_output_weak_ref_set(&window->legacy_fullscreen_output,
1212 					   output);
1213 	}
1214 
1215 	xcb_map_window(wm->conn, map_request->window);
1216 	xcb_map_window(wm->conn, window->frame_id);
1217 
1218 	/* Mapped in the X server, we can draw immediately.
1219 	 * Cannot set pending state though, no weston_surface until
1220 	 * xserver_map_shell_surface() time. */
1221 	weston_wm_window_schedule_repaint(window);
1222 }
1223 
1224 static void
weston_wm_handle_map_notify(struct weston_wm * wm,xcb_generic_event_t * event)1225 weston_wm_handle_map_notify(struct weston_wm *wm, xcb_generic_event_t *event)
1226 {
1227 	xcb_map_notify_event_t *map_notify = (xcb_map_notify_event_t *) event;
1228 
1229 	if (our_resource(wm, map_notify->window)) {
1230 		wm_printf(wm, "XCB_MAP_NOTIFY (window %d, ours)\n",
1231 			  map_notify->window);
1232 			return;
1233 	}
1234 
1235 	wm_printf(wm, "XCB_MAP_NOTIFY (window %d%s)\n", map_notify->window,
1236 		  map_notify->override_redirect ? ", override" : "");
1237 }
1238 
1239 static void
weston_wm_handle_unmap_notify(struct weston_wm * wm,xcb_generic_event_t * event)1240 weston_wm_handle_unmap_notify(struct weston_wm *wm, xcb_generic_event_t *event)
1241 {
1242 	xcb_unmap_notify_event_t *unmap_notify =
1243 		(xcb_unmap_notify_event_t *) event;
1244 	struct weston_wm_window *window;
1245 
1246 	wm_printf(wm, "XCB_UNMAP_NOTIFY (window %d, event %d%s)\n",
1247 		  unmap_notify->window,
1248 		  unmap_notify->event,
1249 		  our_resource(wm, unmap_notify->window) ? ", ours" : "");
1250 
1251 	if (our_resource(wm, unmap_notify->window))
1252 		return;
1253 
1254 	if (unmap_notify->response_type & SEND_EVENT_MASK)
1255 		/* We just ignore the ICCCM 4.1.4 synthetic unmap notify
1256 		 * as it may come in after we've destroyed the window. */
1257 		return;
1258 
1259 	if (!wm_lookup_window(wm, unmap_notify->window, &window))
1260 		return;
1261 
1262 	if (window->surface_id) {
1263 		/* Make sure we're not on the unpaired surface list or we
1264 		 * could be assigned a surface during surface creation that
1265 		 * was mapped before this unmap request.
1266 		 */
1267 		wl_list_remove(&window->link);
1268 		window->surface_id = 0;
1269 	}
1270 	if (wm->focus_window == window)
1271 		wm->focus_window = NULL;
1272 	if (window->surface)
1273 		wl_list_remove(&window->surface_destroy_listener.link);
1274 	window->surface = NULL;
1275 	window->shsurf = NULL;
1276 
1277 	weston_wm_window_set_wm_state(window, ICCCM_WITHDRAWN_STATE);
1278 	weston_wm_window_set_virtual_desktop(window, -1);
1279 
1280 	xcb_unmap_window(wm->conn, window->frame_id);
1281 }
1282 
1283 static void
weston_wm_window_draw_decoration(struct weston_wm_window * window)1284 weston_wm_window_draw_decoration(struct weston_wm_window *window)
1285 {
1286 	cairo_t *cr;
1287 	int width, height;
1288 	const char *how;
1289 
1290 	weston_wm_window_get_frame_size(window, &width, &height);
1291 
1292 	cairo_xcb_surface_set_size(window->cairo_surface, width, height);
1293 	cr = cairo_create(window->cairo_surface);
1294 
1295 	if (window->fullscreen) {
1296 		how = "fullscreen";
1297 		/* nothing */
1298 	} else if (window->decorate) {
1299 		how = "decorate";
1300 		frame_set_title(window->frame, window->name);
1301 		frame_repaint(window->frame, cr);
1302 	} else {
1303 		how = "shadow";
1304 		cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1305 		cairo_set_source_rgba(cr, 0, 0, 0, 0);
1306 		cairo_paint(cr);
1307 
1308 		render_shadow(cr, window->wm->theme->shadow,
1309 			      2, 2, width + 8, height + 8, 64, 64);
1310 	}
1311 
1312 	wm_printf(window->wm, "XWM: draw decoration, win %d, %s\n",
1313 		  window->id, how);
1314 
1315 	cairo_destroy(cr);
1316 	cairo_surface_flush(window->cairo_surface);
1317 	xcb_flush(window->wm->conn);
1318 }
1319 
1320 static void
weston_wm_window_set_pending_state(struct weston_wm_window * window)1321 weston_wm_window_set_pending_state(struct weston_wm_window *window)
1322 {
1323 	int x, y, width, height;
1324 	int32_t input_x, input_y, input_w, input_h;
1325 	const struct weston_desktop_xwayland_interface *xwayland_interface =
1326 		window->wm->server->compositor->xwayland_interface;
1327 
1328 	if (!window->surface)
1329 		return;
1330 
1331 	weston_wm_window_get_frame_size(window, &width, &height);
1332 	weston_wm_window_get_child_position(window, &x, &y);
1333 
1334 	pixman_region32_fini(&window->surface->pending.opaque);
1335 	if (window->has_alpha) {
1336 		pixman_region32_init(&window->surface->pending.opaque);
1337 	} else {
1338 		/* We leave an extra pixel around the X window area to
1339 		 * make sure we don't sample from the undefined alpha
1340 		 * channel when filtering. */
1341 		pixman_region32_init_rect(&window->surface->pending.opaque,
1342 					  x - 1, y - 1,
1343 					  window->width + 2,
1344 					  window->height + 2);
1345 	}
1346 
1347 	if (window->decorate && !window->fullscreen) {
1348 		frame_input_rect(window->frame, &input_x, &input_y,
1349 				 &input_w, &input_h);
1350 	} else {
1351 		input_x = x;
1352 		input_y = y;
1353 		input_w = width;
1354 		input_h = height;
1355 	}
1356 
1357 	wm_printf(window->wm, "XWM: win %d geometry: %d,%d %dx%d\n",
1358 		  window->id, input_x, input_y, input_w, input_h);
1359 
1360 	pixman_region32_fini(&window->surface->pending.input);
1361 	pixman_region32_init_rect(&window->surface->pending.input,
1362 				  input_x, input_y, input_w, input_h);
1363 
1364 	xwayland_interface->set_window_geometry(window->shsurf,
1365 						input_x, input_y,
1366 						input_w, input_h);
1367 	if (window->name)
1368 		xwayland_interface->set_title(window->shsurf, window->name);
1369 	if (window->pid > 0)
1370 		xwayland_interface->set_pid(window->shsurf, window->pid);
1371 }
1372 
1373 static void
weston_wm_window_do_repaint(void * data)1374 weston_wm_window_do_repaint(void *data)
1375 {
1376 	struct weston_wm_window *window = data;
1377 
1378 	window->repaint_source = NULL;
1379 
1380 	weston_wm_window_set_allow_commits(window, false);
1381 	weston_wm_window_read_properties(window);
1382 
1383 	weston_wm_window_draw_decoration(window);
1384 	weston_wm_window_set_pending_state(window);
1385 	weston_wm_window_set_allow_commits(window, true);
1386 }
1387 
1388 static void
weston_wm_window_set_pending_state_OR(struct weston_wm_window * window)1389 weston_wm_window_set_pending_state_OR(struct weston_wm_window *window)
1390 {
1391 	int width, height;
1392 
1393 	/* for override-redirect windows */
1394 	assert(window->frame_id == XCB_WINDOW_NONE);
1395 
1396 	if (!window->surface)
1397 		return;
1398 
1399 	weston_wm_window_get_frame_size(window, &width, &height);
1400 	pixman_region32_fini(&window->surface->pending.opaque);
1401 	if (window->has_alpha) {
1402 		pixman_region32_init(&window->surface->pending.opaque);
1403 	} else {
1404 		pixman_region32_init_rect(&window->surface->pending.opaque, 0, 0,
1405 					  width, height);
1406 	}
1407 }
1408 
1409 static void
weston_wm_window_schedule_repaint(struct weston_wm_window * window)1410 weston_wm_window_schedule_repaint(struct weston_wm_window *window)
1411 {
1412 	struct weston_wm *wm = window->wm;
1413 
1414 	if (window->frame_id == XCB_WINDOW_NONE) {
1415 		/* Override-redirect windows go through here, but we
1416 		 * cannot assert(window->override_redirect); because
1417 		 * we do not deal with changing OR flag yet.
1418 		 * XXX: handle OR flag changes in message handlers
1419 		 */
1420 		weston_wm_window_set_pending_state_OR(window);
1421 		return;
1422 	}
1423 
1424 	if (window->repaint_source)
1425 		return;
1426 
1427 	wm_printf(wm, "XWM: schedule repaint, win %d\n", window->id);
1428 
1429 	window->repaint_source =
1430 		wl_event_loop_add_idle(wm->server->loop,
1431 				       weston_wm_window_do_repaint, window);
1432 }
1433 
1434 static void
weston_wm_handle_property_notify(struct weston_wm * wm,xcb_generic_event_t * event)1435 weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event)
1436 {
1437 	xcb_property_notify_event_t *property_notify =
1438 		(xcb_property_notify_event_t *) event;
1439 	struct weston_wm_window *window;
1440 	FILE *fp = NULL;
1441 	char *logstr;
1442 	size_t logsize;
1443 	char timestr[128];
1444 
1445 	if (!wm_lookup_window(wm, property_notify->window, &window))
1446 		return;
1447 
1448 	window->properties_dirty = 1;
1449 
1450 	if (wm_debug_is_enabled(wm))
1451 		fp = open_memstream(&logstr, &logsize);
1452 
1453 	if (fp) {
1454 		fprintf(fp, "%s XCB_PROPERTY_NOTIFY: window %d, ",
1455 			weston_log_scope_timestamp(wm->server->wm_debug,
1456 			timestr, sizeof timestr),
1457 			property_notify->window);
1458 		if (property_notify->state == XCB_PROPERTY_DELETE)
1459 			fprintf(fp, "deleted %s\n",
1460 					get_atom_name(wm->conn, property_notify->atom));
1461 		else
1462 			read_and_dump_property(fp, wm, property_notify->window,
1463 					       property_notify->atom);
1464 
1465 		if (fclose(fp) == 0)
1466 			weston_log_scope_write(wm->server->wm_debug,
1467 						 logstr, logsize);
1468 		free(logstr);
1469 	} else {
1470 		/* read_and_dump_property() is a X11 roundtrip.
1471 		 * Mimic it to maintain ordering semantics between debug
1472 		 * and non-debug paths.
1473 		 */
1474 		get_atom_name(wm->conn, property_notify->atom);
1475 	}
1476 
1477 	if (property_notify->atom == wm->atom.net_wm_name ||
1478 	    property_notify->atom == XCB_ATOM_WM_NAME)
1479 		weston_wm_window_schedule_repaint(window);
1480 }
1481 
1482 static void
weston_wm_window_create(struct weston_wm * wm,xcb_window_t id,int width,int height,int x,int y,int override)1483 weston_wm_window_create(struct weston_wm *wm,
1484 			xcb_window_t id, int width, int height, int x, int y, int override)
1485 {
1486 	struct weston_wm_window *window;
1487 	uint32_t values[1];
1488 	xcb_get_geometry_cookie_t geometry_cookie;
1489 	xcb_get_geometry_reply_t *geometry_reply;
1490 
1491 	window = zalloc(sizeof *window);
1492 	if (window == NULL) {
1493 		wm_printf(wm, "failed to allocate window\n");
1494 		return;
1495 	}
1496 
1497 	geometry_cookie = xcb_get_geometry(wm->conn, id);
1498 
1499 	values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE |
1500                     XCB_EVENT_MASK_FOCUS_CHANGE;
1501 	xcb_change_window_attributes(wm->conn, id, XCB_CW_EVENT_MASK, values);
1502 
1503 	window->wm = wm;
1504 	window->id = id;
1505 	window->properties_dirty = 1;
1506 	window->override_redirect = override;
1507 	window->width = width;
1508 	window->height = height;
1509 	window->x = x;
1510 	window->y = y;
1511 	window->pos_dirty = false;
1512 	window->map_request_x = INT_MIN; /* out of range for valid positions */
1513 	window->map_request_y = INT_MIN; /* out of range for valid positions */
1514 	weston_output_weak_ref_init(&window->legacy_fullscreen_output);
1515 
1516 	geometry_reply = xcb_get_geometry_reply(wm->conn, geometry_cookie, NULL);
1517 	/* technically we should use XRender and check the visual format's
1518 	alpha_mask, but checking depth is simpler and works in all known cases */
1519 	if (geometry_reply != NULL)
1520 		window->has_alpha = geometry_reply->depth == 32;
1521 	free(geometry_reply);
1522 
1523 	hash_table_insert(wm->window_hash, id, window);
1524 }
1525 
1526 static void
weston_wm_window_destroy(struct weston_wm_window * window)1527 weston_wm_window_destroy(struct weston_wm_window *window)
1528 {
1529 	struct weston_wm *wm = window->wm;
1530 
1531 	weston_output_weak_ref_clear(&window->legacy_fullscreen_output);
1532 
1533 	if (window->configure_source)
1534 		wl_event_source_remove(window->configure_source);
1535 	if (window->repaint_source)
1536 		wl_event_source_remove(window->repaint_source);
1537 	if (window->cairo_surface)
1538 		cairo_surface_destroy(window->cairo_surface);
1539 
1540 	if (window->frame_id) {
1541 		xcb_reparent_window(wm->conn, window->id, wm->wm_window, 0, 0);
1542 		xcb_destroy_window(wm->conn, window->frame_id);
1543 		weston_wm_window_set_wm_state(window, ICCCM_WITHDRAWN_STATE);
1544 		weston_wm_window_set_virtual_desktop(window, -1);
1545 		hash_table_remove(wm->window_hash, window->frame_id);
1546 		window->frame_id = XCB_WINDOW_NONE;
1547 	}
1548 
1549 	if (window->frame)
1550 		frame_destroy(window->frame);
1551 
1552 	if (window->surface_id)
1553 		wl_list_remove(&window->link);
1554 
1555 	if (window->surface)
1556 		wl_list_remove(&window->surface_destroy_listener.link);
1557 
1558 	hash_table_remove(window->wm->window_hash, window->id);
1559 	free(window);
1560 }
1561 
1562 static void
weston_wm_handle_create_notify(struct weston_wm * wm,xcb_generic_event_t * event)1563 weston_wm_handle_create_notify(struct weston_wm *wm, xcb_generic_event_t *event)
1564 {
1565 	xcb_create_notify_event_t *create_notify =
1566 		(xcb_create_notify_event_t *) event;
1567 
1568 	wm_printf(wm, "XCB_CREATE_NOTIFY (window %d, at (%d, %d), width %d, height %d%s%s)\n",
1569 		  create_notify->window,
1570 		  create_notify->x, create_notify->y,
1571 		  create_notify->width, create_notify->height,
1572 		  create_notify->override_redirect ? ", override" : "",
1573 		  our_resource(wm, create_notify->window) ? ", ours" : "");
1574 
1575 	if (our_resource(wm, create_notify->window))
1576 		return;
1577 
1578 	weston_wm_window_create(wm, create_notify->window,
1579 				create_notify->width, create_notify->height,
1580 				create_notify->x, create_notify->y,
1581 				create_notify->override_redirect);
1582 }
1583 
1584 static void
weston_wm_handle_destroy_notify(struct weston_wm * wm,xcb_generic_event_t * event)1585 weston_wm_handle_destroy_notify(struct weston_wm *wm, xcb_generic_event_t *event)
1586 {
1587 	xcb_destroy_notify_event_t *destroy_notify =
1588 		(xcb_destroy_notify_event_t *) event;
1589 	struct weston_wm_window *window;
1590 
1591 	wm_printf(wm, "XCB_DESTROY_NOTIFY, win %d, event %d%s\n",
1592 		  destroy_notify->window,
1593 		  destroy_notify->event,
1594 		  our_resource(wm, destroy_notify->window) ? ", ours" : "");
1595 
1596 	if (our_resource(wm, destroy_notify->window))
1597 		return;
1598 
1599 	if (!wm_lookup_window(wm, destroy_notify->window, &window))
1600 		return;
1601 
1602 	weston_wm_window_destroy(window);
1603 }
1604 
1605 static void
weston_wm_handle_reparent_notify(struct weston_wm * wm,xcb_generic_event_t * event)1606 weston_wm_handle_reparent_notify(struct weston_wm *wm, xcb_generic_event_t *event)
1607 {
1608 	xcb_reparent_notify_event_t *reparent_notify =
1609 		(xcb_reparent_notify_event_t *) event;
1610 	struct weston_wm_window *window;
1611 
1612 	wm_printf(wm, "XCB_REPARENT_NOTIFY (window %d, parent %d, event %d%s)\n",
1613 		  reparent_notify->window,
1614 		  reparent_notify->parent,
1615 		  reparent_notify->event,
1616 		  reparent_notify->override_redirect ? ", override" : "");
1617 
1618 	if (reparent_notify->parent == wm->screen->root) {
1619 		weston_wm_window_create(wm, reparent_notify->window, 10, 10,
1620 					reparent_notify->x, reparent_notify->y,
1621 					reparent_notify->override_redirect);
1622 	} else if (!our_resource(wm, reparent_notify->parent)) {
1623 		if (!wm_lookup_window(wm, reparent_notify->window, &window))
1624 			return;
1625 		weston_wm_window_destroy(window);
1626 	}
1627 }
1628 
1629 struct weston_seat *
weston_wm_pick_seat(struct weston_wm * wm)1630 weston_wm_pick_seat(struct weston_wm *wm)
1631 {
1632 	struct wl_list *seats = wm->server->compositor->seat_list.next;
1633 	if (wl_list_empty(seats))
1634 		return NULL;
1635 	return container_of(seats, struct weston_seat, link);
1636 }
1637 
1638 static struct weston_seat *
weston_wm_pick_seat_for_window(struct weston_wm_window * window)1639 weston_wm_pick_seat_for_window(struct weston_wm_window *window)
1640 {
1641 	struct weston_wm *wm = window->wm;
1642 	struct weston_seat *seat, *s;
1643 
1644 	seat = NULL;
1645 	wl_list_for_each(s, &wm->server->compositor->seat_list, link) {
1646 		struct weston_pointer *pointer = weston_seat_get_pointer(s);
1647 		struct weston_pointer *old_pointer =
1648 			weston_seat_get_pointer(seat);
1649 
1650 		if (pointer && pointer->focus &&
1651 		    pointer->focus->surface == window->surface &&
1652 		    pointer->button_count > 0 &&
1653 		    (!seat ||
1654 		     pointer->grab_serial -
1655 		     old_pointer->grab_serial < (1 << 30)))
1656 			seat = s;
1657 	}
1658 
1659 	return seat;
1660 }
1661 
1662 static void
weston_wm_window_handle_moveresize(struct weston_wm_window * window,xcb_client_message_event_t * client_message)1663 weston_wm_window_handle_moveresize(struct weston_wm_window *window,
1664 				   xcb_client_message_event_t *client_message)
1665 {
1666 	static const int map[] = {
1667 		THEME_LOCATION_RESIZING_TOP_LEFT,
1668 		THEME_LOCATION_RESIZING_TOP,
1669 		THEME_LOCATION_RESIZING_TOP_RIGHT,
1670 		THEME_LOCATION_RESIZING_RIGHT,
1671 		THEME_LOCATION_RESIZING_BOTTOM_RIGHT,
1672 		THEME_LOCATION_RESIZING_BOTTOM,
1673 		THEME_LOCATION_RESIZING_BOTTOM_LEFT,
1674 		THEME_LOCATION_RESIZING_LEFT
1675 	};
1676 
1677 	struct weston_wm *wm = window->wm;
1678 	struct weston_seat *seat = weston_wm_pick_seat_for_window(window);
1679 	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
1680 	int detail;
1681 	const struct weston_desktop_xwayland_interface *xwayland_interface =
1682 		wm->server->compositor->xwayland_interface;
1683 
1684 	if (!pointer || pointer->button_count != 1
1685 	    || !pointer->focus
1686 	    || pointer->focus->surface != window->surface)
1687 		return;
1688 
1689 	detail = client_message->data.data32[2];
1690 	switch (detail) {
1691 	case _NET_WM_MOVERESIZE_MOVE:
1692 		xwayland_interface->move(window->shsurf, pointer);
1693 		break;
1694 	case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
1695 	case _NET_WM_MOVERESIZE_SIZE_TOP:
1696 	case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
1697 	case _NET_WM_MOVERESIZE_SIZE_RIGHT:
1698 	case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
1699 	case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
1700 	case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
1701 	case _NET_WM_MOVERESIZE_SIZE_LEFT:
1702 		xwayland_interface->resize(window->shsurf, pointer, map[detail]);
1703 		break;
1704 	case _NET_WM_MOVERESIZE_CANCEL:
1705 		break;
1706 	}
1707 }
1708 
1709 #define _NET_WM_STATE_REMOVE	0
1710 #define _NET_WM_STATE_ADD	1
1711 #define _NET_WM_STATE_TOGGLE	2
1712 
1713 static int
update_state(int action,int * state)1714 update_state(int action, int *state)
1715 {
1716 	int new_state, changed;
1717 
1718 	switch (action) {
1719 	case _NET_WM_STATE_REMOVE:
1720 		new_state = 0;
1721 		break;
1722 	case _NET_WM_STATE_ADD:
1723 		new_state = 1;
1724 		break;
1725 	case _NET_WM_STATE_TOGGLE:
1726 		new_state = !*state;
1727 		break;
1728 	default:
1729 		return 0;
1730 	}
1731 
1732 	changed = (*state != new_state);
1733 	*state = new_state;
1734 
1735 	return changed;
1736 }
1737 
1738 static void
1739 weston_wm_window_configure(void *data);
1740 
1741 static void
weston_wm_window_set_toplevel(struct weston_wm_window * window)1742 weston_wm_window_set_toplevel(struct weston_wm_window *window)
1743 {
1744 	const struct weston_desktop_xwayland_interface *xwayland_interface =
1745 		window->wm->server->compositor->xwayland_interface;
1746 
1747 	xwayland_interface->set_toplevel(window->shsurf);
1748 	window->width = window->saved_width;
1749 	window->height = window->saved_height;
1750 	if (window->frame)
1751 		frame_resize_inside(window->frame,
1752 					window->width,
1753 					window->height);
1754 	weston_wm_window_configure(window);
1755 }
1756 
1757 static inline bool
weston_wm_window_is_maximized(struct weston_wm_window * window)1758 weston_wm_window_is_maximized(struct weston_wm_window *window)
1759 {
1760 	return window->maximized_horz && window->maximized_vert;
1761 }
1762 
1763 static void
weston_wm_window_handle_state(struct weston_wm_window * window,xcb_client_message_event_t * client_message)1764 weston_wm_window_handle_state(struct weston_wm_window *window,
1765 			      xcb_client_message_event_t *client_message)
1766 {
1767 	struct weston_wm *wm = window->wm;
1768 	const struct weston_desktop_xwayland_interface *xwayland_interface =
1769 		wm->server->compositor->xwayland_interface;
1770 	uint32_t action, property1, property2;
1771 	int maximized = weston_wm_window_is_maximized(window);
1772 
1773 	action = client_message->data.data32[0];
1774 	property1 = client_message->data.data32[1];
1775 	property2 = client_message->data.data32[2];
1776 
1777 	if ((property1 == wm->atom.net_wm_state_fullscreen ||
1778 	     property2 == wm->atom.net_wm_state_fullscreen) &&
1779 	    update_state(action, &window->fullscreen)) {
1780 		weston_wm_window_set_net_wm_state(window);
1781 		if (window->fullscreen) {
1782 			window->saved_width = window->width;
1783 			window->saved_height = window->height;
1784 
1785 			if (window->shsurf)
1786 				xwayland_interface->set_fullscreen(window->shsurf,
1787 								   NULL);
1788 		} else {
1789 			if (window->shsurf)
1790 				weston_wm_window_set_toplevel(window);
1791 		}
1792 	} else {
1793 		if ((property1 == wm->atom.net_wm_state_maximized_vert ||
1794 		     property2 == wm->atom.net_wm_state_maximized_vert) &&
1795 		    update_state(action, &window->maximized_vert))
1796 			weston_wm_window_set_net_wm_state(window);
1797 		if ((property1 == wm->atom.net_wm_state_maximized_horz ||
1798 		     property2 == wm->atom.net_wm_state_maximized_horz) &&
1799 		    update_state(action, &window->maximized_horz))
1800 			weston_wm_window_set_net_wm_state(window);
1801 
1802 		if (maximized != weston_wm_window_is_maximized(window)) {
1803 			if (weston_wm_window_is_maximized(window)) {
1804 				window->saved_width = window->width;
1805 				window->saved_height = window->height;
1806 
1807 				if (window->shsurf)
1808 					xwayland_interface->set_maximized(window->shsurf);
1809 			} else if (window->shsurf) {
1810 				weston_wm_window_set_toplevel(window);
1811 			}
1812 		}
1813 	}
1814 }
1815 
1816 static void
surface_destroy(struct wl_listener * listener,void * data)1817 surface_destroy(struct wl_listener *listener, void *data)
1818 {
1819 	struct weston_wm_window *window =
1820 		container_of(listener,
1821 			     struct weston_wm_window, surface_destroy_listener);
1822 
1823 	wm_printf(window->wm, "surface for xid %d destroyed\n", window->id);
1824 
1825 	/* This should have been freed by the shell.
1826 	 * Don't try to use it later. */
1827 	window->shsurf = NULL;
1828 	window->surface = NULL;
1829 }
1830 
1831 static void
weston_wm_window_handle_surface_id(struct weston_wm_window * window,xcb_client_message_event_t * client_message)1832 weston_wm_window_handle_surface_id(struct weston_wm_window *window,
1833 				   xcb_client_message_event_t *client_message)
1834 {
1835 	struct weston_wm *wm = window->wm;
1836 	struct wl_resource *resource;
1837 
1838 	if (window->surface_id != 0) {
1839 		wm_printf(wm, "already have surface id for window %d\n",
1840 			  window->id);
1841 		return;
1842 	}
1843 
1844 	/* Xwayland will send the wayland requests to create the
1845 	 * wl_surface before sending this client message.  Even so, we
1846 	 * can end up handling the X event before the wayland requests
1847 	 * and thus when we try to look up the surface ID, the surface
1848 	 * hasn't been created yet.  In that case put the window on
1849 	 * the unpaired window list and continue when the surface gets
1850 	 * created. */
1851 	uint32_t id = client_message->data.data32[0];
1852 	resource = wl_client_get_object(wm->server->client, id);
1853 	if (resource) {
1854 		window->surface_id = 0;
1855 		xserver_map_shell_surface(window,
1856 					  wl_resource_get_user_data(resource));
1857 	}
1858 	else {
1859 		window->surface_id = id;
1860 		wl_list_insert(&wm->unpaired_window_list, &window->link);
1861 	}
1862 }
1863 
1864 static void
weston_wm_handle_client_message(struct weston_wm * wm,xcb_generic_event_t * event)1865 weston_wm_handle_client_message(struct weston_wm *wm,
1866 				xcb_generic_event_t *event)
1867 {
1868 	xcb_client_message_event_t *client_message =
1869 		(xcb_client_message_event_t *) event;
1870 	struct weston_wm_window *window;
1871 
1872 	wm_printf(wm, "XCB_CLIENT_MESSAGE (%s %d %d %d %d %d win %d)\n",
1873 		  get_atom_name(wm->conn, client_message->type),
1874 		  client_message->data.data32[0],
1875 		  client_message->data.data32[1],
1876 		  client_message->data.data32[2],
1877 		  client_message->data.data32[3],
1878 		  client_message->data.data32[4],
1879 		  client_message->window);
1880 
1881 	/* The window may get created and destroyed before we actually
1882 	 * handle the message.  If it doesn't exist, bail.
1883 	 */
1884 	if (!wm_lookup_window(wm, client_message->window, &window))
1885 		return;
1886 
1887 	if (client_message->type == wm->atom.net_wm_moveresize)
1888 		weston_wm_window_handle_moveresize(window, client_message);
1889 	else if (client_message->type == wm->atom.net_wm_state)
1890 		weston_wm_window_handle_state(window, client_message);
1891 	else if (client_message->type == wm->atom.wl_surface_id)
1892 		weston_wm_window_handle_surface_id(window, client_message);
1893 }
1894 
1895 enum cursor_type {
1896 	XWM_CURSOR_TOP,
1897 	XWM_CURSOR_BOTTOM,
1898 	XWM_CURSOR_LEFT,
1899 	XWM_CURSOR_RIGHT,
1900 	XWM_CURSOR_TOP_LEFT,
1901 	XWM_CURSOR_TOP_RIGHT,
1902 	XWM_CURSOR_BOTTOM_LEFT,
1903 	XWM_CURSOR_BOTTOM_RIGHT,
1904 	XWM_CURSOR_LEFT_PTR,
1905 };
1906 
1907 /*
1908  * The following correspondences between file names and cursors was copied
1909  * from: https://bugs.kde.org/attachment.cgi?id=67313
1910  */
1911 
1912 static const char *bottom_left_corners[] = {
1913 	"bottom_left_corner",
1914 	"sw-resize",
1915 	"size_bdiag"
1916 };
1917 
1918 static const char *bottom_right_corners[] = {
1919 	"bottom_right_corner",
1920 	"se-resize",
1921 	"size_fdiag"
1922 };
1923 
1924 static const char *bottom_sides[] = {
1925 	"bottom_side",
1926 	"s-resize",
1927 	"size_ver"
1928 };
1929 
1930 static const char *left_ptrs[] = {
1931 	"left_ptr",
1932 	"default",
1933 	"top_left_arrow",
1934 	"left-arrow"
1935 };
1936 
1937 static const char *left_sides[] = {
1938 	"left_side",
1939 	"w-resize",
1940 	"size_hor"
1941 };
1942 
1943 static const char *right_sides[] = {
1944 	"right_side",
1945 	"e-resize",
1946 	"size_hor"
1947 };
1948 
1949 static const char *top_left_corners[] = {
1950 	"top_left_corner",
1951 	"nw-resize",
1952 	"size_fdiag"
1953 };
1954 
1955 static const char *top_right_corners[] = {
1956 	"top_right_corner",
1957 	"ne-resize",
1958 	"size_bdiag"
1959 };
1960 
1961 static const char *top_sides[] = {
1962 	"top_side",
1963 	"n-resize",
1964 	"size_ver"
1965 };
1966 
1967 struct cursor_alternatives {
1968 	const char **names;
1969 	size_t count;
1970 };
1971 
1972 static const struct cursor_alternatives cursors[] = {
1973 	{top_sides, ARRAY_LENGTH(top_sides)},
1974 	{bottom_sides, ARRAY_LENGTH(bottom_sides)},
1975 	{left_sides, ARRAY_LENGTH(left_sides)},
1976 	{right_sides, ARRAY_LENGTH(right_sides)},
1977 	{top_left_corners, ARRAY_LENGTH(top_left_corners)},
1978 	{top_right_corners, ARRAY_LENGTH(top_right_corners)},
1979 	{bottom_left_corners, ARRAY_LENGTH(bottom_left_corners)},
1980 	{bottom_right_corners, ARRAY_LENGTH(bottom_right_corners)},
1981 	{left_ptrs, ARRAY_LENGTH(left_ptrs)},
1982 };
1983 
1984 static void
weston_wm_create_cursors(struct weston_wm * wm)1985 weston_wm_create_cursors(struct weston_wm *wm)
1986 {
1987 	const char *name;
1988 	int i, count = ARRAY_LENGTH(cursors);
1989 	size_t j;
1990 
1991 	wm->cursors = malloc(count * sizeof(xcb_cursor_t));
1992 	for (i = 0; i < count; i++) {
1993 		for (j = 0; j < cursors[i].count; j++) {
1994 			name = cursors[i].names[j];
1995 			wm->cursors[i] =
1996 				xcb_cursor_library_load_cursor(wm, name);
1997 			if (wm->cursors[i] != (xcb_cursor_t)-1)
1998 				break;
1999 		}
2000 	}
2001 
2002 	wm->last_cursor = -1;
2003 }
2004 
2005 static void
weston_wm_destroy_cursors(struct weston_wm * wm)2006 weston_wm_destroy_cursors(struct weston_wm *wm)
2007 {
2008 	uint8_t i;
2009 
2010 	for (i = 0; i < ARRAY_LENGTH(cursors); i++)
2011 		xcb_free_cursor(wm->conn, wm->cursors[i]);
2012 
2013 	free(wm->cursors);
2014 }
2015 
2016 static int
get_cursor_for_location(enum theme_location location)2017 get_cursor_for_location(enum theme_location location)
2018 {
2019 	switch (location) {
2020 		case THEME_LOCATION_RESIZING_TOP:
2021 			return XWM_CURSOR_TOP;
2022 		case THEME_LOCATION_RESIZING_BOTTOM:
2023 			return XWM_CURSOR_BOTTOM;
2024 		case THEME_LOCATION_RESIZING_LEFT:
2025 			return XWM_CURSOR_LEFT;
2026 		case THEME_LOCATION_RESIZING_RIGHT:
2027 			return XWM_CURSOR_RIGHT;
2028 		case THEME_LOCATION_RESIZING_TOP_LEFT:
2029 			return XWM_CURSOR_TOP_LEFT;
2030 		case THEME_LOCATION_RESIZING_TOP_RIGHT:
2031 			return XWM_CURSOR_TOP_RIGHT;
2032 		case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
2033 			return XWM_CURSOR_BOTTOM_LEFT;
2034 		case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
2035 			return XWM_CURSOR_BOTTOM_RIGHT;
2036 		case THEME_LOCATION_EXTERIOR:
2037 		case THEME_LOCATION_TITLEBAR:
2038 		default:
2039 			return XWM_CURSOR_LEFT_PTR;
2040 	}
2041 }
2042 
2043 static void
weston_wm_window_set_cursor(struct weston_wm * wm,xcb_window_t window_id,int cursor)2044 weston_wm_window_set_cursor(struct weston_wm *wm, xcb_window_t window_id,
2045 			    int cursor)
2046 {
2047 	uint32_t cursor_value_list;
2048 
2049 	if (wm->last_cursor == cursor)
2050 		return;
2051 
2052 	wm->last_cursor = cursor;
2053 
2054 	cursor_value_list = wm->cursors[cursor];
2055 	xcb_change_window_attributes (wm->conn, window_id,
2056 				      XCB_CW_CURSOR, &cursor_value_list);
2057 	xcb_flush(wm->conn);
2058 }
2059 
2060 static void
weston_wm_window_close(struct weston_wm_window * window,xcb_timestamp_t time)2061 weston_wm_window_close(struct weston_wm_window *window, xcb_timestamp_t time)
2062 {
2063 	xcb_client_message_event_t client_message;
2064 
2065 	if (window->delete_window) {
2066 		client_message.response_type = XCB_CLIENT_MESSAGE;
2067 		client_message.format = 32;
2068 		client_message.window = window->id;
2069 		client_message.type = window->wm->atom.wm_protocols;
2070 		client_message.data.data32[0] =
2071 			window->wm->atom.wm_delete_window;
2072 		client_message.data.data32[1] = time;
2073 
2074 		xcb_send_event(window->wm->conn, 0, window->id,
2075 			       XCB_EVENT_MASK_NO_EVENT,
2076 			       (char *) &client_message);
2077 	} else {
2078 		xcb_kill_client(window->wm->conn, window->id);
2079 	}
2080 }
2081 
2082 #define DOUBLE_CLICK_PERIOD 250
2083 static void
weston_wm_handle_button(struct weston_wm * wm,xcb_generic_event_t * event)2084 weston_wm_handle_button(struct weston_wm *wm, xcb_generic_event_t *event)
2085 {
2086 	xcb_button_press_event_t *button = (xcb_button_press_event_t *) event;
2087 	const struct weston_desktop_xwayland_interface *xwayland_interface =
2088 		wm->server->compositor->xwayland_interface;
2089 	struct weston_seat *seat;
2090 	struct weston_pointer *pointer;
2091 	struct weston_wm_window *window;
2092 	enum theme_location location;
2093 	enum wl_pointer_button_state button_state;
2094 	uint32_t button_id;
2095 	uint32_t double_click = 0;
2096 
2097 	wm_printf(wm, "XCB_BUTTON_%s (detail %d)\n",
2098 		  button->response_type == XCB_BUTTON_PRESS ?
2099 		  "PRESS" : "RELEASE", button->detail);
2100 
2101 	if (!wm_lookup_window(wm, button->event, &window) ||
2102 	    !window->decorate)
2103 		return;
2104 
2105 	if (button->detail != 1 && button->detail != 2)
2106 		return;
2107 
2108 	seat = weston_wm_pick_seat_for_window(window);
2109 	pointer = weston_seat_get_pointer(seat);
2110 
2111 	button_state = button->response_type == XCB_BUTTON_PRESS ?
2112 		WL_POINTER_BUTTON_STATE_PRESSED :
2113 		WL_POINTER_BUTTON_STATE_RELEASED;
2114 	button_id = button->detail == 1 ? BTN_LEFT : BTN_RIGHT;
2115 
2116 	if (button_state == WL_POINTER_BUTTON_STATE_PRESSED) {
2117 		if (button->time - window->last_button_time <= DOUBLE_CLICK_PERIOD) {
2118 			double_click = 1;
2119 			window->did_double = 1;
2120 		} else
2121 			window->did_double = 0;
2122 
2123 		window->last_button_time = button->time;
2124 	} else if (window->did_double == 1) {
2125 		double_click = 1;
2126 		window->did_double = 0;
2127 	}
2128 
2129 	/* Make sure we're looking at the right location.  The frame
2130 	 * could have received a motion event from a pointer from a
2131 	 * different wl_seat, but under X it looks like our core
2132 	 * pointer moved.  Move the frame pointer to the button press
2133 	 * location before deciding what to do. */
2134 	location = frame_pointer_motion(window->frame, NULL,
2135 					button->event_x, button->event_y);
2136 	if (double_click)
2137 		location = frame_double_click(window->frame, NULL,
2138 					      button_id, button_state);
2139 	else
2140 		location = frame_pointer_button(window->frame, NULL,
2141 						button_id, button_state);
2142 
2143 	if (frame_status(window->frame) & FRAME_STATUS_REPAINT)
2144 		weston_wm_window_schedule_repaint(window);
2145 
2146 	if (frame_status(window->frame) & FRAME_STATUS_MOVE) {
2147 		if (pointer)
2148 			xwayland_interface->move(window->shsurf, pointer);
2149 		frame_status_clear(window->frame, FRAME_STATUS_MOVE);
2150 	}
2151 
2152 	if (frame_status(window->frame) & FRAME_STATUS_RESIZE) {
2153 		if (pointer)
2154 			xwayland_interface->resize(window->shsurf, pointer, location);
2155 		frame_status_clear(window->frame, FRAME_STATUS_RESIZE);
2156 	}
2157 
2158 	if (frame_status(window->frame) & FRAME_STATUS_CLOSE) {
2159 		weston_wm_window_close(window, button->time);
2160 		frame_status_clear(window->frame, FRAME_STATUS_CLOSE);
2161 	}
2162 
2163 	if (frame_status(window->frame) & FRAME_STATUS_MAXIMIZE) {
2164 		window->maximized_horz = !window->maximized_horz;
2165 		window->maximized_vert = !window->maximized_vert;
2166 		if (weston_wm_window_is_maximized(window)) {
2167 			window->saved_width = window->width;
2168 			window->saved_height = window->height;
2169 			xwayland_interface->set_maximized(window->shsurf);
2170 		} else {
2171 			weston_wm_window_set_toplevel(window);
2172 		}
2173 		frame_status_clear(window->frame, FRAME_STATUS_MAXIMIZE);
2174 	}
2175 }
2176 
2177 static void
weston_wm_handle_motion(struct weston_wm * wm,xcb_generic_event_t * event)2178 weston_wm_handle_motion(struct weston_wm *wm, xcb_generic_event_t *event)
2179 {
2180 	xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *) event;
2181 	struct weston_wm_window *window;
2182 	enum theme_location location;
2183 	int cursor;
2184 
2185 	if (!wm_lookup_window(wm, motion->event, &window) ||
2186 	    !window->decorate)
2187 		return;
2188 
2189 	location = frame_pointer_motion(window->frame, NULL,
2190 					motion->event_x, motion->event_y);
2191 	if (frame_status(window->frame) & FRAME_STATUS_REPAINT)
2192 		weston_wm_window_schedule_repaint(window);
2193 
2194 	cursor = get_cursor_for_location(location);
2195 	weston_wm_window_set_cursor(wm, window->frame_id, cursor);
2196 }
2197 
2198 static void
weston_wm_handle_enter(struct weston_wm * wm,xcb_generic_event_t * event)2199 weston_wm_handle_enter(struct weston_wm *wm, xcb_generic_event_t *event)
2200 {
2201 	xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *) event;
2202 	struct weston_wm_window *window;
2203 	enum theme_location location;
2204 	int cursor;
2205 
2206 	if (!wm_lookup_window(wm, enter->event, &window) ||
2207 	    !window->decorate)
2208 		return;
2209 
2210 	location = frame_pointer_enter(window->frame, NULL,
2211 				       enter->event_x, enter->event_y);
2212 	if (frame_status(window->frame) & FRAME_STATUS_REPAINT)
2213 		weston_wm_window_schedule_repaint(window);
2214 
2215 	cursor = get_cursor_for_location(location);
2216 	weston_wm_window_set_cursor(wm, window->frame_id, cursor);
2217 }
2218 
2219 static void
weston_wm_handle_leave(struct weston_wm * wm,xcb_generic_event_t * event)2220 weston_wm_handle_leave(struct weston_wm *wm, xcb_generic_event_t *event)
2221 {
2222 	xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *) event;
2223 	struct weston_wm_window *window;
2224 
2225 	if (!wm_lookup_window(wm, leave->event, &window) ||
2226 	    !window->decorate)
2227 		return;
2228 
2229 	frame_pointer_leave(window->frame, NULL);
2230 	if (frame_status(window->frame) & FRAME_STATUS_REPAINT)
2231 		weston_wm_window_schedule_repaint(window);
2232 
2233 	weston_wm_window_set_cursor(wm, window->frame_id, XWM_CURSOR_LEFT_PTR);
2234 }
2235 
2236 static void
weston_wm_handle_focus_in(struct weston_wm * wm,xcb_generic_event_t * event)2237 weston_wm_handle_focus_in(struct weston_wm *wm, xcb_generic_event_t *event)
2238 {
2239 	xcb_focus_in_event_t *focus = (xcb_focus_in_event_t *) event;
2240 
2241 	/* Do not interfere with grabs */
2242 	if (focus->mode == XCB_NOTIFY_MODE_GRAB ||
2243 	    focus->mode == XCB_NOTIFY_MODE_UNGRAB)
2244 		return;
2245 
2246 	/* Do not let X clients change the focus behind the compositor's
2247 	 * back. Reset the focus to the old one if it changed. */
2248 	if (!wm->focus_window || focus->event != wm->focus_window->id)
2249 		weston_wm_send_focus_window(wm, wm->focus_window);
2250 }
2251 
2252 static int
weston_wm_handle_event(int fd,uint32_t mask,void * data)2253 weston_wm_handle_event(int fd, uint32_t mask, void *data)
2254 {
2255 	struct weston_wm *wm = data;
2256 	xcb_generic_event_t *event;
2257 	int count = 0;
2258 
2259 	while (event = xcb_poll_for_event(wm->conn), event != NULL) {
2260 		if (weston_wm_handle_selection_event(wm, event)) {
2261 			free(event);
2262 			count++;
2263 			continue;
2264 		}
2265 
2266 		if (weston_wm_handle_dnd_event(wm, event)) {
2267 			free(event);
2268 			count++;
2269 			continue;
2270 		}
2271 
2272 		switch (EVENT_TYPE(event)) {
2273 		case XCB_BUTTON_PRESS:
2274 		case XCB_BUTTON_RELEASE:
2275 			weston_wm_handle_button(wm, event);
2276 			break;
2277 		case XCB_ENTER_NOTIFY:
2278 			weston_wm_handle_enter(wm, event);
2279 			break;
2280 		case XCB_LEAVE_NOTIFY:
2281 			weston_wm_handle_leave(wm, event);
2282 			break;
2283 		case XCB_MOTION_NOTIFY:
2284 			weston_wm_handle_motion(wm, event);
2285 			break;
2286 		case XCB_CREATE_NOTIFY:
2287 			weston_wm_handle_create_notify(wm, event);
2288 			break;
2289 		case XCB_MAP_REQUEST:
2290 			weston_wm_handle_map_request(wm, event);
2291 			break;
2292 		case XCB_MAP_NOTIFY:
2293 			weston_wm_handle_map_notify(wm, event);
2294 			break;
2295 		case XCB_UNMAP_NOTIFY:
2296 			weston_wm_handle_unmap_notify(wm, event);
2297 			break;
2298 		case XCB_REPARENT_NOTIFY:
2299 			weston_wm_handle_reparent_notify(wm, event);
2300 			break;
2301 		case XCB_CONFIGURE_REQUEST:
2302 			weston_wm_handle_configure_request(wm, event);
2303 			break;
2304 		case XCB_CONFIGURE_NOTIFY:
2305 			weston_wm_handle_configure_notify(wm, event);
2306 			break;
2307 		case XCB_DESTROY_NOTIFY:
2308 			weston_wm_handle_destroy_notify(wm, event);
2309 			break;
2310 		case XCB_MAPPING_NOTIFY:
2311 			wm_printf(wm, "XCB_MAPPING_NOTIFY\n");
2312 			break;
2313 		case XCB_PROPERTY_NOTIFY:
2314 			weston_wm_handle_property_notify(wm, event);
2315 			break;
2316 		case XCB_CLIENT_MESSAGE:
2317 			weston_wm_handle_client_message(wm, event);
2318 			break;
2319 		case XCB_FOCUS_IN:
2320 			weston_wm_handle_focus_in(wm, event);
2321 			break;
2322 		}
2323 
2324 		free(event);
2325 		count++;
2326 	}
2327 
2328 	if (count != 0)
2329 		xcb_flush(wm->conn);
2330 
2331 	return count;
2332 }
2333 
2334 static void
weston_wm_set_net_active_window(struct weston_wm * wm,xcb_window_t window)2335 weston_wm_set_net_active_window(struct weston_wm *wm, xcb_window_t window) {
2336 	xcb_change_property(wm->conn, XCB_PROP_MODE_REPLACE,
2337 			wm->screen->root, wm->atom.net_active_window,
2338 			wm->atom.window, 32, 1, &window);
2339 }
2340 
2341 static void
weston_wm_get_visual_and_colormap(struct weston_wm * wm)2342 weston_wm_get_visual_and_colormap(struct weston_wm *wm)
2343 {
2344 	xcb_depth_iterator_t d_iter;
2345 	xcb_visualtype_iterator_t vt_iter;
2346 	xcb_visualtype_t *visualtype;
2347 
2348 	d_iter = xcb_screen_allowed_depths_iterator(wm->screen);
2349 	visualtype = NULL;
2350 	while (d_iter.rem > 0) {
2351 		if (d_iter.data->depth == 32) {
2352 			vt_iter = xcb_depth_visuals_iterator(d_iter.data);
2353 			visualtype = vt_iter.data;
2354 			break;
2355 		}
2356 
2357 		xcb_depth_next(&d_iter);
2358 	}
2359 
2360 	if (visualtype == NULL) {
2361 		weston_log("no 32 bit visualtype\n");
2362 		return;
2363 	}
2364 
2365 	wm->visual_id = visualtype->visual_id;
2366 	wm->colormap = xcb_generate_id(wm->conn);
2367 	xcb_create_colormap(wm->conn, XCB_COLORMAP_ALLOC_NONE,
2368 			    wm->colormap, wm->screen->root, wm->visual_id);
2369 }
2370 
2371 static void
weston_wm_get_resources(struct weston_wm * wm)2372 weston_wm_get_resources(struct weston_wm *wm)
2373 {
2374 
2375 #define F(field) offsetof(struct weston_wm, field)
2376 
2377 	static const struct { const char *name; int offset; } atoms[] = {
2378 		{ "WM_PROTOCOLS",	F(atom.wm_protocols) },
2379 		{ "WM_NORMAL_HINTS",	F(atom.wm_normal_hints) },
2380 		{ "WM_TAKE_FOCUS",	F(atom.wm_take_focus) },
2381 		{ "WM_DELETE_WINDOW",	F(atom.wm_delete_window) },
2382 		{ "WM_STATE",		F(atom.wm_state) },
2383 		{ "WM_S0",		F(atom.wm_s0) },
2384 		{ "WM_CLIENT_MACHINE",	F(atom.wm_client_machine) },
2385 		{ "_NET_WM_CM_S0",	F(atom.net_wm_cm_s0) },
2386 		{ "_NET_WM_NAME",	F(atom.net_wm_name) },
2387 		{ "_NET_WM_PID",	F(atom.net_wm_pid) },
2388 		{ "_NET_WM_ICON",	F(atom.net_wm_icon) },
2389 		{ "_NET_WM_STATE",	F(atom.net_wm_state) },
2390 		{ "_NET_WM_STATE_MAXIMIZED_VERT", F(atom.net_wm_state_maximized_vert) },
2391 		{ "_NET_WM_STATE_MAXIMIZED_HORZ", F(atom.net_wm_state_maximized_horz) },
2392 		{ "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
2393 		{ "_NET_WM_USER_TIME", F(atom.net_wm_user_time) },
2394 		{ "_NET_WM_ICON_NAME", F(atom.net_wm_icon_name) },
2395 		{ "_NET_WM_DESKTOP", F(atom.net_wm_desktop) },
2396 		{ "_NET_WM_WINDOW_TYPE", F(atom.net_wm_window_type) },
2397 
2398 		{ "_NET_WM_WINDOW_TYPE_DESKTOP", F(atom.net_wm_window_type_desktop) },
2399 		{ "_NET_WM_WINDOW_TYPE_DOCK", F(atom.net_wm_window_type_dock) },
2400 		{ "_NET_WM_WINDOW_TYPE_TOOLBAR", F(atom.net_wm_window_type_toolbar) },
2401 		{ "_NET_WM_WINDOW_TYPE_MENU", F(atom.net_wm_window_type_menu) },
2402 		{ "_NET_WM_WINDOW_TYPE_UTILITY", F(atom.net_wm_window_type_utility) },
2403 		{ "_NET_WM_WINDOW_TYPE_SPLASH", F(atom.net_wm_window_type_splash) },
2404 		{ "_NET_WM_WINDOW_TYPE_DIALOG", F(atom.net_wm_window_type_dialog) },
2405 		{ "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", F(atom.net_wm_window_type_dropdown) },
2406 		{ "_NET_WM_WINDOW_TYPE_POPUP_MENU", F(atom.net_wm_window_type_popup) },
2407 		{ "_NET_WM_WINDOW_TYPE_TOOLTIP", F(atom.net_wm_window_type_tooltip) },
2408 		{ "_NET_WM_WINDOW_TYPE_NOTIFICATION", F(atom.net_wm_window_type_notification) },
2409 		{ "_NET_WM_WINDOW_TYPE_COMBO", F(atom.net_wm_window_type_combo) },
2410 		{ "_NET_WM_WINDOW_TYPE_DND", F(atom.net_wm_window_type_dnd) },
2411 		{ "_NET_WM_WINDOW_TYPE_NORMAL",	F(atom.net_wm_window_type_normal) },
2412 
2413 		{ "_NET_WM_MOVERESIZE", F(atom.net_wm_moveresize) },
2414 		{ "_NET_SUPPORTING_WM_CHECK",
2415 					F(atom.net_supporting_wm_check) },
2416 		{ "_NET_SUPPORTED",     F(atom.net_supported) },
2417 		{ "_NET_ACTIVE_WINDOW",     F(atom.net_active_window) },
2418 		{ "_MOTIF_WM_HINTS",	F(atom.motif_wm_hints) },
2419 		{ "CLIPBOARD",		F(atom.clipboard) },
2420 		{ "CLIPBOARD_MANAGER",	F(atom.clipboard_manager) },
2421 		{ "TARGETS",		F(atom.targets) },
2422 		{ "UTF8_STRING",	F(atom.utf8_string) },
2423 		{ "_WL_SELECTION",	F(atom.wl_selection) },
2424 		{ "INCR",		F(atom.incr) },
2425 		{ "TIMESTAMP",		F(atom.timestamp) },
2426 		{ "MULTIPLE",		F(atom.multiple) },
2427 		{ "UTF8_STRING"	,	F(atom.utf8_string) },
2428 		{ "COMPOUND_TEXT",	F(atom.compound_text) },
2429 		{ "TEXT",		F(atom.text) },
2430 		{ "STRING",		F(atom.string) },
2431 		{ "WINDOW",		F(atom.window) },
2432 		{ "text/plain;charset=utf-8",	F(atom.text_plain_utf8) },
2433 		{ "text/plain",		F(atom.text_plain) },
2434 		{ "XdndSelection",	F(atom.xdnd_selection) },
2435 		{ "XdndAware",		F(atom.xdnd_aware) },
2436 		{ "XdndEnter",		F(atom.xdnd_enter) },
2437 		{ "XdndLeave",		F(atom.xdnd_leave) },
2438 		{ "XdndDrop",		F(atom.xdnd_drop) },
2439 		{ "XdndStatus",		F(atom.xdnd_status) },
2440 		{ "XdndFinished",	F(atom.xdnd_finished) },
2441 		{ "XdndTypeList",	F(atom.xdnd_type_list) },
2442 		{ "XdndActionCopy",	F(atom.xdnd_action_copy) },
2443 		{ "_XWAYLAND_ALLOW_COMMITS",	F(atom.allow_commits) },
2444 		{ "WL_SURFACE_ID",	F(atom.wl_surface_id) }
2445 	};
2446 #undef F
2447 
2448 	xcb_xfixes_query_version_cookie_t xfixes_cookie;
2449 	xcb_xfixes_query_version_reply_t *xfixes_reply;
2450 	xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
2451 	xcb_intern_atom_reply_t *reply;
2452 	xcb_render_query_pict_formats_reply_t *formats_reply;
2453 	xcb_render_query_pict_formats_cookie_t formats_cookie;
2454 	xcb_render_pictforminfo_t *formats;
2455 	uint32_t i;
2456 
2457 	xcb_prefetch_extension_data (wm->conn, &xcb_xfixes_id);
2458 	xcb_prefetch_extension_data (wm->conn, &xcb_composite_id);
2459 
2460 	formats_cookie = xcb_render_query_pict_formats(wm->conn);
2461 
2462 	for (i = 0; i < ARRAY_LENGTH(atoms); i++)
2463 		cookies[i] = xcb_intern_atom (wm->conn, 0,
2464 					      strlen(atoms[i].name),
2465 					      atoms[i].name);
2466 
2467 	for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
2468 		reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL);
2469 		*(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom;
2470 		free(reply);
2471 	}
2472 
2473 	wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id);
2474 	if (!wm->xfixes || !wm->xfixes->present)
2475 		weston_log("xfixes not available\n");
2476 
2477 	xfixes_cookie = xcb_xfixes_query_version(wm->conn,
2478 						 XCB_XFIXES_MAJOR_VERSION,
2479 						 XCB_XFIXES_MINOR_VERSION);
2480 	xfixes_reply = xcb_xfixes_query_version_reply(wm->conn,
2481 						      xfixes_cookie, NULL);
2482 
2483 	weston_log("xfixes version: %d.%d\n",
2484 	       xfixes_reply->major_version, xfixes_reply->minor_version);
2485 
2486 	free(xfixes_reply);
2487 
2488 	formats_reply = xcb_render_query_pict_formats_reply(wm->conn,
2489 							    formats_cookie, 0);
2490 	if (formats_reply == NULL)
2491 		return;
2492 
2493 	formats = xcb_render_query_pict_formats_formats(formats_reply);
2494 	for (i = 0; i < formats_reply->num_formats; i++) {
2495 		if (formats[i].direct.red_mask != 0xff &&
2496 		    formats[i].direct.red_shift != 16)
2497 			continue;
2498 		if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT &&
2499 		    formats[i].depth == 24)
2500 			wm->format_rgb = formats[i];
2501 		if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT &&
2502 		    formats[i].depth == 32 &&
2503 		    formats[i].direct.alpha_mask == 0xff &&
2504 		    formats[i].direct.alpha_shift == 24)
2505 			wm->format_rgba = formats[i];
2506 	}
2507 
2508 	free(formats_reply);
2509 }
2510 
2511 static void
weston_wm_create_wm_window(struct weston_wm * wm)2512 weston_wm_create_wm_window(struct weston_wm *wm)
2513 {
2514 	static const char name[] = "Weston WM";
2515 
2516 	wm->wm_window = xcb_generate_id(wm->conn);
2517 	xcb_create_window(wm->conn,
2518 			  XCB_COPY_FROM_PARENT,
2519 			  wm->wm_window,
2520 			  wm->screen->root,
2521 			  0, 0,
2522 			  10, 10,
2523 			  0,
2524 			  XCB_WINDOW_CLASS_INPUT_OUTPUT,
2525 			  wm->screen->root_visual,
2526 			  0, NULL);
2527 
2528 	xcb_change_property(wm->conn,
2529 			    XCB_PROP_MODE_REPLACE,
2530 			    wm->wm_window,
2531 			    wm->atom.net_supporting_wm_check,
2532 			    XCB_ATOM_WINDOW,
2533 			    32, /* format */
2534 			    1, &wm->wm_window);
2535 
2536 	xcb_change_property(wm->conn,
2537 			    XCB_PROP_MODE_REPLACE,
2538 			    wm->wm_window,
2539 			    wm->atom.net_wm_name,
2540 			    wm->atom.utf8_string,
2541 			    8, /* format */
2542 			    strlen(name), name);
2543 
2544 	xcb_change_property(wm->conn,
2545 			    XCB_PROP_MODE_REPLACE,
2546 			    wm->screen->root,
2547 			    wm->atom.net_supporting_wm_check,
2548 			    XCB_ATOM_WINDOW,
2549 			    32, /* format */
2550 			    1, &wm->wm_window);
2551 
2552 	/* Claim the WM_S0 selection even though we don't support
2553 	 * the --replace functionality. */
2554 	xcb_set_selection_owner(wm->conn,
2555 				wm->wm_window,
2556 				wm->atom.wm_s0,
2557 				XCB_TIME_CURRENT_TIME);
2558 
2559 	xcb_set_selection_owner(wm->conn,
2560 				wm->wm_window,
2561 				wm->atom.net_wm_cm_s0,
2562 				XCB_TIME_CURRENT_TIME);
2563 }
2564 
2565 struct weston_wm *
weston_wm_create(struct weston_xserver * wxs,int fd)2566 weston_wm_create(struct weston_xserver *wxs, int fd)
2567 {
2568 	struct weston_wm *wm;
2569 	struct wl_event_loop *loop;
2570 	xcb_screen_iterator_t s;
2571 	uint32_t values[1];
2572 	xcb_atom_t supported[6];
2573 
2574 	wm = zalloc(sizeof *wm);
2575 	if (wm == NULL)
2576 		return NULL;
2577 
2578 	wm->server = wxs;
2579 	wm->window_hash = hash_table_create();
2580 	if (wm->window_hash == NULL) {
2581 		free(wm);
2582 		return NULL;
2583 	}
2584 
2585 	/* xcb_connect_to_fd takes ownership of the fd. */
2586 	wm->conn = xcb_connect_to_fd(fd, NULL);
2587 	if (xcb_connection_has_error(wm->conn)) {
2588 		weston_log("xcb_connect_to_fd failed\n");
2589 		close(fd);
2590 		hash_table_destroy(wm->window_hash);
2591 		free(wm);
2592 		return NULL;
2593 	}
2594 
2595 	s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn));
2596 	wm->screen = s.data;
2597 
2598 	loop = wl_display_get_event_loop(wxs->wl_display);
2599 	wm->source =
2600 		wl_event_loop_add_fd(loop, fd,
2601 				     WL_EVENT_READABLE,
2602 				     weston_wm_handle_event, wm);
2603 	wl_event_source_check(wm->source);
2604 
2605 	weston_wm_get_resources(wm);
2606 	weston_wm_get_visual_and_colormap(wm);
2607 
2608 	values[0] =
2609 		XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
2610 		XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
2611 		XCB_EVENT_MASK_PROPERTY_CHANGE;
2612 	xcb_change_window_attributes(wm->conn, wm->screen->root,
2613 				     XCB_CW_EVENT_MASK, values);
2614 
2615 	xcb_composite_redirect_subwindows(wm->conn, wm->screen->root,
2616 					  XCB_COMPOSITE_REDIRECT_MANUAL);
2617 
2618 	wm->theme = theme_create();
2619 
2620 	supported[0] = wm->atom.net_wm_moveresize;
2621 	supported[1] = wm->atom.net_wm_state;
2622 	supported[2] = wm->atom.net_wm_state_fullscreen;
2623 	supported[3] = wm->atom.net_wm_state_maximized_vert;
2624 	supported[4] = wm->atom.net_wm_state_maximized_horz;
2625 	supported[5] = wm->atom.net_active_window;
2626 	xcb_change_property(wm->conn,
2627 			    XCB_PROP_MODE_REPLACE,
2628 			    wm->screen->root,
2629 			    wm->atom.net_supported,
2630 			    XCB_ATOM_ATOM,
2631 			    32, /* format */
2632 			    ARRAY_LENGTH(supported), supported);
2633 
2634 	weston_wm_set_net_active_window(wm, XCB_WINDOW_NONE);
2635 
2636 	weston_wm_selection_init(wm);
2637 
2638 	weston_wm_dnd_init(wm);
2639 
2640 	xcb_flush(wm->conn);
2641 
2642 	wm->create_surface_listener.notify = weston_wm_create_surface;
2643 	wl_signal_add(&wxs->compositor->create_surface_signal,
2644 		      &wm->create_surface_listener);
2645 	wm->activate_listener.notify = weston_wm_window_activate;
2646 	wl_signal_add(&wxs->compositor->activate_signal,
2647 		      &wm->activate_listener);
2648 	wm->kill_listener.notify = weston_wm_kill_client;
2649 	wl_signal_add(&wxs->compositor->kill_signal,
2650 		      &wm->kill_listener);
2651 	wl_list_init(&wm->unpaired_window_list);
2652 
2653 	weston_wm_create_cursors(wm);
2654 	weston_wm_window_set_cursor(wm, wm->screen->root, XWM_CURSOR_LEFT_PTR);
2655 
2656 	/* Create wm window and take WM_S0 selection last, which
2657 	 * signals to Xwayland that we're done with setup. */
2658 	weston_wm_create_wm_window(wm);
2659 
2660 	weston_log("created wm, root %d\n", wm->screen->root);
2661 
2662 	return wm;
2663 }
2664 
2665 void
weston_wm_destroy(struct weston_wm * wm)2666 weston_wm_destroy(struct weston_wm *wm)
2667 {
2668 	/* FIXME: Free windows in hash. */
2669 	hash_table_destroy(wm->window_hash);
2670 	weston_wm_destroy_cursors(wm);
2671 	xcb_disconnect(wm->conn);
2672 	wl_event_source_remove(wm->source);
2673 	wl_list_remove(&wm->selection_listener.link);
2674 	wl_list_remove(&wm->activate_listener.link);
2675 	wl_list_remove(&wm->kill_listener.link);
2676 	wl_list_remove(&wm->create_surface_listener.link);
2677 
2678 	free(wm);
2679 }
2680 
2681 static struct weston_wm_window *
get_wm_window(struct weston_surface * surface)2682 get_wm_window(struct weston_surface *surface)
2683 {
2684 	struct wl_listener *listener;
2685 
2686 	listener = wl_signal_get(&surface->destroy_signal, surface_destroy);
2687 	if (listener)
2688 		return container_of(listener, struct weston_wm_window,
2689 				    surface_destroy_listener);
2690 
2691 	return NULL;
2692 }
2693 
2694 static bool
is_wm_window(struct weston_surface * surface)2695 is_wm_window(struct weston_surface *surface)
2696 {
2697 	return get_wm_window(surface) != NULL;
2698 }
2699 
2700 static void
weston_wm_window_configure(void * data)2701 weston_wm_window_configure(void *data)
2702 {
2703 	struct weston_wm_window *window = data;
2704 	struct weston_wm *wm = window->wm;
2705 	uint32_t values[4];
2706 	int x, y;
2707 
2708 	if (window->configure_source) {
2709 		wl_event_source_remove(window->configure_source);
2710 		window->configure_source = NULL;
2711 	}
2712 
2713 	weston_wm_window_set_allow_commits(window, false);
2714 
2715 	weston_wm_window_get_child_position(window, &x, &y);
2716 	values[0] = x;
2717 	values[1] = y;
2718 	values[2] = window->width;
2719 	values[3] = window->height;
2720 	weston_wm_configure_window(wm, window->id,
2721 				   XCB_CONFIG_WINDOW_X |
2722 				   XCB_CONFIG_WINDOW_Y |
2723 				   XCB_CONFIG_WINDOW_WIDTH |
2724 				   XCB_CONFIG_WINDOW_HEIGHT,
2725 				   values);
2726 
2727 	weston_wm_window_configure_frame(window);
2728 	weston_wm_window_schedule_repaint(window);
2729 }
2730 
2731 static void
send_configure(struct weston_surface * surface,int32_t width,int32_t height)2732 send_configure(struct weston_surface *surface, int32_t width, int32_t height)
2733 {
2734 	struct weston_wm_window *window = get_wm_window(surface);
2735 	struct weston_wm *wm;
2736 	struct theme *t;
2737 	int new_width, new_height;
2738 	int vborder, hborder;
2739 
2740 	if (!window || !window->wm)
2741 		return;
2742 	wm = window->wm;
2743 	t = wm->theme;
2744 
2745 	if (window->decorate && !window->fullscreen) {
2746 		hborder = 2 * t->width;
2747 		vborder = t->titlebar_height + t->width;
2748 	} else {
2749 		hborder = 0;
2750 		vborder = 0;
2751 	}
2752 
2753 	if (width > hborder)
2754 		new_width = width - hborder;
2755 	else
2756 		new_width = 1;
2757 
2758 	if (height > vborder)
2759 		new_height = height - vborder;
2760 	else
2761 		new_height = 1;
2762 
2763 	if (window->width != new_width || window->height != new_height) {
2764 		window->width = new_width;
2765 		window->height = new_height;
2766 
2767 		if (window->frame) {
2768 			frame_resize_inside(window->frame,
2769 					    window->width, window->height);
2770 		}
2771 	}
2772 
2773 	if (window->configure_source)
2774 		return;
2775 
2776 	window->configure_source =
2777 		wl_event_loop_add_idle(wm->server->loop,
2778 				       weston_wm_window_configure, window);
2779 }
2780 
2781 static void
send_position(struct weston_surface * surface,int32_t x,int32_t y)2782 send_position(struct weston_surface *surface, int32_t x, int32_t y)
2783 {
2784 	struct weston_wm_window *window = get_wm_window(surface);
2785 	struct weston_wm *wm;
2786 	uint32_t values[2];
2787 	uint16_t mask;
2788 
2789 	if (!window || !window->wm)
2790 		return;
2791 
2792 	wm = window->wm;
2793 	/* We use pos_dirty to tell whether a configure message is in flight.
2794 	 * This is needed in case we send two configure events in a very
2795 	 * short time, since window->x/y is set in after a roundtrip, hence
2796 	 * we cannot just check if the current x and y are different. */
2797 	if (window->x != x || window->y != y || window->pos_dirty) {
2798 		window->pos_dirty = true;
2799 		values[0] = x;
2800 		values[1] = y;
2801 		mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
2802 
2803 		weston_wm_configure_window(wm, window->frame_id, mask, values);
2804 		xcb_flush(wm->conn);
2805 	}
2806 }
2807 
2808 static const struct weston_xwayland_client_interface shell_client = {
2809 	send_configure,
2810 };
2811 
2812 static int
legacy_fullscreen(struct weston_wm * wm,struct weston_wm_window * window,struct weston_output ** output_ret)2813 legacy_fullscreen(struct weston_wm *wm,
2814 		  struct weston_wm_window *window,
2815 		  struct weston_output **output_ret)
2816 {
2817 	struct weston_compositor *compositor = wm->server->compositor;
2818 	struct weston_output *output;
2819 	uint32_t minmax = PMinSize | PMaxSize;
2820 	int matching_size;
2821 
2822 	/* Heuristics for detecting legacy fullscreen windows... */
2823 
2824 	wl_list_for_each(output, &compositor->output_list, link) {
2825 		if (output->x == window->x &&
2826 		    output->y == window->y &&
2827 		    output->width == window->width &&
2828 		    output->height == window->height &&
2829 		    window->override_redirect) {
2830 			*output_ret = output;
2831 			return 1;
2832 		}
2833 
2834 		matching_size = 0;
2835 		if ((window->size_hints.flags & (USSize |PSize)) &&
2836 		    window->size_hints.width == output->width &&
2837 		    window->size_hints.height == output->height)
2838 			matching_size = 1;
2839 		if ((window->size_hints.flags & minmax) == minmax &&
2840 		    window->size_hints.min_width == output->width &&
2841 		    window->size_hints.min_height == output->height &&
2842 		    window->size_hints.max_width == output->width &&
2843 		    window->size_hints.max_height == output->height)
2844 			matching_size = 1;
2845 
2846 		if (matching_size && !window->decorate &&
2847 		    (window->size_hints.flags & (USPosition | PPosition)) &&
2848 		    window->size_hints.x == output->x &&
2849 		    window->size_hints.y == output->y) {
2850 			*output_ret = output;
2851 			return 1;
2852 		}
2853 	}
2854 
2855 	return 0;
2856 }
2857 
2858 static bool
weston_wm_window_is_positioned(struct weston_wm_window * window)2859 weston_wm_window_is_positioned(struct weston_wm_window *window)
2860 {
2861 	if (window->map_request_x == INT_MIN ||
2862 	    window->map_request_y == INT_MIN)
2863 		weston_log("XWM warning: win %d did not see map request\n",
2864 			   window->id);
2865 
2866 	return window->map_request_x != 0 || window->map_request_y != 0;
2867 }
2868 
2869 static bool
weston_wm_window_type_inactive(struct weston_wm_window * window)2870 weston_wm_window_type_inactive(struct weston_wm_window *window)
2871 {
2872 	struct weston_wm *wm = window->wm;
2873 
2874 	return window->type == wm->atom.net_wm_window_type_tooltip ||
2875 	       window->type == wm->atom.net_wm_window_type_dropdown ||
2876 	       window->type == wm->atom.net_wm_window_type_dnd ||
2877 	       window->type == wm->atom.net_wm_window_type_combo ||
2878 	       window->type == wm->atom.net_wm_window_type_popup ||
2879 	       window->type == wm->atom.net_wm_window_type_utility;
2880 }
2881 
2882 static void
xserver_map_shell_surface(struct weston_wm_window * window,struct weston_surface * surface)2883 xserver_map_shell_surface(struct weston_wm_window *window,
2884 			  struct weston_surface *surface)
2885 {
2886 	struct weston_wm *wm = window->wm;
2887 	struct weston_desktop_xwayland *xwayland =
2888 		wm->server->compositor->xwayland;
2889 	const struct weston_desktop_xwayland_interface *xwayland_interface =
2890 		wm->server->compositor->xwayland_interface;
2891 	struct weston_wm_window *parent;
2892 
2893 	/* This should be necessary only for override-redirected windows,
2894 	 * because otherwise MapRequest handler would have already updated
2895 	 * the properties. However, if X11 clients set properties after
2896 	 * sending MapWindow, here we can still process them. The decorations
2897 	 * have already been drawn once with the old property values, so if the
2898 	 * app changes something affecting decor after MapWindow, we glitch.
2899 	 * We only hit xserver_map_shell_surface() once per MapWindow and
2900 	 * wl_surface, so better ensure we get the window type right.
2901 	 */
2902 	weston_wm_window_read_properties(window);
2903 
2904 	/* A weston_wm_window may have many different surfaces assigned
2905 	 * throughout its life, so we must make sure to remove the listener
2906 	 * from the old surface signal list. */
2907 	if (window->surface)
2908 		wl_list_remove(&window->surface_destroy_listener.link);
2909 
2910 	window->surface = surface;
2911 	window->surface_destroy_listener.notify = surface_destroy;
2912 	wl_signal_add(&window->surface->destroy_signal,
2913 		      &window->surface_destroy_listener);
2914 
2915 	if (!xwayland_interface)
2916 		return;
2917 
2918 	if (window->surface->committed) {
2919 		weston_log("warning, unexpected in %s: "
2920 			   "surface's configure hook is already set.\n",
2921 			   __func__);
2922 		return;
2923 	}
2924 
2925 	window->shsurf =
2926 		xwayland_interface->create_surface(xwayland,
2927 						   window->surface,
2928 						   &shell_client);
2929 
2930 	wm_printf(wm, "XWM: map shell surface, win %d, weston_surface %p, xwayland surface %p\n",
2931 		  window->id, window->surface, window->shsurf);
2932 
2933 	if (window->name)
2934 		xwayland_interface->set_title(window->shsurf, window->name);
2935 	if (window->pid > 0)
2936 		xwayland_interface->set_pid(window->shsurf, window->pid);
2937 
2938 	if (window->fullscreen) {
2939 		window->saved_width = window->width;
2940 		window->saved_height = window->height;
2941 		xwayland_interface->set_fullscreen(window->shsurf,
2942 						   window->legacy_fullscreen_output.output);
2943 	} else if (window->override_redirect) {
2944 		xwayland_interface->set_xwayland(window->shsurf,
2945 						 window->x, window->y);
2946 	} else if (window->transient_for && window->transient_for->surface) {
2947 		parent = window->transient_for;
2948 		if (weston_wm_window_type_inactive(window)) {
2949 			xwayland_interface->set_transient(window->shsurf,
2950 							  parent->surface,
2951 							  window->x - parent->x,
2952 							  window->y - parent->y);
2953 		} else {
2954 			xwayland_interface->set_toplevel(window->shsurf);
2955 			xwayland_interface->set_parent(window->shsurf,
2956 						       parent->surface);
2957 		}
2958 	} else if (weston_wm_window_is_maximized(window)) {
2959 		xwayland_interface->set_maximized(window->shsurf);
2960 	} else {
2961 		if (weston_wm_window_type_inactive(window)) {
2962 			xwayland_interface->set_xwayland(window->shsurf,
2963 							 window->x,
2964 							 window->y);
2965 		} else if (weston_wm_window_is_positioned(window)) {
2966 			xwayland_interface->set_toplevel_with_position(window->shsurf,
2967 								       window->map_request_x,
2968 								       window->map_request_y);
2969 		} else {
2970 			xwayland_interface->set_toplevel(window->shsurf);
2971 		}
2972 	}
2973 
2974 	if (window->frame_id == XCB_WINDOW_NONE) {
2975 		weston_wm_window_set_pending_state_OR(window);
2976 	} else {
2977 		weston_wm_window_set_pending_state(window);
2978 		weston_wm_window_set_allow_commits(window, true);
2979 		xcb_flush(wm->conn);
2980 	}
2981 }
2982 
2983 const struct weston_xwayland_surface_api surface_api = {
2984 	is_wm_window,
2985 	send_position,
2986 };
2987