window-manager.c 80.9 KB
Newer Older
1 2 3
/*
 * Copyright © 2011 Intel Corporation
 *
4 5 6 7 8 9 10
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
11
 *
12 13 14 15 16 17 18 19 20 21 22 23
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
24 25
 */

26
#include "config.h"
27 28

#include <stdlib.h>
29
#include <stdint.h>
30
#include <inttypes.h>
31 32 33 34 35 36 37 38
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#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 "compositor.h"
45
#include "xwayland.h"
46
#include "xwayland-internal-interface.h"
47

48
#include "cairo-util.h"
49
#include "hash.h"
50
#include "shared/helpers.h"
51

52
struct wm_size_hints {
53
	uint32_t flags;
54 55 56 57
	int32_t x, y;
	int32_t width, height;	/* should set so old wm's don't mess up */
	int32_t min_width, min_height;
	int32_t max_width, max_height;
58
	int32_t width_inc, height_inc;
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
	struct {
		int32_t x;
		int32_t y;
	} min_aspect, max_aspect;
	int32_t base_width, base_height;
	int32_t win_gravity;
};

#define USPosition	(1L << 0)
#define USSize		(1L << 1)
#define PPosition	(1L << 2)
#define PSize		(1L << 3)
#define PMinSize	(1L << 4)
#define PMaxSize	(1L << 5)
#define PResizeInc	(1L << 6)
#define PAspect		(1L << 7)
#define PBaseSize	(1L << 8)
#define PWinGravity	(1L << 9)

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
struct motif_wm_hints {
	uint32_t flags;
	uint32_t functions;
	uint32_t decorations;
	int32_t input_mode;
	uint32_t status;
};

#define MWM_HINTS_FUNCTIONS     (1L << 0)
#define MWM_HINTS_DECORATIONS   (1L << 1)
#define MWM_HINTS_INPUT_MODE    (1L << 2)
#define MWM_HINTS_STATUS        (1L << 3)

#define MWM_FUNC_ALL            (1L << 0)
#define MWM_FUNC_RESIZE         (1L << 1)
#define MWM_FUNC_MOVE           (1L << 2)
#define MWM_FUNC_MINIMIZE       (1L << 3)
#define MWM_FUNC_MAXIMIZE       (1L << 4)
#define MWM_FUNC_CLOSE          (1L << 5)

#define MWM_DECOR_ALL           (1L << 0)
#define MWM_DECOR_BORDER        (1L << 1)
#define MWM_DECOR_RESIZEH       (1L << 2)
#define MWM_DECOR_TITLE         (1L << 3)
#define MWM_DECOR_MENU          (1L << 4)
#define MWM_DECOR_MINIMIZE      (1L << 5)
#define MWM_DECOR_MAXIMIZE      (1L << 6)

106 107 108 109
#define MWM_DECOR_EVERYTHING \
	(MWM_DECOR_BORDER | MWM_DECOR_RESIZEH | MWM_DECOR_TITLE | \
	 MWM_DECOR_MENU | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE)

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
#define MWM_INPUT_MODELESS 0
#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
#define MWM_INPUT_SYSTEM_MODAL 2
#define MWM_INPUT_FULL_APPLICATION_MODAL 3
#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL

#define MWM_TEAROFF_WINDOW      (1L<<0)

#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
#define _NET_WM_MOVERESIZE_SIZE_TOP          1
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
#define _NET_WM_MOVERESIZE_MOVE              8   /* movement only */
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9   /* size via keyboard */
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10   /* move via keyboard */
#define _NET_WM_MOVERESIZE_CANCEL           11   /* cancel operation */

131 132 133 134 135
struct weston_output_weak_ref {
	struct weston_output *output;
	struct wl_listener destroy_listener;
};

136 137 138 139
struct weston_wm_window {
	struct weston_wm *wm;
	xcb_window_t id;
	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 146
	struct wl_listener surface_destroy_listener;
	struct wl_event_source *repaint_source;
147
	struct wl_event_source *configure_source;
148
	int properties_dirty;
149 150
	int pid;
	char *machine;
151 152 153 154 155 156
	char *class;
	char *name;
	struct weston_wm_window *transient_for;
	uint32_t protocols;
	xcb_atom_t type;
	int width, height;
157 158
	int x;
	int y;
159
	bool pos_dirty;
160 161
	int map_request_x;
	int map_request_y;
162
	struct weston_output_weak_ref legacy_fullscreen_output;
163
	int saved_width, saved_height;
164
	int decorate;
165 166
	uint32_t last_button_time;
	int did_double;
167
	int override_redirect;
168
	int fullscreen;
169
	int has_alpha;
170
	int delete_window;
171 172
	int maximized_vert;
	int maximized_horz;
173 174
	struct wm_size_hints size_hints;
	struct motif_wm_hints motif_hints;
175
	struct wl_list link;
176 177 178 179 180
};

static struct weston_wm_window *
get_wm_window(struct weston_surface *surface);

181 182 183
static void
weston_wm_set_net_active_window(struct weston_wm *wm, xcb_window_t window);

184 185 186
static void
weston_wm_window_schedule_repaint(struct weston_wm_window *window);

187 188 189 190 191
static int
legacy_fullscreen(struct weston_wm *wm,
		  struct weston_wm_window *window,
		  struct weston_output **output_ret);

192 193 194 195
static void
xserver_map_shell_surface(struct weston_wm_window *window,
			  struct weston_surface *surface);

196 197
static bool
wm_debug_is_enabled(struct weston_wm *wm)
198
{
199 200
	return weston_debug_scope_is_enabled(wm->server->wm_debug);
}
201

202 203 204 205 206
static void __attribute__ ((format (printf, 2, 3)))
wm_printf(struct weston_wm *wm, const char *fmt, ...)
{
	va_list ap;
	char timestr[128];
207

208 209 210 211
	if (wm_debug_is_enabled(wm))
		weston_debug_scope_printf(wm->server->wm_debug, "%s ",
				weston_debug_scope_timestamp(wm->server->wm_debug,
				timestr, sizeof timestr));
212

213 214 215 216
	va_start(ap, fmt);
	weston_debug_scope_vprintf(wm->server->wm_debug, fmt, ap);
	va_end(ap);
}
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
static void
weston_output_weak_ref_init(struct weston_output_weak_ref *ref)
{
	ref->output = NULL;
}

static void
weston_output_weak_ref_clear(struct weston_output_weak_ref *ref)
{
	if (!ref->output)
		return;

	wl_list_remove(&ref->destroy_listener.link);
	ref->output = NULL;
}

static void
weston_output_weak_ref_handle_destroy(struct wl_listener *listener, void *data)
{
	struct weston_output_weak_ref *ref;

	ref = wl_container_of(listener, ref, destroy_listener);
	assert(ref->output == data);

	weston_output_weak_ref_clear(ref);
}

static void
weston_output_weak_ref_set(struct weston_output_weak_ref *ref,
			   struct weston_output *output)
{
	weston_output_weak_ref_clear(ref);

	if (!output)
		return;

	ref->destroy_listener.notify = weston_output_weak_ref_handle_destroy;
	wl_signal_add(&output->destroy_signal, &ref->destroy_listener);
	ref->output = output;
}

258 259 260 261 262 263 264 265 266
static bool __attribute__ ((warn_unused_result))
wm_lookup_window(struct weston_wm *wm, xcb_window_t hash,
		 struct weston_wm_window **window)
{
	*window = hash_table_lookup(wm->window_hash, hash);
	if (*window)
		return true;
	return false;
}
267

268 269 270 271 272 273 274 275 276 277 278 279 280
const char *
get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
{
	xcb_get_atom_name_cookie_t cookie;
	xcb_get_atom_name_reply_t *reply;
	xcb_generic_error_t *e;
	static char buffer[64];

	if (atom == XCB_ATOM_NONE)
		return "None";

	cookie = xcb_get_atom_name (c, atom);
	reply = xcb_get_atom_name_reply (c, cookie, &e);
281

Dawid Gajownik's avatar
Dawid Gajownik committed
282
	if (reply) {
283 284 285 286 287 288 289
		snprintf(buffer, sizeof buffer, "%.*s",
			 xcb_get_atom_name_name_length (reply),
			 xcb_get_atom_name_name (reply));
	} else {
		snprintf(buffer, sizeof buffer, "(atom %u)", atom);
	}

290 291 292 293 294
	free(reply);

	return buffer;
}

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
static xcb_cursor_t
xcb_cursor_image_load_cursor(struct weston_wm *wm, const XcursorImage *img)
{
	xcb_connection_t *c = wm->conn;
	xcb_screen_iterator_t s = xcb_setup_roots_iterator(xcb_get_setup(c));
	xcb_screen_t *screen = s.data;
	xcb_gcontext_t gc;
	xcb_pixmap_t pix;
	xcb_render_picture_t pic;
	xcb_cursor_t cursor;
	int stride = img->width * 4;

	pix = xcb_generate_id(c);
	xcb_create_pixmap(c, 32, pix, screen->root, img->width, img->height);

	pic = xcb_generate_id(c);
	xcb_render_create_picture(c, pic, pix, wm->format_rgba.id, 0, 0);

	gc = xcb_generate_id(c);
	xcb_create_gc(c, gc, pix, 0, 0);

	xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc,
		      img->width, img->height, 0, 0, 0, 32,
		      stride * img->height, (uint8_t *) img->pixels);
	xcb_free_gc(c, gc);

	cursor = xcb_generate_id(c);
	xcb_render_create_cursor(c, cursor, pic, img->xhot, img->yhot);

	xcb_render_free_picture(c, pic);
	xcb_free_pixmap(c, pix);

	return cursor;
}

static xcb_cursor_t
xcb_cursor_images_load_cursor(struct weston_wm *wm, const XcursorImages *images)
{
	/* TODO: treat animated cursors as well */
	if (images->nimage != 1)
		return -1;

	return xcb_cursor_image_load_cursor(wm, images->images[0]);
}

static xcb_cursor_t
xcb_cursor_library_load_cursor(struct weston_wm *wm, const char *file)
{
	xcb_cursor_t cursor;
	XcursorImages *images;
	char *v = NULL;
	int size = 0;

	if (!file)
		return 0;

	v = getenv ("XCURSOR_SIZE");
	if (v)
		size = atoi(v);

	if (!size)
		size = 32;

	images = XcursorLibraryLoadImages (file, NULL, size);
359 360 361
	if (!images)
		return -1;

362 363 364 365 366 367
	cursor = xcb_cursor_images_load_cursor (wm, images);
	XcursorImagesDestroy (images);

	return cursor;
}

368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
static unsigned
dump_cardinal_array_elem(FILE *fp, unsigned format,
			 void *arr, unsigned len, unsigned ind)
{
	const char *comma;

	/* If more than 16 elements, print 0-14, ..., last */
	if (ind > 14 && ind < len - 1) {
		fprintf(fp, ", ...");
		return len - 1;
	}

	comma = ind ? ", " : "";

	switch (format) {
	case 32:
		fprintf(fp, "%s%" PRIu32, comma, ((uint32_t *)arr)[ind]);
		break;
	case 16:
		fprintf(fp, "%s%" PRIu16, comma, ((uint16_t *)arr)[ind]);
		break;
	case 8:
		fprintf(fp, "%s%" PRIu8, comma, ((uint8_t *)arr)[ind]);
		break;
	default:
		fprintf(fp, "%s???", comma);
	}

	return ind + 1;
}

static void
400
dump_cardinal_array(FILE *fp, xcb_get_property_reply_t *reply)
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
{
	unsigned i = 0;
	void *arr;
	char *str = NULL;

	assert(reply->type == XCB_ATOM_CARDINAL);

	arr = xcb_get_property_value(reply);

	fprintf(fp, "[");
	while (i < reply->value_len)
		i = dump_cardinal_array_elem(fp, reply->format,
					     arr, reply->value_len, i);
	fprintf(fp, "]");

	free(str);
}

419
void
420
dump_property(FILE *fp, struct weston_wm *wm,
421
	      xcb_atom_t property, xcb_get_property_reply_t *reply)
422 423 424 425
{
	int32_t *incr_value;
	const char *text_value, *name;
	xcb_atom_t *atom_value;
426
	xcb_window_t *window_value;
427 428
	int width, len;
	uint32_t i;
429 430 431 432

	width = fprintf(fp, "%s: ", get_atom_name(wm->conn, property));
	if (reply == NULL) {
		fprintf(fp, "(no reply)\n");
433
		return;
434 435
	}

436 437 438 439 440
	width += fprintf(fp, "%s/%d, length %d (value_len %d): ",
			 get_atom_name(wm->conn, reply->type),
			 reply->format,
			 xcb_get_property_value_length(reply),
			 reply->value_len);
441 442 443

	if (reply->type == wm->atom.incr) {
		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 448 449 450 451
		text_value = xcb_get_property_value(reply);
		if (reply->value_len > 40)
			len = 40;
		else
			len = reply->value_len;
452
		fprintf(fp, "\"%.*s\"\n", len, text_value);
453 454 455 456 457
	} else if (reply->type == XCB_ATOM_ATOM) {
		atom_value = xcb_get_property_value(reply);
		for (i = 0; i < reply->value_len; i++) {
			name = get_atom_name(wm->conn, atom_value[i]);
			if (width + strlen(name) + 2 > 78) {
458
				fprintf(fp, "\n    ");
459 460
				width = 4;
			} 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 470
	} else if (reply->type == XCB_ATOM_WINDOW && reply->format == 32) {
		window_value = xcb_get_property_value(reply);
471
		fprintf(fp, "win %u\n", *window_value);
472
	} else {
473
		fprintf(fp, "huh?\n");
474 475 476
	}
}

477
static void
478
read_and_dump_property(FILE *fp, struct weston_wm *wm,
479
		       xcb_window_t window, xcb_atom_t property)
480
{
481 482
	xcb_get_property_reply_t *reply;
	xcb_get_property_cookie_t cookie;
483

484 485 486
	cookie = xcb_get_property(wm->conn, 0, window,
				  property, XCB_ATOM_ANY, 0, 2048);
	reply = xcb_get_property_reply(wm->conn, cookie, NULL);
487

488
	dump_property(fp, wm, property, reply);
489

490
	free(reply);
491 492
}

493 494 495 496
/* We reuse some predefined, but otherwise useles atoms
 * as local type placeholders that never touch the X11 server,
 * to make weston_wm_window_read_properties() less exceptional.
 */
497 498
#define TYPE_WM_PROTOCOLS	XCB_ATOM_CUT_BUFFER0
#define TYPE_MOTIF_WM_HINTS	XCB_ATOM_CUT_BUFFER1
499
#define TYPE_NET_WM_STATE	XCB_ATOM_CUT_BUFFER2
500
#define TYPE_WM_NORMAL_HINTS	XCB_ATOM_CUT_BUFFER3
501 502 503 504 505 506

static void
weston_wm_window_read_properties(struct weston_wm_window *window)
{
	struct weston_wm *wm = window->wm;

507
#define F(field) (&window->field)
508 509 510
	const struct {
		xcb_atom_t atom;
		xcb_atom_t type;
511
		void *ptr;
512
	} props[] = {
513 514 515 516 517 518 519 520 521 522 523
		{ XCB_ATOM_WM_CLASS,           XCB_ATOM_STRING,            F(class) },
		{ XCB_ATOM_WM_NAME,            XCB_ATOM_STRING,            F(name) },
		{ XCB_ATOM_WM_TRANSIENT_FOR,   XCB_ATOM_WINDOW,            F(transient_for) },
		{ wm->atom.wm_protocols,       TYPE_WM_PROTOCOLS,          NULL },
		{ wm->atom.wm_normal_hints,    TYPE_WM_NORMAL_HINTS,       NULL },
		{ wm->atom.net_wm_state,       TYPE_NET_WM_STATE,          NULL },
		{ wm->atom.net_wm_window_type, XCB_ATOM_ATOM,              F(type) },
		{ wm->atom.net_wm_name,        XCB_ATOM_STRING,            F(name) },
		{ wm->atom.net_wm_pid,         XCB_ATOM_CARDINAL,          F(pid) },
		{ wm->atom.motif_wm_hints,     TYPE_MOTIF_WM_HINTS,        NULL },
		{ wm->atom.wm_client_machine,  XCB_ATOM_WM_CLIENT_MACHINE, F(machine) },
524 525 526 527 528 529 530 531 532
	};
#undef F

	xcb_get_property_cookie_t cookie[ARRAY_LENGTH(props)];
	xcb_get_property_reply_t *reply;
	void *p;
	uint32_t *xid;
	xcb_atom_t *atom;
	uint32_t i;
533
	char name[1024];
534 535 536 537 538 539 540 541 542 543 544 545

	if (!window->properties_dirty)
		return;
	window->properties_dirty = 0;

	for (i = 0; i < ARRAY_LENGTH(props); i++)
		cookie[i] = xcb_get_property(wm->conn,
					     0, /* delete */
					     window->id,
					     props[i].atom,
					     XCB_ATOM_ANY, 0, 2048);

546
	window->decorate = window->override_redirect ? 0 : MWM_DECOR_EVERYTHING;
547 548
	window->size_hints.flags = 0;
	window->motif_hints.flags = 0;
549
	window->delete_window = 0;
550

551 552 553 554 555 556 557 558 559 560 561
	for (i = 0; i < ARRAY_LENGTH(props); i++)  {
		reply = xcb_get_property_reply(wm->conn, cookie[i], NULL);
		if (!reply)
			/* Bad window, typically */
			continue;
		if (reply->type == XCB_ATOM_NONE) {
			/* No such property */
			free(reply);
			continue;
		}

562
		p = props[i].ptr;
563 564

		switch (props[i].type) {
565
		case XCB_ATOM_WM_CLIENT_MACHINE:
566 567 568 569 570 571 572 573 574 575 576 577
		case XCB_ATOM_STRING:
			/* FIXME: We're using this for both string and
			   utf8_string */
			if (*(char **) p)
				free(*(char **) p);

			*(char **) p =
				strndup(xcb_get_property_value(reply),
					xcb_get_property_value_length(reply));
			break;
		case XCB_ATOM_WINDOW:
			xid = xcb_get_property_value(reply);
578 579 580
			if (!wm_lookup_window(wm, *xid, p))
				weston_log("XCB_ATOM_WINDOW contains window"
					   " id not found in hash table.\n");
581
			break;
582
		case XCB_ATOM_CARDINAL:
583 584 585 586 587
		case XCB_ATOM_ATOM:
			atom = xcb_get_property_value(reply);
			*(xcb_atom_t *) p = *atom;
			break;
		case TYPE_WM_PROTOCOLS:
588 589
			atom = xcb_get_property_value(reply);
			for (i = 0; i < reply->value_len; i++)
Derek Foreman's avatar
Derek Foreman committed
590
				if (atom[i] == wm->atom.wm_delete_window) {
591
					window->delete_window = 1;
Derek Foreman's avatar
Derek Foreman committed
592 593
					break;
				}
594
			break;
595 596 597 598 599
		case TYPE_WM_NORMAL_HINTS:
			memcpy(&window->size_hints,
			       xcb_get_property_value(reply),
			       sizeof window->size_hints);
			break;
600 601 602
		case TYPE_NET_WM_STATE:
			window->fullscreen = 0;
			atom = xcb_get_property_value(reply);
603
			for (i = 0; i < reply->value_len; i++) {
604 605
				if (atom[i] == wm->atom.net_wm_state_fullscreen)
					window->fullscreen = 1;
606 607 608 609
				if (atom[i] == wm->atom.net_wm_state_maximized_vert)
					window->maximized_vert = 1;
				if (atom[i] == wm->atom.net_wm_state_maximized_horz)
					window->maximized_horz = 1;
610
			}
611
			break;
612
		case TYPE_MOTIF_WM_HINTS:
613 614 615
			memcpy(&window->motif_hints,
			       xcb_get_property_value(reply),
			       sizeof window->motif_hints);
616 617 618 619 620 621 622 623 624
			if (window->motif_hints.flags & MWM_HINTS_DECORATIONS) {
				if (window->motif_hints.decorations & MWM_DECOR_ALL)
					/* MWM_DECOR_ALL means all except the other values listed. */
					window->decorate =
						MWM_DECOR_EVERYTHING & (~window->motif_hints.decorations);
				else
					window->decorate =
						window->motif_hints.decorations;
			}
625 626 627 628 629 630
			break;
		default:
			break;
		}
		free(reply);
	}
631

632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
	if (window->pid > 0) {
		gethostname(name, sizeof(name));
		for (i = 0; i < sizeof(name); i++) {
			if (name[i] == '\0')
				break;
		}
		if (i == sizeof(name))
			name[0] = '\0'; /* ignore stupid hostnames */

		/* this is only one heuristic to guess the PID of a client is
		* valid, assuming it's compliant with icccm and ewmh.
		* Non-compliants and remote applications of course fail. */
		if (!window->machine || strcmp(window->machine, name))
			window->pid = 0;
	}
647 648
}

649 650 651 652 653
#undef TYPE_WM_PROTOCOLS
#undef TYPE_MOTIF_WM_HINTS
#undef TYPE_NET_WM_STATE
#undef TYPE_WM_NORMAL_HINTS

654 655 656 657 658 659
static void
weston_wm_window_get_frame_size(struct weston_wm_window *window,
				int *width, int *height)
{
	struct theme *t = window->wm->theme;

660 661 662
	if (window->fullscreen) {
		*width = window->width;
		*height = window->height;
663
	} else if (window->decorate && window->frame) {
664 665
		*width = frame_width(window->frame);
		*height = frame_height(window->frame);
666 667 668 669 670 671 672 673 674 675 676 677
	} else {
		*width = window->width + t->margin * 2;
		*height = window->height + t->margin * 2;
	}
}

static void
weston_wm_window_get_child_position(struct weston_wm_window *window,
				    int *x, int *y)
{
	struct theme *t = window->wm->theme;

678 679 680
	if (window->fullscreen) {
		*x = 0;
		*y = 0;
681
	} else if (window->decorate && window->frame) {
682
		frame_interior(window->frame, x, y, NULL, NULL);
683 684 685 686 687
	} else {
		*x = t->margin;
		*y = t->margin;
	}
}
688

689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
static void
weston_wm_window_send_configure_notify(struct weston_wm_window *window)
{
	xcb_configure_notify_event_t configure_notify;
	struct weston_wm *wm = window->wm;
	int x, y;

	weston_wm_window_get_child_position(window, &x, &y);
	configure_notify.response_type = XCB_CONFIGURE_NOTIFY;
	configure_notify.pad0 = 0;
	configure_notify.event = window->id;
	configure_notify.window = window->id;
	configure_notify.above_sibling = XCB_WINDOW_NONE;
	configure_notify.x = x;
	configure_notify.y = y;
	configure_notify.width = window->width;
	configure_notify.height = window->height;
	configure_notify.border_width = 0;
	configure_notify.override_redirect = 0;
	configure_notify.pad1 = 0;

Fulz's avatar
Fulz committed
710
	xcb_send_event(wm->conn, 0, window->id,
711 712 713 714
		       XCB_EVENT_MASK_STRUCTURE_NOTIFY,
		       (char *) &configure_notify);
}

715 716 717
static void
weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event)
{
Fulz's avatar
Fulz committed
718
	xcb_configure_request_event_t *configure_request =
719 720 721 722 723
		(xcb_configure_request_event_t *) event;
	struct weston_wm_window *window;
	uint32_t mask, values[16];
	int x, y, width, height, i = 0;

724 725 726 727
	wm_printf(wm, "XCB_CONFIGURE_REQUEST (window %d) %d,%d @ %dx%d\n",
		  configure_request->window,
		  configure_request->x, configure_request->y,
		  configure_request->width, configure_request->height);
728

729 730
	if (!wm_lookup_window(wm, configure_request->window, &window))
		return;
731

732 733 734 735 736
	if (window->fullscreen) {
		weston_wm_window_send_configure_notify(window);
		return;
	}

737 738 739 740 741
	if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
		window->width = configure_request->width;
	if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
		window->height = configure_request->height;

742 743 744
	if (window->frame)
		frame_resize_inside(window->frame, window->width, window->height);

745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
	weston_wm_window_get_child_position(window, &x, &y);
	values[i++] = x;
	values[i++] = y;
	values[i++] = window->width;
	values[i++] = window->height;
	values[i++] = 0;
	mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
		XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
		XCB_CONFIG_WINDOW_BORDER_WIDTH;
	if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
		values[i++] = configure_request->sibling;
		mask |= XCB_CONFIG_WINDOW_SIBLING;
	}
	if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
		values[i++] = configure_request->stack_mode;
		mask |= XCB_CONFIG_WINDOW_STACK_MODE;
	}

	xcb_configure_window(wm->conn, window->id, mask, values);

	weston_wm_window_get_frame_size(window, &width, &height);
	values[0] = width;
	values[1] = height;
	mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
	xcb_configure_window(wm->conn, window->frame_id, mask, values);

	weston_wm_window_schedule_repaint(window);
}

774 775 776 777 778 779 780 781 782 783
static int
our_resource(struct weston_wm *wm, uint32_t id)
{
	const xcb_setup_t *setup;

	setup = xcb_get_setup(wm->conn);

	return (id & ~setup->resource_id_mask) == setup->resource_id_base;
}

784 785 786
static void
weston_wm_handle_configure_notify(struct weston_wm *wm, xcb_generic_event_t *event)
{
Fulz's avatar
Fulz committed
787
	xcb_configure_notify_event_t *configure_notify =
788
		(xcb_configure_notify_event_t *) event;
789 790
	const struct weston_desktop_xwayland_interface *xwayland_api =
		wm->server->compositor->xwayland_interface;
791 792
	struct weston_wm_window *window;

793 794 795 796 797
	wm_printf(wm, "XCB_CONFIGURE_NOTIFY (window %d) %d,%d @ %dx%d%s\n",
		  configure_notify->window,
		  configure_notify->x, configure_notify->y,
		  configure_notify->width, configure_notify->height,
		  configure_notify->override_redirect ? ", override" : "");
798

799 800 801
	if (!wm_lookup_window(wm, configure_notify->window, &window))
		return;

802 803
	window->x = configure_notify->x;
	window->y = configure_notify->y;
804 805
	window->pos_dirty = false;

806 807 808
	if (window->override_redirect) {
		window->width = configure_notify->width;
		window->height = configure_notify->height;
809 810 811
		if (window->frame)
			frame_resize_inside(window->frame,
					    window->width, window->height);
812 813 814 815 816 817 818

		/* We should check if shsurf has been created because sometimes
		 * there are races
		 * (configure_notify is sent before xserver_map_surface) */
		if (window->shsurf)
			xwayland_api->set_xwayland(window->shsurf,
						   window->x, window->y);
819
	}
820 821
}

822 823 824 825 826 827 828 829
static void
weston_wm_kill_client(struct wl_listener *listener, void *data)
{
	struct weston_surface *surface = data;
	struct weston_wm_window *window = get_wm_window(surface);
	if (!window)
		return;

830
	if (window->pid > 0)
831 832 833
		kill(window->pid, SIGKILL);
}

834 835 836 837 838 839 840 841 842 843 844 845
static void
weston_wm_create_surface(struct wl_listener *listener, void *data)
{
	struct weston_surface *surface = data;
	struct weston_wm *wm =
		container_of(listener,
			     struct weston_wm, create_surface_listener);
	struct weston_wm_window *window;

	if (wl_resource_get_client(surface->resource) != wm->server->client)
		return;

846
	wm_printf(wm, "XWM: create weston_surface %p\n", surface);
847

848 849 850 851
	wl_list_for_each(window, &wm->unpaired_window_list, link)
		if (window->surface_id ==
		    wl_resource_get_id(surface->resource)) {
			xserver_map_shell_surface(window, surface);
852 853
			window->surface_id = 0;
			wl_list_remove(&window->link);
854
			break;
Fulz's avatar
Fulz committed
855
		}
856 857
}

858
static void
859 860
weston_wm_send_focus_window(struct weston_wm *wm,
			    struct weston_wm_window *window)
861
{
862 863 864
	xcb_client_message_event_t client_message;

	if (window) {
865 866
		uint32_t values[1];

867 868 869
		if (window->override_redirect)
			return;

870 871 872 873 874 875
		client_message.response_type = XCB_CLIENT_MESSAGE;
		client_message.format = 32;
		client_message.window = window->id;
		client_message.type = wm->atom.wm_protocols;
		client_message.data.data32[0] = wm->atom.wm_take_focus;
		client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
876

Fulz's avatar
Fulz committed
877
		xcb_send_event(wm->conn, 0, window->id,
878 879 880 881 882
			       XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
			       (char *) &client_message);

		xcb_set_input_focus (wm->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
				     window->id, XCB_TIME_CURRENT_TIME);
883 884 885 886

		values[0] = XCB_STACK_MODE_ABOVE;
		xcb_configure_window (wm->conn, window->frame_id,
				      XCB_CONFIG_WINDOW_STACK_MODE, values);
887
	} else {
888 889 890 891
		xcb_set_input_focus (wm->conn,
				     XCB_INPUT_FOCUS_POINTER_ROOT,
				     XCB_NONE,
				     XCB_TIME_CURRENT_TIME);
892
	}
893 894 895 896 897
}

static void
weston_wm_window_activate(struct wl_listener *listener, void *data)
{
898 899
	struct weston_surface_activation_data *activation_data = data;
	struct weston_surface *surface = activation_data->surface;
900 901 902 903 904 905 906 907
	struct weston_wm_window *window = NULL;
	struct weston_wm *wm =
		container_of(listener, struct weston_wm, activate_listener);

	if (surface) {
		window = get_wm_window(surface);
	}

908 909 910 911 912 913
	if (window) {
		weston_wm_set_net_active_window(wm, window->id);
	} else {
		weston_wm_set_net_active_window(wm, XCB_WINDOW_NONE);
	}

914
	weston_wm_send_focus_window(wm, window);
915

916
	if (wm->focus_window) {
917 918
		if (wm->focus_window->frame)
			frame_unset_flag(wm->focus_window->frame, FRAME_FLAG_ACTIVE);
919
		weston_wm_window_schedule_repaint(wm->focus_window);
920
	}
921
	wm->focus_window = window;
922
	if (wm->focus_window) {
923 924
		if (wm->focus_window->frame)
			frame_set_flag(wm->focus_window->frame, FRAME_FLAG_ACTIVE);
925
		weston_wm_window_schedule_repaint(wm->focus_window);
926
	}
927 928 929

	xcb_flush(wm->conn);

930 931
}

932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
/** Control Xwayland wl_surface.commit behaviour
 *
 * This function sets the "_XWAYLAND_ALLOW_COMMITS" property of the frame window
 * (not the content window!) to \p allow.
 *
 * If the property is set to \c true, Xwayland will commit whenever it likes.
 * If the property is set to \c false, Xwayland will not commit.
 * If the property is not set at all, Xwayland assumes it is \c true.
 *
 * \param window The XWM window to control.
 * \param allow Whether Xwayland is allowed to wl_surface.commit for the window.
 */
static void
weston_wm_window_set_allow_commits(struct weston_wm_window *window, bool allow)
{
	struct weston_wm *wm = window->wm;
	uint32_t property[1];

	assert(window->frame_id != XCB_WINDOW_NONE);

	property[0] = allow ? 1 : 0;

	xcb_change_property(wm->conn,
			    XCB_PROP_MODE_REPLACE,
			    window->frame_id,
			    wm->atom.allow_commits,
			    XCB_ATOM_CARDINAL,
			    32, /* format */
			    1, property);
}

963 964 965 966 967
#define ICCCM_WITHDRAWN_STATE	0
#define ICCCM_NORMAL_STATE	1
#define ICCCM_ICONIC_STATE	3

static void
968
weston_wm_window_set_wm_state(struct weston_wm_window *window, int32_t state)
969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
{
	struct weston_wm *wm = window->wm;
	uint32_t property[2];

	property[0] = state;
	property[1] = XCB_WINDOW_NONE;

	xcb_change_property(wm->conn,
			    XCB_PROP_MODE_REPLACE,
			    window->id,
			    wm->atom.wm_state,
			    wm->atom.wm_state,
			    32, /* format */
			    2, property);
}

985 986 987 988
static void
weston_wm_window_set_net_wm_state(struct weston_wm_window *window)
{
	struct weston_wm *wm = window->wm;
989
	uint32_t property[3];
990 991 992 993 994
	int i;

	i = 0;
	if (window->fullscreen)
		property[i++] = wm->atom.net_wm_state_fullscreen;
995 996 997 998
	if (window->maximized_vert)
		property[i++] = wm->atom.net_wm_state_maximized_vert;
	if (window->maximized_horz)
		property[i++] = wm->atom.net_wm_state_maximized_horz;
999 1000 1001 1002 1003 1004 1005 1006 1007 1008

	xcb_change_property(wm->conn,
			    XCB_PROP_MODE_REPLACE,
			    window->id,
			    wm->atom.net_wm_state,
			    XCB_ATOM_ATOM,
			    32, /* format */
			    i, property);
}

1009
static void
1010
weston_wm_window_create_frame(struct weston_wm_window *window)
1011
{
1012
	struct weston_wm *wm = window->wm;
1013
	uint32_t values[3];
1014
	int x, y, width, height;
1015 1016 1017 1018
	int buttons = FRAME_BUTTON_CLOSE;

	if (window->decorate & MWM_DECOR_MAXIMIZE)
		buttons |= FRAME_BUTTON_MAXIMIZE;
1019

1020
	window->frame = frame_create(window->wm->theme,
1021 1022
				     window->width, window->height,
				     buttons, window->name, NULL);
1023 1024
	frame_resize_inside(window->frame, window->width, window->height);

1025 1026 1027
	weston_wm_window_get_frame_size(window, &width, &height);
	weston_wm_window_get_child_position(window, &x, &y);

1028 1029
	values[0] = wm->screen->black_pixel;
	values[1] =
1030 1031 1032 1033
		XCB_EVENT_MASK_KEY_PRESS |
		XCB_EVENT_MASK_KEY_RELEASE |
		XCB_EVENT_MASK_BUTTON_PRESS |
		XCB_EVENT_MASK_BUTTON_RELEASE |
1034 1035 1036
		XCB_EVENT_MASK_POINTER_MOTION |
		XCB_EVENT_MASK_ENTER_WINDOW |
		XCB_EVENT_MASK_LEAVE_WINDOW |
1037
		XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
1038
		XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
1039
	values[2] = wm->colormap;
1040 1041 1042

	window->frame_id = xcb_generate_id(wm->conn);
	xcb_create_window(wm->conn,
1043
			  32,
1044 1045 1046 1047 1048 1049
			  window->frame_id,
			  wm->screen->root,
			  0, 0,
			  width, height,
			  0,
			  XCB_WINDOW_CLASS_INPUT_OUTPUT,
1050 1051 1052 1053 1054
			  wm->visual_id,
			  XCB_CW_BORDER_PIXEL |
			  XCB_CW_EVENT_MASK |
			  XCB_CW_COLORMAP, values);

1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
	xcb_reparent_window(wm->conn, window->id, window->frame_id, x, y);

	values[0] = 0;
	xcb_configure_window(wm->conn, window->id,
			     XCB_CONFIG_WINDOW_BORDER_WIDTH, values);

	window->cairo_surface =
		cairo_xcb_surface_create_with_xrender_format(wm->conn,
							     wm->screen,
							     window->frame_id,
1065
							     &wm->format_rgba,
1066 1067 1068 1069 1070
							     width, height);

	hash_table_insert(wm->window_hash, window->frame_id, window);
}

1071 1072 1073 1074 1075 1076
/*
 * Sets the _NET_WM_DESKTOP property for the window to 'desktop'.
 * Passing a <0 desktop value deletes the property.
 */
static void
weston_wm_window_set_virtual_desktop(struct weston_wm_window *window,
1077
				     int desktop)
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
{
	if (desktop >= 0) {
		xcb_change_property(window->wm->conn,
		                    XCB_PROP_MODE_REPLACE,
		                    window->id,
		                    window->wm->atom.net_wm_desktop,
		                    XCB_ATOM_CARDINAL,
		                    32, /* format */
		                    1, &desktop);
	} else {
		xcb_delete_property(window->wm->conn,
		                    window->id,
		                    window->wm->atom.net_wm_desktop);
	}
}

1094 1095 1096 1097 1098 1099
static void
weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
{
	xcb_map_request_event_t *map_request =
		(xcb_map_request_event_t *) event;
	struct weston_wm_window *window;
1100
	struct weston_output *output;
1101 1102

	if (our_resource(wm, map_request->window)) {
1103 1104
		wm_printf(wm, "XCB_MAP_REQUEST (window %d, ours)\n",
			  map_request->window);
1105 1106 1107
		return;
	}

1108 1109
	if (!wm_lookup_window(wm, map_request->window, &window))
		return;
1110 1111 1112

	weston_wm_window_read_properties(window);

1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
	/* For a new Window, MapRequest happens before the Window is realized
	 * in Xwayland. We do the real xcb_map_window() here as a response to
	 * MapRequest. The Window will get realized (wl_surface created in
	 * Wayland and WL_SURFACE_ID sent in X11) when it has been mapped for
	 * real.
	 *
	 * MapRequest only happens for (X11) unmapped Windows. On UnmapNotify,
	 * we reset shsurf to NULL, so even if X11 connection races far ahead
	 * of the Wayland connection and the X11 client is repeatedly mapping
	 * and unmapping, we will never have shsurf set on MapRequest.
	 */
	assert(!window->shsurf);

1126 1127 1128
	window->map_request_x = window->x;
	window->map_request_y = window->y;

1129
	if (window->frame_id == XCB_WINDOW_NONE)
1130 1131
		weston_wm_window_create_frame(window); /* sets frame_id */
	assert(window->frame_id != XCB_WINDOW_NONE);
1132

1133 1134 1135 1136
	wm_printf(wm, "XCB_MAP_REQUEST (window %d, %p, frame %d, %dx%d @ %d,%d)\n",
		  window->id, window, window->frame_id,
		  window->width, window->height,
		  window->map_request_x, window->map_request_y);
1137

1138
	weston_wm_window_set_allow_commits(window, false);
1139 1140
	weston_wm_window_set_wm_state(window, ICCCM_NORMAL_STATE);
	weston_wm_window_set_net_wm_state(window);
1141
	weston_wm_window_set_virtual_desktop(window, 0);
1142

1143 1144 1145 1146 1147 1148
	if (legacy_fullscreen(wm, window, &output)) {
		window->fullscreen = 1;
		weston_output_weak_ref_set(&window->legacy_fullscreen_output,
					   output);
	}

1149 1150
	xcb_map_window(wm->conn, map_request->window);
	xcb_map_window(wm->conn, window->frame_id);
1151 1152 1153 1154 1155

	/* Mapped in the X server, we can draw immediately.
	 * Cannot set pending state though, no weston_surface until
	 * xserver_map_shell_surface() time. */
	weston_wm_window_schedule_repaint(window);
1156 1157
}

1158 1159 1160 1161 1162 1163
static void
weston_wm_handle_map_notify(struct weston_wm *wm, xcb_generic_event_t *event)
{
	xcb_map_notify_event_t *map_notify = (xcb_map_notify_event_t *) event;

	if (our_resource(wm, map_notify->window)) {
1164 1165
		wm_printf(wm, "XCB_MAP_NOTIFY (window %d, ours)\n",
			  map_notify->window);
1166 1167 1168
			return;
	}

1169 1170
	wm_printf(wm, "XCB_MAP_NOTIFY (window %d%s)\n", map_notify->window,
		  map_notify->override_redirect ? ", override" : "");
1171 1172
}

1173 1174 1175 1176 1177 1178 1179
static void
weston_wm_handle_unmap_notify(struct weston_wm *wm, xcb_generic_event_t *event)
{
	xcb_unmap_notify_event_t *unmap_notify =
		(xcb_unmap_notify_event_t *) event;
	struct weston_wm_window *window;

1180 1181 1182 1183
	wm_printf(wm, "XCB_UNMAP_NOTIFY (window %d, event %d%s)\n",
		  unmap_notify->window,
		  unmap_notify->event,
		  our_resource(wm, unmap_notify->window) ? ", ours" : "");
1184 1185 1186 1187

	if (our_resource(wm, unmap_notify->window))
		return;

1188
	if (unmap_notify->response_type & SEND_EVENT_MASK)
1189 1190
		/* We just ignore the ICCCM 4.1.4 synthetic unmap notify
		 * as it may come in after we've destroyed the window. */
1191 1192
		return;

1193 1194 1195
	if (!wm_lookup_window(wm, unmap_notify->window, &window))
		return;

1196 1197 1198 1199 1200 1201 1202 1203
	if (window->surface_id) {
		/* Make sure we're not on the unpaired surface list or we
		 * could be assigned a surface during surface creation that
		 * was mapped before this unmap request.
		 */
		wl_list_remove(&window->link);
		window->surface_id = 0;
	}
1204 1205 1206 1207 1208
	if (wm->focus_window == window)
		wm->focus_window = NULL;
	if (window->surface)
		wl_list_remove(&window->surface_destroy_listener.link);
	window->surface = NULL;
1209
	window->shsurf = NULL;
1210 1211 1212 1213

	weston_wm_window_set_wm_state(window, ICCCM_WITHDRAWN_STATE);
	weston_wm_window_set_virtual_desktop(window, -1);

1214
	xcb_unmap_window(wm->conn, window->frame_id);
1215 1216
}

1217
static void
1218
weston_wm_window_draw_decoration(struct weston_wm_window *window)
1219 1220
{
	cairo_t *cr;
1221
	int width, height;
1222

1223
	wm_printf(window->wm, "XWM: draw decoration, win %d\n", window->id);
1224 1225 1226 1227 1228 1229

	weston_wm_window_get_frame_size(window, &width, &height);

	cairo_xcb_surface_set_size(window->cairo_surface, width, height);
	cr = cairo_create(window->cairo_surface);

1230 1231 1232
	if (window->fullscreen) {
		/* nothing */
	} else if (window->decorate) {
1233
		frame_set_title(window->frame, window->name);
1234
		frame_repaint(window->frame, cr);
1235 1236 1237 1238 1239
	} else {
		cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
		cairo_set_source_rgba(cr, 0, 0, 0, 0);
		cairo_paint(cr);

1240 1241
		render_shadow(cr, window->wm->theme->shadow,
			      2, 2, width + 8, height + 8, 64, 64);
1242 1243 1244
	}

	cairo_destroy(cr);
1245 1246
	cairo_surface_flush(window->cairo_surface);
	xcb_flush(window->wm->conn);
1247
}
1248

1249 1250 1251 1252 1253 1254 1255
static void
weston_wm_window_set_pending_state(struct weston_wm_window *window)
{
	int x, y, width, height;
	int32_t input_x, input_y, input_w, input_h;
	const struct weston_desktop_xwayland_interface *xwayland_interface =
		window->wm->server->compositor->xwayland_interface;
1256

1257 1258
	if (!window->surface)
		return;
1259

1260 1261
	weston_wm_window_get_frame_size(window, &width, &height);
	weston_wm_window_get_child_position(window, &x, &y);
1262

1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
	pixman_region32_fini(&window->surface->pending.opaque);
	if (window->has_alpha) {
		pixman_region32_init(&window->surface->pending.opaque);
	} else {
		/* We leave an extra pixel around the X window area to
		 * make sure we don't sample from the undefined alpha
		 * channel when filtering. */
		pixman_region32_init_rect(&window->surface->pending.opaque,
					  x - 1, y - 1,
					  window->width + 2,
					  window->height + 2);
	}
1275

1276 1277 1278 1279 1280 1281 1282 1283
	if (window->decorate && !window->fullscreen) {
		frame_input_rect(window->frame, &input_x, &input_y,
				 &input_w, &input_h);
	} else {
		input_x = x;
		input_y = y;
		input_w = width;
		input_h = height;
1284
	}
1285

1286 1287
	wm_printf(window->wm, "XWM: win %d geometry: %d,%d %dx%d\n",
		  window->id, input_x, input_y, input_w, input_h);
1288

1289
	pixman_region32_fini(&window->surface->pending.input);
1290 1291 1292 1293
	pixman_region32_init_rect(&window->surface->pending.input,
				  input_x, input_y, input_w, input_h);

	xwayland_interface->set_window_geometry(window->shsurf,
1294 1295
						input_x, input_y,
						input_w, input_h);
1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312
	if (window->name)
		xwayland_interface->set_title(window->shsurf, window->name);
	if (window->pid > 0)
		xwayland_interface->set_pid(window->shsurf, window->pid);
}

static void
weston_wm_window_do_repaint(void *data)
{
	struct weston_wm_window *window = data;

	window->repaint_source = NULL;

	weston_wm_window_read_properties(window);

	weston_wm_window_draw_decoration(window);
	weston_wm_window_set_pending_state(window);
1313 1314
}

1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
static void
weston_wm_window_set_pending_state_OR(struct weston_wm_window *window)
{
	int width, height;

	/* for override-redirect windows */
	assert(window->frame_id == XCB_WINDOW_NONE);

	if (!window->surface)
		return;

	weston_wm_window_get_frame_size(window, &width, &height);
	pixman_region32_fini(&window->surface->pending.opaque);
	if (window->has_alpha) {
		pixman_region32_init(&window->surface->pending.opaque);
	} else {
		pixman_region32_init_rect(&window->surface->pending.opaque, 0, 0,
					  width, height);
	}
}

1336 1337 1338 1339 1340
static void
weston_wm_window_schedule_repaint(struct weston_wm_window *window)
{
	struct weston_wm *wm = window->wm;

1341
	if (window->frame_id == XCB_WINDOW_NONE) {
1342 1343 1344 1345 1346 1347
		/* Override-redirect windows go through here, but we
		 * cannot assert(window->override_redirect); because
		 * we do not deal with changing OR flag yet.
		 * XXX: handle OR flag changes in message handlers
		 */
		weston_wm_window_set_pending_state_OR(window);
1348 1349 1350 1351
		return;
	}

	if (window->repaint_source)
1352 1353
		return;

1354
	wm_printf(wm, "XWM: schedule repaint, win %d\n", window->id);
1355

1356 1357
	window->repaint_source =
		wl_event_loop_add_idle(wm->server->loop,
1358
				       weston_wm_window_do_repaint, window);
1359 1360 1361 1362 1363 1364 1365 1366
}

static void
weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event)
{
	xcb_property_notify_event_t *property_notify =
		(xcb_property_notify_event_t *) event;
	struct weston_wm_window *window;
1367
	FILE *fp = NULL;
1368 1369
	char *logstr;
	size_t logsize;
1370
	char timestr[128];
1371

1372
	if (!wm_lookup_window(wm, property_notify->window, &window))
1373 1374 1375
		return;

	window->properties_dirty = 1;
1376

1377 1378 1379
	if (wm_debug_is_enabled(wm))
		fp = open_memstream(&logstr, &logsize);

1380
	if (fp) {
1381 1382 1383 1384
		fprintf(fp, "%s XCB_PROPERTY_NOTIFY: window %d, ",
			weston_debug_scope_timestamp(wm->server->wm_debug,
			timestr, sizeof timestr),
			property_notify->window);
1385 1386 1387 1388 1389 1390 1391 1392
		if (property_notify->state == XCB_PROPERTY_DELETE)
			fprintf(fp, "deleted %s\n",
					get_atom_name(wm->conn, property_notify->atom));
		else
			read_and_dump_property(fp, wm, property_notify->window,
					       property_notify->atom);

		if (fclose(fp) == 0)
1393 1394
			weston_debug_scope_write(wm->server->wm_debug,
						 logstr, logsize);
1395 1396 1397 1398 1399 1400 1401 1402
		free(logstr);
	} else {
		/* read_and_dump_property() is a X11 roundtrip.
		 * Mimic it to maintain ordering semantics between debug
		 * and non-debug paths.
		 */
		get_atom_name(wm->conn, property_notify->atom);
	}
1403 1404 1405

	if (property_notify->atom == wm->atom.net_wm_name ||
	    property_notify->atom == XCB_ATOM_WM_NAME)
1406 1407 1408 1409
		weston_wm_window_schedule_repaint(window);
}

static void
1410
weston_wm_window_create(struct weston_wm *wm,
1411
			xcb_window_t id, int width, int height, int x, int y, int override)
1412 1413 1414
{
	struct weston_wm_window *window;
	uint32_t values[1];
1415 1416
	xcb_get_geometry_cookie_t geometry_cookie;
	xcb_get_geometry_reply_t *geometry_reply;
1417

1418
	window = zalloc(sizeof *window);
1419
	if (window == NULL) {
1420
		wm_printf(wm, "failed to allocate window\n");
1421 1422 1423
		return;
	}

1424 1425
	geometry_cookie = xcb_get_geometry(wm->conn, id);

1426 1427
	values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE |
                    XCB_EVENT_MASK_FOCUS_CHANGE;
1428
	xcb_change_window_attributes(wm->conn, id, XCB_CW_EVENT_MASK, values);
1429 1430

	window->wm = wm;
1431
	window->id = id;
1432
	window->properties_dirty = 1;
1433
	window->override_redirect = override;
1434 1435
	window->width = width;
	window->height = height;
1436 1437
	window->x = x;
	window->y = y;
1438
	window->pos_dirty = false;
1439 1440
	window->map_request_x = INT_MIN; /* out of range for valid positions */
	window->map_request_y = INT_MIN; /* out of range for valid positions */
1441
	weston_output_weak_ref_init(&window->legacy_fullscreen_output);
1442

1443 1444 1445
	geometry_reply = xcb_get_geometry_reply(wm->conn, geometry_cookie, NULL);
	/* technically we should use XRender and check the visual format's
	alpha_mask, but checking depth is simpler and works in all known cases */
Dawid Gajownik's avatar
Dawid Gajownik committed
1446
	if (geometry_reply != NULL)
1447 1448 1449
		window->has_alpha = geometry_reply->depth == 32;
	free(geometry_reply);

1450 1451 1452 1453 1454 1455
	hash_table_insert(wm->window_hash, id, window);
}

static void
weston_wm_window_destroy(struct weston_wm_window *window)
{
1456 1457
	struct weston_wm *wm = window->wm;

1458 1459
	weston_output_weak_ref_clear(&window->legacy_fullscreen_output);

1460 1461 1462 1463 1464 1465 1466 1467 1468
	if (window->repaint_source)
		wl_event_source_remove(window->repaint_source);
	if (window->cairo_surface)
		cairo_surface_destroy(window->cairo_surface);

	if (window->frame_id) {
		xcb_reparent_window(wm->conn, window->id, wm->wm_window, 0, 0);
		xcb_destroy_window(wm->conn, window->frame_id);
		weston_wm_window_set_wm_state(window, ICCCM_WITHDRAWN_STATE);
1469
		weston_wm_window_set_virtual_desktop(window, -1);
1470 1471 1472 1473
		hash_table_remove(wm->window_hash, window->frame_id);
		window->frame_id = XCB_WINDOW_NONE;
	}

Scott Moreau's avatar
Scott Moreau committed
1474 1475 1476
	if (window->frame)
		frame_destroy(window->frame);

1477 1478 1479
	if (window->surface_id)
		wl_list_remove(&window->link);

1480 1481 1482
	if (window->surface)
		wl_list_remove(&window->surface_destroy_listener.link);

1483 1484 1485 1486 1487 1488 1489 1490 1491 1492
	hash_table_remove(window->wm->window_hash, window->id);
	free(window);
}

static void
weston_wm_handle_create_notify(struct weston_wm *wm, xcb_generic_event_t *event)
{
	xcb_create_notify_event_t *create_notify =
		(xcb_create_notify_event_t *) event;

1493 1494 1495 1496 1497 1498
	wm_printf(wm, "XCB_CREATE_NOTIFY (window %d, at (%d, %d), width %d, height %d%s%s)\n",
		  create_notify->window,
		  create_notify->x, create_notify->y,
		  create_notify->width, create_notify->height,
		  create_notify->override_redirect ? ", override" : "",
		  our_resource(wm, create_notify->window) ? ", ours" : "");
1499 1500 1501

	if (our_resource(wm, create_notify->window))
		return;
1502

1503
	weston_wm_window_create(wm, create_notify->window,
1504
				create_notify->width, create_notify->height,
1505
				create_notify->x, create_notify->y,
1506
				create_notify->override_redirect);
1507 1508 1509 1510 1511 1512 1513 1514 1515
}

static void
weston_wm_handle_destroy_notify(struct weston_wm *wm, xcb_generic_event_t *event)
{
	xcb_destroy_notify_event_t *destroy_notify =
		(xcb_destroy_notify_event_t *) event;
	struct weston_wm_window *window;

1516 1517 1518 1519
	wm_printf(wm, "XCB_DESTROY_NOTIFY, win %d, event %d%s\n",
		  destroy_notify->window,
		  destroy_notify->event,
		  our_resource(wm, destroy_notify->window) ? ", ours" : "");
1520 1521

	if (our_resource(wm, destroy_notify->window))
1522 1523
		return;

1524 1525 1526
	if (!wm_lookup_window(wm, destroy_notify->window, &window))
		return;

1527 1528
	weston_wm_window_destroy(window);
}
1529

1530 1531 1532 1533 1534 1535
static void
weston_wm_handle_reparent_notify(struct weston_wm *wm, xcb_generic_event_t *event)
{
	xcb_reparent_notify_event_t *reparent_notify =
		(xcb_reparent_notify_event_t *) event;
	struct weston_wm_window *window;
1536

1537 1538 1539 1540 1541
	wm_printf(wm, "XCB_REPARENT_NOTIFY (window %d, parent %d, event %d%s)\n",
		  reparent_notify->window,
		  reparent_notify->parent,
		  reparent_notify->event,
		  reparent_notify->override_redirect ? ", override" : "");
1542 1543

	if (reparent_notify->parent == wm->screen->root) {
1544
		weston_wm_window_create(wm, reparent_notify->window, 10, 10,
1545
					reparent_notify->x, reparent_notify->y,
1546
					reparent_notify->override_redirect);
1547
	} else if (!our_resource(wm, reparent_notify->parent)) {
1548 1549
		if (!