blob: 6d4ce1bbf11a55a954c4bbb622655c8d46808d44 [file] [log] [blame]
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001/*
2 * Copyright © 2011 Intel Corporation
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#define _GNU_SOURCE
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28#include <sys/socket.h>
29#include <sys/un.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <unistd.h>
33#include <signal.h>
Tiago Vignatti90fada42012-07-16 12:02:08 -040034#include <X11/Xcursor/Xcursor.h>
Kristian Høgsberg380deee2012-05-21 17:12:41 -040035
36#include "xwayland.h"
37
38#include "../../shared/cairo-util.h"
39#include "../compositor.h"
40#include "xserver-server-protocol.h"
41#include "hash.h"
42
43struct motif_wm_hints {
44 uint32_t flags;
45 uint32_t functions;
46 uint32_t decorations;
47 int32_t input_mode;
48 uint32_t status;
49};
50
51#define MWM_HINTS_FUNCTIONS (1L << 0)
52#define MWM_HINTS_DECORATIONS (1L << 1)
53#define MWM_HINTS_INPUT_MODE (1L << 2)
54#define MWM_HINTS_STATUS (1L << 3)
55
56#define MWM_FUNC_ALL (1L << 0)
57#define MWM_FUNC_RESIZE (1L << 1)
58#define MWM_FUNC_MOVE (1L << 2)
59#define MWM_FUNC_MINIMIZE (1L << 3)
60#define MWM_FUNC_MAXIMIZE (1L << 4)
61#define MWM_FUNC_CLOSE (1L << 5)
62
63#define MWM_DECOR_ALL (1L << 0)
64#define MWM_DECOR_BORDER (1L << 1)
65#define MWM_DECOR_RESIZEH (1L << 2)
66#define MWM_DECOR_TITLE (1L << 3)
67#define MWM_DECOR_MENU (1L << 4)
68#define MWM_DECOR_MINIMIZE (1L << 5)
69#define MWM_DECOR_MAXIMIZE (1L << 6)
70
71#define MWM_INPUT_MODELESS 0
72#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
73#define MWM_INPUT_SYSTEM_MODAL 2
74#define MWM_INPUT_FULL_APPLICATION_MODAL 3
75#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL
76
77#define MWM_TEAROFF_WINDOW (1L<<0)
78
79#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
80#define _NET_WM_MOVERESIZE_SIZE_TOP 1
81#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
82#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
83#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
84#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
85#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
86#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
87#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */
88#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
89#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
90#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
91
92
93
94struct weston_wm_window {
95 struct weston_wm *wm;
96 xcb_window_t id;
97 xcb_window_t frame_id;
98 cairo_surface_t *cairo_surface;
99 struct weston_surface *surface;
100 struct shell_surface *shsurf;
101 struct wl_listener surface_destroy_listener;
102 struct wl_event_source *repaint_source;
Kristian Høgsberga61ca062012-05-22 16:05:52 -0400103 struct wl_event_source *configure_source;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400104 int properties_dirty;
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300105 int pid;
106 char *machine;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400107 char *class;
108 char *name;
109 struct weston_wm_window *transient_for;
110 uint32_t protocols;
111 xcb_atom_t type;
112 int width, height;
113 int x, y;
114 int decorate;
Tiago Vignatti771241e2012-06-04 20:01:45 +0300115 int override_redirect;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400116};
117
118static struct weston_wm_window *
119get_wm_window(struct weston_surface *surface);
120
Kristian Høgsbergeaee7842012-05-22 10:04:20 -0400121static void
122weston_wm_window_schedule_repaint(struct weston_wm_window *window);
123
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400124const char *
125get_atom_name(xcb_connection_t *c, xcb_atom_t atom)
126{
127 xcb_get_atom_name_cookie_t cookie;
128 xcb_get_atom_name_reply_t *reply;
129 xcb_generic_error_t *e;
130 static char buffer[64];
131
132 if (atom == XCB_ATOM_NONE)
133 return "None";
134
135 cookie = xcb_get_atom_name (c, atom);
136 reply = xcb_get_atom_name_reply (c, cookie, &e);
137 snprintf(buffer, sizeof buffer, "%.*s",
138 xcb_get_atom_name_name_length (reply),
139 xcb_get_atom_name_name (reply));
140 free(reply);
141
142 return buffer;
143}
144
Tiago Vignatti90fada42012-07-16 12:02:08 -0400145static xcb_cursor_t
146xcb_cursor_image_load_cursor(struct weston_wm *wm, const XcursorImage *img)
147{
148 xcb_connection_t *c = wm->conn;
149 xcb_screen_iterator_t s = xcb_setup_roots_iterator(xcb_get_setup(c));
150 xcb_screen_t *screen = s.data;
151 xcb_gcontext_t gc;
152 xcb_pixmap_t pix;
153 xcb_render_picture_t pic;
154 xcb_cursor_t cursor;
155 int stride = img->width * 4;
156
157 pix = xcb_generate_id(c);
158 xcb_create_pixmap(c, 32, pix, screen->root, img->width, img->height);
159
160 pic = xcb_generate_id(c);
161 xcb_render_create_picture(c, pic, pix, wm->format_rgba.id, 0, 0);
162
163 gc = xcb_generate_id(c);
164 xcb_create_gc(c, gc, pix, 0, 0);
165
166 xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc,
167 img->width, img->height, 0, 0, 0, 32,
168 stride * img->height, (uint8_t *) img->pixels);
169 xcb_free_gc(c, gc);
170
171 cursor = xcb_generate_id(c);
172 xcb_render_create_cursor(c, cursor, pic, img->xhot, img->yhot);
173
174 xcb_render_free_picture(c, pic);
175 xcb_free_pixmap(c, pix);
176
177 return cursor;
178}
179
180static xcb_cursor_t
181xcb_cursor_images_load_cursor(struct weston_wm *wm, const XcursorImages *images)
182{
183 /* TODO: treat animated cursors as well */
184 if (images->nimage != 1)
185 return -1;
186
187 return xcb_cursor_image_load_cursor(wm, images->images[0]);
188}
189
190static xcb_cursor_t
191xcb_cursor_library_load_cursor(struct weston_wm *wm, const char *file)
192{
193 xcb_cursor_t cursor;
194 XcursorImages *images;
195 char *v = NULL;
196 int size = 0;
197
198 if (!file)
199 return 0;
200
201 v = getenv ("XCURSOR_SIZE");
202 if (v)
203 size = atoi(v);
204
205 if (!size)
206 size = 32;
207
208 images = XcursorLibraryLoadImages (file, NULL, size);
209 cursor = xcb_cursor_images_load_cursor (wm, images);
210 XcursorImagesDestroy (images);
211
212 return cursor;
213}
214
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400215void
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400216dump_property(struct weston_wm *wm,
217 xcb_atom_t property, xcb_get_property_reply_t *reply)
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400218{
219 int32_t *incr_value;
220 const char *text_value, *name;
221 xcb_atom_t *atom_value;
222 int width, len;
223 uint32_t i;
224
Martin Minarik6d118362012-06-07 18:01:59 +0200225 width = weston_log_continue("%s: ", get_atom_name(wm->conn, property));
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400226 if (reply == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200227 weston_log_continue("(no reply)\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400228 return;
229 }
230
Martin Minarik6d118362012-06-07 18:01:59 +0200231 width += weston_log_continue(
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400232 "%s/%d, length %d (value_len %d): ",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400233 get_atom_name(wm->conn, reply->type),
234 reply->format,
235 xcb_get_property_value_length(reply),
236 reply->value_len);
237
238 if (reply->type == wm->atom.incr) {
239 incr_value = xcb_get_property_value(reply);
Martin Minarik6d118362012-06-07 18:01:59 +0200240 weston_log_continue("%d\n", *incr_value);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400241 } else if (reply->type == wm->atom.utf8_string ||
242 reply->type == wm->atom.string) {
243 text_value = xcb_get_property_value(reply);
244 if (reply->value_len > 40)
245 len = 40;
246 else
247 len = reply->value_len;
Martin Minarik6d118362012-06-07 18:01:59 +0200248 weston_log_continue("\"%.*s\"\n", len, text_value);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400249 } else if (reply->type == XCB_ATOM_ATOM) {
250 atom_value = xcb_get_property_value(reply);
251 for (i = 0; i < reply->value_len; i++) {
252 name = get_atom_name(wm->conn, atom_value[i]);
253 if (width + strlen(name) + 2 > 78) {
Martin Minarik6d118362012-06-07 18:01:59 +0200254 weston_log_continue("\n ");
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400255 width = 4;
256 } else if (i > 0) {
Martin Minarik6d118362012-06-07 18:01:59 +0200257 width += weston_log_continue(", ");
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400258 }
259
Martin Minarik6d118362012-06-07 18:01:59 +0200260 width += weston_log_continue("%s", name);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400261 }
Martin Minarik6d118362012-06-07 18:01:59 +0200262 weston_log_continue("\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400263 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200264 weston_log_continue("huh?\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400265 }
266}
267
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400268void
269read_and_dump_property(struct weston_wm *wm,
270 xcb_window_t window, xcb_atom_t property)
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400271{
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400272 xcb_get_property_reply_t *reply;
273 xcb_get_property_cookie_t cookie;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400274
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400275 cookie = xcb_get_property(wm->conn, 0, window,
276 property, XCB_ATOM_ANY, 0, 2048);
277 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400278
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400279 dump_property(wm, property, reply);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400280
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400281 free(reply);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400282}
283
284/* We reuse some predefined, but otherwise useles atoms */
285#define TYPE_WM_PROTOCOLS XCB_ATOM_CUT_BUFFER0
286#define TYPE_MOTIF_WM_HINTS XCB_ATOM_CUT_BUFFER1
287
288static void
289weston_wm_window_read_properties(struct weston_wm_window *window)
290{
291 struct weston_wm *wm = window->wm;
292
293#define F(field) offsetof(struct weston_wm_window, field)
294 const struct {
295 xcb_atom_t atom;
296 xcb_atom_t type;
297 int offset;
298 } props[] = {
299 { XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, F(class) },
300 { XCB_ATOM_WM_NAME, XCB_ATOM_STRING, F(name) },
301 { XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, F(transient_for) },
302 { wm->atom.wm_protocols, TYPE_WM_PROTOCOLS, F(protocols) },
303 { wm->atom.net_wm_window_type, XCB_ATOM_ATOM, F(type) },
304 { wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) },
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300305 { wm->atom.net_wm_pid, XCB_ATOM_CARDINAL, F(pid) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400306 { wm->atom.motif_wm_hints, TYPE_MOTIF_WM_HINTS, 0 },
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300307 { wm->atom.wm_client_machine, XCB_ATOM_WM_CLIENT_MACHINE, F(machine) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400308 };
309#undef F
310
311 xcb_get_property_cookie_t cookie[ARRAY_LENGTH(props)];
312 xcb_get_property_reply_t *reply;
313 void *p;
314 uint32_t *xid;
315 xcb_atom_t *atom;
316 uint32_t i;
317 struct motif_wm_hints *hints;
318
319 if (!window->properties_dirty)
320 return;
321 window->properties_dirty = 0;
322
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400323 for (i = 0; i < ARRAY_LENGTH(props); i++)
324 cookie[i] = xcb_get_property(wm->conn,
325 0, /* delete */
326 window->id,
327 props[i].atom,
328 XCB_ATOM_ANY, 0, 2048);
329
Tiago Vignatti2ea74d92012-07-20 23:09:53 +0300330 window->decorate = !window->override_redirect;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400331 for (i = 0; i < ARRAY_LENGTH(props); i++) {
332 reply = xcb_get_property_reply(wm->conn, cookie[i], NULL);
333 if (!reply)
334 /* Bad window, typically */
335 continue;
336 if (reply->type == XCB_ATOM_NONE) {
337 /* No such property */
338 free(reply);
339 continue;
340 }
341
342 p = ((char *) window + props[i].offset);
343
344 switch (props[i].type) {
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300345 case XCB_ATOM_WM_CLIENT_MACHINE:
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400346 case XCB_ATOM_STRING:
347 /* FIXME: We're using this for both string and
348 utf8_string */
349 if (*(char **) p)
350 free(*(char **) p);
351
352 *(char **) p =
353 strndup(xcb_get_property_value(reply),
354 xcb_get_property_value_length(reply));
355 break;
356 case XCB_ATOM_WINDOW:
357 xid = xcb_get_property_value(reply);
358 *(struct weston_wm_window **) p =
359 hash_table_lookup(wm->window_hash, *xid);
360 break;
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300361 case XCB_ATOM_CARDINAL:
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400362 case XCB_ATOM_ATOM:
363 atom = xcb_get_property_value(reply);
364 *(xcb_atom_t *) p = *atom;
365 break;
366 case TYPE_WM_PROTOCOLS:
367 break;
368 case TYPE_MOTIF_WM_HINTS:
369 hints = xcb_get_property_value(reply);
370 if (hints->flags & MWM_HINTS_DECORATIONS)
371 window->decorate = hints->decorations > 0;
372 break;
373 default:
374 break;
375 }
376 free(reply);
377 }
378}
379
380static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400381weston_wm_window_get_frame_size(struct weston_wm_window *window,
382 int *width, int *height)
383{
384 struct theme *t = window->wm->theme;
385
386 if (window->decorate) {
387 *width = window->width + (t->margin + t->width) * 2;
388 *height = window->height +
389 t->margin * 2 + t->width + t->titlebar_height;
390 } else {
391 *width = window->width + t->margin * 2;
392 *height = window->height + t->margin * 2;
393 }
394}
395
396static void
397weston_wm_window_get_child_position(struct weston_wm_window *window,
398 int *x, int *y)
399{
400 struct theme *t = window->wm->theme;
401
402 if (window->decorate) {
403 *x = t->margin + t->width;
404 *y = t->margin + t->titlebar_height;
405 } else {
406 *x = t->margin;
407 *y = t->margin;
408 }
409}
Kristian Høgsbergeaee7842012-05-22 10:04:20 -0400410
411static void
412weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event)
413{
414 xcb_configure_request_event_t *configure_request =
415 (xcb_configure_request_event_t *) event;
416 struct weston_wm_window *window;
417 uint32_t mask, values[16];
418 int x, y, width, height, i = 0;
419
Martin Minarik6d118362012-06-07 18:01:59 +0200420 weston_log("XCB_CONFIGURE_REQUEST (window %d) %d,%d @ %dx%d\n",
Kristian Høgsbergeaee7842012-05-22 10:04:20 -0400421 configure_request->window,
422 configure_request->x, configure_request->y,
423 configure_request->width, configure_request->height);
424
425 window = hash_table_lookup(wm->window_hash, configure_request->window);
426
427 if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
428 window->width = configure_request->width;
429 if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
430 window->height = configure_request->height;
431
432 weston_wm_window_get_child_position(window, &x, &y);
433 values[i++] = x;
434 values[i++] = y;
435 values[i++] = window->width;
436 values[i++] = window->height;
437 values[i++] = 0;
438 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
439 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
440 XCB_CONFIG_WINDOW_BORDER_WIDTH;
441 if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
442 values[i++] = configure_request->sibling;
443 mask |= XCB_CONFIG_WINDOW_SIBLING;
444 }
445 if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
446 values[i++] = configure_request->stack_mode;
447 mask |= XCB_CONFIG_WINDOW_STACK_MODE;
448 }
449
450 xcb_configure_window(wm->conn, window->id, mask, values);
451
452 weston_wm_window_get_frame_size(window, &width, &height);
453 values[0] = width;
454 values[1] = height;
455 mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
456 xcb_configure_window(wm->conn, window->frame_id, mask, values);
457
458 weston_wm_window_schedule_repaint(window);
459}
460
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400461static void
462weston_wm_handle_configure_notify(struct weston_wm *wm, xcb_generic_event_t *event)
463{
464 xcb_configure_notify_event_t *configure_notify =
465 (xcb_configure_notify_event_t *) event;
466 struct weston_wm_window *window;
Tiago Vignattie66fcee2012-07-20 23:09:54 +0300467 int x, y;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400468
469 window = hash_table_lookup(wm->window_hash, configure_notify->window);
470
Martin Minarik6d118362012-06-07 18:01:59 +0200471 weston_log("XCB_CONFIGURE_NOTIFY (%s window %d) %d,%d @ %dx%d\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400472 configure_notify->window == window->id ? "client" : "frame",
473 configure_notify->window,
474 configure_notify->x, configure_notify->y,
475 configure_notify->width, configure_notify->height);
Tiago Vignattie66fcee2012-07-20 23:09:54 +0300476
477 /* resize falls here */
478 if (configure_notify->window != window->id)
479 return;
480
481 weston_wm_window_get_child_position(window, &x, &y);
482 window->x = configure_notify->x - x;
483 window->y = configure_notify->y - y;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400484}
485
486static void
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300487weston_wm_kill_client(struct wl_listener *listener, void *data)
488{
489 struct weston_surface *surface = data;
490 struct weston_wm_window *window = get_wm_window(surface);
491 char name[1024];
492
493 if (!window)
494 return;
495
496 gethostname(name, 1024);
497
498 /* this is only one heuristic to guess the PID of a client is valid,
499 * assuming it's compliant with icccm and ewmh. Non-compliants and
500 * remote applications of course fail. */
501 if (!strcmp(window->machine, name) && window->pid != 0)
502 kill(window->pid, SIGKILL);
503}
504
505static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400506weston_wm_window_activate(struct wl_listener *listener, void *data)
507{
508 struct weston_surface *surface = data;
509 struct weston_wm_window *window = get_wm_window(surface);
Scott Moreau85ecac02012-05-21 15:49:14 -0600510 struct weston_wm *wm =
511 container_of(listener, struct weston_wm, activate_listener);
512 xcb_client_message_event_t client_message;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400513
Scott Moreau85ecac02012-05-21 15:49:14 -0600514 if (window) {
515 client_message.response_type = XCB_CLIENT_MESSAGE;
516 client_message.format = 32;
517 client_message.window = window->id;
518 client_message.type = wm->atom.wm_protocols;
519 client_message.data.data32[0] = wm->atom.wm_take_focus;
520 client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
521
522 xcb_send_event(wm->conn, 0, window->id,
523 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
524 (char *) &client_message);
525
526 xcb_set_input_focus (wm->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
527 window->id, XCB_TIME_CURRENT_TIME);
528 } else {
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400529 xcb_set_input_focus (wm->conn,
530 XCB_INPUT_FOCUS_POINTER_ROOT,
531 XCB_NONE,
532 XCB_TIME_CURRENT_TIME);
Scott Moreau85ecac02012-05-21 15:49:14 -0600533 }
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400534
535 if (wm->focus_window)
536 weston_wm_window_schedule_repaint(wm->focus_window);
537 wm->focus_window = window;
Tiago Vignattice1baa82012-07-20 23:09:55 +0300538 if (window)
539 wm->focus_latest = window;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400540 if (wm->focus_window)
541 weston_wm_window_schedule_repaint(wm->focus_window);
542}
543
544static int
545our_resource(struct weston_wm *wm, uint32_t id)
546{
547 const xcb_setup_t *setup;
548
549 setup = xcb_get_setup(wm->conn);
550
551 return (id & ~setup->resource_id_mask) == setup->resource_id_base;
552}
553
Kristian Høgsberga6d9a5e2012-05-30 12:15:44 -0400554#define ICCCM_WITHDRAWN_STATE 0
555#define ICCCM_NORMAL_STATE 1
556#define ICCCM_ICONIC_STATE 3
557
558static void
559weston_wm_window_set_state(struct weston_wm_window *window, int32_t state)
560{
561 struct weston_wm *wm = window->wm;
562 uint32_t property[2];
563
564 property[0] = state;
565 property[1] = XCB_WINDOW_NONE;
566
567 xcb_change_property(wm->conn,
568 XCB_PROP_MODE_REPLACE,
569 window->id,
570 wm->atom.wm_state,
571 wm->atom.wm_state,
572 32, /* format */
573 2, property);
574}
575
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400576static void
577weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
578{
579 xcb_map_request_event_t *map_request =
580 (xcb_map_request_event_t *) event;
581 struct weston_wm_window *window;
582 uint32_t values[1];
583 int x, y, width, height;
584
585 if (our_resource(wm, map_request->window)) {
Martin Minarik6d118362012-06-07 18:01:59 +0200586 weston_log("XCB_MAP_REQUEST (window %d, ours)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400587 map_request->window);
588 return;
589 }
590
591 window = hash_table_lookup(wm->window_hash, map_request->window);
592
Kristian Høgsbergbc6e1622012-05-30 09:59:56 -0400593 if (window->frame_id)
594 return;
595
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400596 weston_wm_window_read_properties(window);
597
598 weston_wm_window_get_frame_size(window, &width, &height);
599 weston_wm_window_get_child_position(window, &x, &y);
600
601 values[0] =
602 XCB_EVENT_MASK_KEY_PRESS |
603 XCB_EVENT_MASK_KEY_RELEASE |
604 XCB_EVENT_MASK_BUTTON_PRESS |
605 XCB_EVENT_MASK_BUTTON_RELEASE |
Tiago Vignatti236b48d2012-07-16 12:09:19 -0400606 XCB_EVENT_MASK_POINTER_MOTION |
607 XCB_EVENT_MASK_ENTER_WINDOW |
608 XCB_EVENT_MASK_LEAVE_WINDOW |
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400609 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
Kristian Høgsberg44c20132012-05-30 10:09:21 -0400610 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400611
612 window->frame_id = xcb_generate_id(wm->conn);
613 xcb_create_window(wm->conn,
614 XCB_COPY_FROM_PARENT,
615 window->frame_id,
616 wm->screen->root,
617 0, 0,
618 width, height,
619 0,
620 XCB_WINDOW_CLASS_INPUT_OUTPUT,
621 wm->screen->root_visual,
622 XCB_CW_EVENT_MASK, values);
623 xcb_reparent_window(wm->conn, window->id, window->frame_id, x, y);
624
625 values[0] = 0;
626 xcb_configure_window(wm->conn, window->id,
627 XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
628
Martin Minarik6d118362012-06-07 18:01:59 +0200629 weston_log("XCB_MAP_REQUEST (window %d, %p, frame %d)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400630 window->id, window, window->frame_id);
631
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400632 xcb_map_window(wm->conn, map_request->window);
633 xcb_map_window(wm->conn, window->frame_id);
Kristian Høgsberga6d9a5e2012-05-30 12:15:44 -0400634 weston_wm_window_set_state(window, ICCCM_NORMAL_STATE);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400635
636 window->cairo_surface =
637 cairo_xcb_surface_create_with_xrender_format(wm->conn,
638 wm->screen,
639 window->frame_id,
Kristian Høgsberge89cef32012-07-16 11:57:08 -0400640 &wm->format_rgb,
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400641 width, height);
642
643 hash_table_insert(wm->window_hash, window->frame_id, window);
644}
645
646static void
647weston_wm_handle_map_notify(struct weston_wm *wm, xcb_generic_event_t *event)
648{
649 xcb_map_notify_event_t *map_notify = (xcb_map_notify_event_t *) event;
650
651 if (our_resource(wm, map_notify->window)) {
Martin Minarik6d118362012-06-07 18:01:59 +0200652 weston_log("XCB_MAP_NOTIFY (window %d, ours)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400653 map_notify->window);
654 return;
655 }
656
Martin Minarik6d118362012-06-07 18:01:59 +0200657 weston_log("XCB_MAP_NOTIFY (window %d)\n", map_notify->window);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400658}
659
660static void
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400661weston_wm_handle_unmap_notify(struct weston_wm *wm, xcb_generic_event_t *event)
662{
663 xcb_unmap_notify_event_t *unmap_notify =
664 (xcb_unmap_notify_event_t *) event;
665 struct weston_wm_window *window;
666
Martin Minarik6d118362012-06-07 18:01:59 +0200667 weston_log("XCB_UNMAP_NOTIFY (window %d, event %d%s)\n",
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400668 unmap_notify->window,
669 unmap_notify->event,
670 our_resource(wm, unmap_notify->window) ? ", ours" : "");
671
672 if (our_resource(wm, unmap_notify->window))
673 return;
674
Kristian Høgsbergd64bdf42012-05-31 10:33:43 -0400675 if (unmap_notify->response_type & 0x80)
676 /* We just ignore the ICCCM 4.1.4 synthetic unmap notify
677 * as it may come in after we've destroyed the window. */
Kristian Høgsbergf197e9f2012-05-30 11:46:29 -0400678 return;
Kristian Høgsbergf197e9f2012-05-30 11:46:29 -0400679
Kristian Høgsbergd64bdf42012-05-31 10:33:43 -0400680 window = hash_table_lookup(wm->window_hash, unmap_notify->window);
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400681 if (window->repaint_source)
682 wl_event_source_remove(window->repaint_source);
683 if (window->cairo_surface)
684 cairo_surface_destroy(window->cairo_surface);
685
Kristian Høgsbergbe375b32012-06-05 11:46:08 -0400686 if (window->frame_id) {
687 xcb_reparent_window(wm->conn, window->id, wm->wm_window, 0, 0);
688 xcb_destroy_window(wm->conn, window->frame_id);
689 weston_wm_window_set_state(window, ICCCM_WITHDRAWN_STATE);
690 hash_table_remove(wm->window_hash, window->frame_id);
691 window->frame_id = XCB_WINDOW_NONE;
692 }
Kristian Høgsberga6d9a5e2012-05-30 12:15:44 -0400693
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400694 if (wm->focus_window == window)
695 wm->focus_window = NULL;
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400696 if (window->surface)
697 wl_list_remove(&window->surface_destroy_listener.link);
698 window->surface = NULL;
699}
700
701static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400702weston_wm_window_draw_decoration(void *data)
703{
704 struct weston_wm_window *window = data;
705 struct weston_wm *wm = window->wm;
706 struct theme *t = wm->theme;
707 cairo_t *cr;
708 int x, y, width, height;
709 const char *title;
710 uint32_t flags = 0;
711
712 weston_wm_window_read_properties(window);
713
714 window->repaint_source = NULL;
715
716 weston_wm_window_get_frame_size(window, &width, &height);
717 weston_wm_window_get_child_position(window, &x, &y);
718
719 cairo_xcb_surface_set_size(window->cairo_surface, width, height);
720 cr = cairo_create(window->cairo_surface);
721
722 if (window->decorate) {
723 if (wm->focus_window == window)
724 flags |= THEME_FRAME_ACTIVE;
725
726 if (window->name)
727 title = window->name;
728 else
729 title = "untitled";
730
731 theme_render_frame(t, cr, width, height, title, flags);
732 } else {
733 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
734 cairo_set_source_rgba(cr, 0, 0, 0, 0);
735 cairo_paint(cr);
736
737 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
738 cairo_set_source_rgba(cr, 0, 0, 0, 0.45);
739 tile_mask(cr, t->shadow, 2, 2, width + 8, height + 8, 64, 64);
740 }
741
742 cairo_destroy(cr);
743
744 if (window->surface) {
Pekka Paalanen4f9c07b2012-09-03 16:48:41 +0300745 pixman_region32_fini(&window->surface->opaque);
746 pixman_region32_init_rect(&window->surface->opaque, 0, 0,
747 width, height);
748
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400749 /* We leave an extra pixel around the X window area to
750 * make sure we don't sample from the undefined alpha
751 * channel when filtering. */
Pekka Paalanen4f9c07b2012-09-03 16:48:41 +0300752 pixman_region32_intersect_rect(&window->surface->opaque,
753 &window->surface->opaque,
754 x - 1, y - 1,
755 window->width + 2,
756 window->height + 2);
757 window->surface->geometry.dirty = 1;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400758
759 pixman_region32_init_rect(&window->surface->input,
760 t->margin, t->margin,
761 width - 2 * t->margin,
762 height - 2 * t->margin);
763 }
764}
765
766static void
767weston_wm_window_schedule_repaint(struct weston_wm_window *window)
768{
769 struct weston_wm *wm = window->wm;
Pekka Paalanen4f9c07b2012-09-03 16:48:41 +0300770 int width, height;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400771
Kristian Høgsbergc4063f32012-07-22 15:32:45 -0400772 if (window->frame_id == XCB_WINDOW_NONE) {
773 if (window->surface != NULL) {
Pekka Paalanen4f9c07b2012-09-03 16:48:41 +0300774 weston_wm_window_get_frame_size(window, &width, &height);
775 pixman_region32_fini(&window->surface->opaque);
776 pixman_region32_init_rect(&window->surface->opaque, 0, 0,
777 width, height);
778 window->surface->geometry.dirty = 1;
Kristian Høgsbergc4063f32012-07-22 15:32:45 -0400779 }
780 return;
781 }
782
783 if (window->repaint_source)
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400784 return;
785
786 window->repaint_source =
787 wl_event_loop_add_idle(wm->server->loop,
788 weston_wm_window_draw_decoration,
789 window);
790}
791
792static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400793weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event)
794{
795 xcb_property_notify_event_t *property_notify =
796 (xcb_property_notify_event_t *) event;
797 struct weston_wm_window *window;
798
799 window = hash_table_lookup(wm->window_hash, property_notify->window);
800 if (window)
801 window->properties_dirty = 1;
802
Martin Minarik6d118362012-06-07 18:01:59 +0200803 weston_log("XCB_PROPERTY_NOTIFY: window %d, ",
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400804 property_notify->window);
Kristian Høgsberge244cb02012-05-30 11:34:35 -0400805 if (property_notify->state == XCB_PROPERTY_DELETE)
Martin Minarik6d118362012-06-07 18:01:59 +0200806 weston_log("deleted\n");
Kristian Høgsberge244cb02012-05-30 11:34:35 -0400807 else
808 read_and_dump_property(wm, property_notify->window,
809 property_notify->atom);
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400810
811 if (property_notify->atom == wm->atom.net_wm_name ||
812 property_notify->atom == XCB_ATOM_WM_NAME)
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400813 weston_wm_window_schedule_repaint(window);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400814}
815
816static void
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400817weston_wm_window_create(struct weston_wm *wm,
Tiago Vignatti771241e2012-06-04 20:01:45 +0300818 xcb_window_t id, int width, int height, int override)
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400819{
820 struct weston_wm_window *window;
821 uint32_t values[1];
822
823 window = malloc(sizeof *window);
824 if (window == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200825 weston_log("failed to allocate window\n");
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400826 return;
827 }
828
829 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
830 xcb_change_window_attributes(wm->conn, id, XCB_CW_EVENT_MASK, values);
831
832 memset(window, 0, sizeof *window);
833 window->wm = wm;
834 window->id = id;
835 window->properties_dirty = 1;
Tiago Vignatti771241e2012-06-04 20:01:45 +0300836 window->override_redirect = override;
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400837 window->width = width;
838 window->height = height;
839
840 hash_table_insert(wm->window_hash, id, window);
841}
842
843static void
844weston_wm_window_destroy(struct weston_wm_window *window)
845{
846 hash_table_remove(window->wm->window_hash, window->id);
847 free(window);
848}
849
850static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400851weston_wm_handle_create_notify(struct weston_wm *wm, xcb_generic_event_t *event)
852{
853 xcb_create_notify_event_t *create_notify =
854 (xcb_create_notify_event_t *) event;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400855
Martin Minarik6d118362012-06-07 18:01:59 +0200856 weston_log("XCB_CREATE_NOTIFY (window %d, width %d, height %d%s%s)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400857 create_notify->window,
858 create_notify->width, create_notify->height,
859 create_notify->override_redirect ? ", override" : "",
860 our_resource(wm, create_notify->window) ? ", ours" : "");
861
862 if (our_resource(wm, create_notify->window))
863 return;
864
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400865 weston_wm_window_create(wm, create_notify->window,
Tiago Vignatti771241e2012-06-04 20:01:45 +0300866 create_notify->width, create_notify->height,
867 create_notify->override_redirect);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400868}
869
870static void
871weston_wm_handle_destroy_notify(struct weston_wm *wm, xcb_generic_event_t *event)
872{
873 xcb_destroy_notify_event_t *destroy_notify =
874 (xcb_destroy_notify_event_t *) event;
875 struct weston_wm_window *window;
876
Martin Minarik6d118362012-06-07 18:01:59 +0200877 weston_log("XCB_DESTROY_NOTIFY, win %d, event %d%s\n",
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400878 destroy_notify->window,
879 destroy_notify->event,
880 our_resource(wm, destroy_notify->window) ? ", ours" : "");
881
882 if (our_resource(wm, destroy_notify->window))
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400883 return;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400884
885 window = hash_table_lookup(wm->window_hash, destroy_notify->window);
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400886 weston_wm_window_destroy(window);
887}
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400888
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400889static void
890weston_wm_handle_reparent_notify(struct weston_wm *wm, xcb_generic_event_t *event)
891{
892 xcb_reparent_notify_event_t *reparent_notify =
893 (xcb_reparent_notify_event_t *) event;
894 struct weston_wm_window *window;
Kristian Høgsbergc9571fb2012-05-29 15:35:29 -0400895
Martin Minarik6d118362012-06-07 18:01:59 +0200896 weston_log("XCB_REPARENT_NOTIFY (window %d, parent %d, event %d)\n",
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400897 reparent_notify->window,
898 reparent_notify->parent,
899 reparent_notify->event);
900
901 if (reparent_notify->parent == wm->screen->root) {
Tiago Vignatti771241e2012-06-04 20:01:45 +0300902 weston_wm_window_create(wm, reparent_notify->window, 10, 10,
903 reparent_notify->override_redirect);
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400904 } else if (!our_resource(wm, reparent_notify->parent)) {
905 window = hash_table_lookup(wm->window_hash,
906 reparent_notify->window);
907 weston_wm_window_destroy(window);
908 }
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400909}
910
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400911struct weston_seat *
912weston_wm_pick_seat(struct weston_wm *wm)
913{
914 return container_of(wm->server->compositor->seat_list.next,
915 struct weston_seat, link);
916}
917
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400918static void
Kristian Høgsberge68fd102012-05-22 17:09:40 -0400919weston_wm_window_handle_moveresize(struct weston_wm_window *window,
920 xcb_client_message_event_t *client_message)
921{
922 static const int map[] = {
923 THEME_LOCATION_RESIZING_TOP_LEFT,
924 THEME_LOCATION_RESIZING_TOP,
925 THEME_LOCATION_RESIZING_TOP_RIGHT,
926 THEME_LOCATION_RESIZING_RIGHT,
927 THEME_LOCATION_RESIZING_BOTTOM_RIGHT,
928 THEME_LOCATION_RESIZING_BOTTOM,
929 THEME_LOCATION_RESIZING_BOTTOM_LEFT,
930 THEME_LOCATION_RESIZING_LEFT
931 };
932
933 struct weston_wm *wm = window->wm;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400934 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberge68fd102012-05-22 17:09:40 -0400935 int detail;
936 struct weston_shell_interface *shell_interface =
937 &wm->server->compositor->shell_interface;
938
939 if (seat->seat.pointer->button_count != 1 ||
940 seat->seat.pointer->focus != &window->surface->surface)
941 return;
942
943 detail = client_message->data.data32[2];
944 switch (detail) {
945 case _NET_WM_MOVERESIZE_MOVE:
946 shell_interface->move(window->shsurf, seat);
947 break;
948 case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
949 case _NET_WM_MOVERESIZE_SIZE_TOP:
950 case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
951 case _NET_WM_MOVERESIZE_SIZE_RIGHT:
952 case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
953 case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
954 case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
955 case _NET_WM_MOVERESIZE_SIZE_LEFT:
956 shell_interface->resize(window->shsurf, seat, map[detail]);
957 break;
958 case _NET_WM_MOVERESIZE_CANCEL:
959 break;
960 }
961}
962
963static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400964weston_wm_handle_client_message(struct weston_wm *wm,
965 xcb_generic_event_t *event)
966{
967 xcb_client_message_event_t *client_message =
968 (xcb_client_message_event_t *) event;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400969 struct weston_wm_window *window;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400970
971 window = hash_table_lookup(wm->window_hash, client_message->window);
972
Martin Minarik6d118362012-06-07 18:01:59 +0200973 weston_log("XCB_CLIENT_MESSAGE (%s %d %d %d %d %d)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400974 get_atom_name(wm->conn, client_message->type),
975 client_message->data.data32[0],
976 client_message->data.data32[1],
977 client_message->data.data32[2],
978 client_message->data.data32[3],
979 client_message->data.data32[4]);
980
Kristian Høgsberge68fd102012-05-22 17:09:40 -0400981 if (client_message->type == wm->atom.net_wm_moveresize)
982 weston_wm_window_handle_moveresize(window, client_message);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400983}
984
Tiago Vignatti236b48d2012-07-16 12:09:19 -0400985enum cursor_type {
986 XWM_CURSOR_TOP,
987 XWM_CURSOR_BOTTOM,
988 XWM_CURSOR_LEFT,
989 XWM_CURSOR_RIGHT,
990 XWM_CURSOR_TOP_LEFT,
991 XWM_CURSOR_TOP_RIGHT,
992 XWM_CURSOR_BOTTOM_LEFT,
993 XWM_CURSOR_BOTTOM_RIGHT,
994 XWM_CURSOR_LEFT_PTR,
995};
996
997static const char *cursors[] = {
998 "top_side",
999 "bottom_side",
1000 "left_side",
1001 "right_side",
1002 "top_left_corner",
1003 "top_right_corner",
1004 "bottom_left_corner",
1005 "bottom_right_corner",
1006 "left_ptr"
1007};
1008
1009static void
1010weston_wm_create_cursors(struct weston_wm *wm)
1011{
1012 int i, count = ARRAY_LENGTH(cursors);
1013
1014 wm->cursors = malloc(count * sizeof(xcb_cursor_t));
1015 for (i = 0; i < count; i++) {
1016 wm->cursors[i] =
1017 xcb_cursor_library_load_cursor(wm, cursors[i]);
1018 }
1019
1020 wm->last_cursor = -1;
1021}
1022
1023static void
1024weston_wm_destroy_cursors(struct weston_wm *wm)
1025{
1026 uint8_t i;
1027
1028 for (i = 0; i < ARRAY_LENGTH(cursors); i++)
1029 xcb_free_cursor(wm->conn, wm->cursors[i]);
1030
1031 free(wm->cursors);
1032}
1033
1034static int
1035get_cursor_for_location(struct theme *t, int width, int height, int x, int y)
1036{
Scott Moreauc6a7e4b2012-09-28 02:45:06 -06001037 int location = theme_get_location(t, x, y, width, height, 0);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001038
1039 switch (location) {
1040 case THEME_LOCATION_RESIZING_TOP:
1041 return XWM_CURSOR_TOP;
1042 case THEME_LOCATION_RESIZING_BOTTOM:
1043 return XWM_CURSOR_BOTTOM;
1044 case THEME_LOCATION_RESIZING_LEFT:
1045 return XWM_CURSOR_LEFT;
1046 case THEME_LOCATION_RESIZING_RIGHT:
1047 return XWM_CURSOR_RIGHT;
1048 case THEME_LOCATION_RESIZING_TOP_LEFT:
1049 return XWM_CURSOR_TOP_LEFT;
1050 case THEME_LOCATION_RESIZING_TOP_RIGHT:
1051 return XWM_CURSOR_TOP_RIGHT;
1052 case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
1053 return XWM_CURSOR_BOTTOM_LEFT;
1054 case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
1055 return XWM_CURSOR_BOTTOM_RIGHT;
1056 case THEME_LOCATION_EXTERIOR:
1057 case THEME_LOCATION_TITLEBAR:
1058 default:
1059 return XWM_CURSOR_LEFT_PTR;
1060 }
1061}
1062
1063static void
Tiago Vignattic1903232012-07-16 12:15:37 -04001064weston_wm_window_set_cursor(struct weston_wm *wm, xcb_window_t window_id,
1065 int cursor)
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001066{
1067 uint32_t cursor_value_list;
1068
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001069 if (wm->last_cursor == cursor)
1070 return;
1071
1072 wm->last_cursor = cursor;
1073
1074 cursor_value_list = wm->cursors[cursor];
Tiago Vignattic1903232012-07-16 12:15:37 -04001075 xcb_change_window_attributes (wm->conn, window_id,
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001076 XCB_CW_CURSOR, &cursor_value_list);
1077 xcb_flush(wm->conn);
1078}
1079
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001080static void
1081weston_wm_handle_button(struct weston_wm *wm, xcb_generic_event_t *event)
1082{
1083 xcb_button_press_event_t *button = (xcb_button_press_event_t *) event;
1084 struct weston_shell_interface *shell_interface =
1085 &wm->server->compositor->shell_interface;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -04001086 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001087 struct weston_wm_window *window;
Kristian Høgsbergf96e6c02012-05-22 16:38:53 -04001088 enum theme_location location;
1089 struct theme *t = wm->theme;
Kristian Høgsbergc1693f22012-05-22 16:56:23 -04001090 int width, height;
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001091
Martin Minarik6d118362012-06-07 18:01:59 +02001092 weston_log("XCB_BUTTON_%s (detail %d)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001093 button->response_type == XCB_BUTTON_PRESS ?
1094 "PRESS" : "RELEASE", button->detail);
1095
1096 window = hash_table_lookup(wm->window_hash, button->event);
Kristian Høgsbergc1693f22012-05-22 16:56:23 -04001097 weston_wm_window_get_frame_size(window, &width, &height);
1098
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001099 if (button->response_type == XCB_BUTTON_PRESS &&
Kristian Høgsbergf96e6c02012-05-22 16:38:53 -04001100 button->detail == 1) {
1101 location = theme_get_location(t,
1102 button->event_x,
1103 button->event_y,
Scott Moreauc6a7e4b2012-09-28 02:45:06 -06001104 width, height, 0);
Kristian Høgsbergf96e6c02012-05-22 16:38:53 -04001105
1106 switch (location) {
1107 case THEME_LOCATION_TITLEBAR:
Kristian Høgsberg5ba31892012-08-10 10:06:59 -04001108 shell_interface->move(window->shsurf, seat);
Kristian Høgsbergf96e6c02012-05-22 16:38:53 -04001109 break;
Kristian Høgsbergc1693f22012-05-22 16:56:23 -04001110 case THEME_LOCATION_RESIZING_TOP:
1111 case THEME_LOCATION_RESIZING_BOTTOM:
1112 case THEME_LOCATION_RESIZING_LEFT:
1113 case THEME_LOCATION_RESIZING_RIGHT:
1114 case THEME_LOCATION_RESIZING_TOP_LEFT:
1115 case THEME_LOCATION_RESIZING_TOP_RIGHT:
1116 case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
1117 case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
1118 shell_interface->resize(window->shsurf,
Kristian Høgsberg5ba31892012-08-10 10:06:59 -04001119 seat, location);
Kristian Høgsbergc1693f22012-05-22 16:56:23 -04001120 break;
Kristian Høgsbergf96e6c02012-05-22 16:38:53 -04001121 default:
1122 break;
1123 }
1124 }
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001125}
1126
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001127static void
1128weston_wm_handle_motion(struct weston_wm *wm, xcb_generic_event_t *event)
1129{
1130 xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *) event;
1131 struct weston_wm_window *window;
1132 int cursor, width, height;
1133
1134 window = hash_table_lookup(wm->window_hash, motion->event);
Tiago Vignatti9134b772012-07-20 19:41:12 +03001135 if (!window || !window->decorate)
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001136 return;
1137
1138 weston_wm_window_get_frame_size(window, &width, &height);
1139 cursor = get_cursor_for_location(wm->theme, width, height,
1140 motion->event_x, motion->event_y);
1141
Tiago Vignattic1903232012-07-16 12:15:37 -04001142 weston_wm_window_set_cursor(wm, window->frame_id, cursor);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001143}
1144
1145static void
1146weston_wm_handle_enter(struct weston_wm *wm, xcb_generic_event_t *event)
1147{
1148 xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *) event;
1149 struct weston_wm_window *window;
1150 int cursor, width, height;
1151
1152 window = hash_table_lookup(wm->window_hash, enter->event);
Tiago Vignatti9134b772012-07-20 19:41:12 +03001153 if (!window || !window->decorate)
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001154 return;
1155
1156 weston_wm_window_get_frame_size(window, &width, &height);
1157 cursor = get_cursor_for_location(wm->theme, width, height,
1158 enter->event_x, enter->event_y);
1159
Tiago Vignattic1903232012-07-16 12:15:37 -04001160 weston_wm_window_set_cursor(wm, window->frame_id, cursor);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001161}
1162
1163static void
1164weston_wm_handle_leave(struct weston_wm *wm, xcb_generic_event_t *event)
1165{
1166 xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *) event;
1167 struct weston_wm_window *window;
1168
1169 window = hash_table_lookup(wm->window_hash, leave->event);
Tiago Vignatti9134b772012-07-20 19:41:12 +03001170 if (!window || !window->decorate)
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001171 return;
1172
Tiago Vignattic1903232012-07-16 12:15:37 -04001173 weston_wm_window_set_cursor(wm, window->frame_id, XWM_CURSOR_LEFT_PTR);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001174}
1175
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001176static int
1177weston_wm_handle_event(int fd, uint32_t mask, void *data)
1178{
1179 struct weston_wm *wm = data;
1180 xcb_generic_event_t *event;
1181 int count = 0;
1182
1183 while (event = xcb_poll_for_event(wm->conn), event != NULL) {
1184 if (weston_wm_handle_selection_event(wm, event)) {
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001185 free(event);
1186 count++;
1187 continue;
1188 }
1189
Kristian Høgsbergf197e9f2012-05-30 11:46:29 -04001190 switch (event->response_type & ~0x80) {
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001191 case XCB_BUTTON_PRESS:
1192 case XCB_BUTTON_RELEASE:
1193 weston_wm_handle_button(wm, event);
1194 break;
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001195 case XCB_ENTER_NOTIFY:
1196 weston_wm_handle_enter(wm, event);
1197 break;
1198 case XCB_LEAVE_NOTIFY:
1199 weston_wm_handle_leave(wm, event);
1200 break;
1201 case XCB_MOTION_NOTIFY:
1202 weston_wm_handle_motion(wm, event);
1203 break;
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001204 case XCB_CREATE_NOTIFY:
1205 weston_wm_handle_create_notify(wm, event);
1206 break;
1207 case XCB_MAP_REQUEST:
1208 weston_wm_handle_map_request(wm, event);
1209 break;
1210 case XCB_MAP_NOTIFY:
1211 weston_wm_handle_map_notify(wm, event);
1212 break;
1213 case XCB_UNMAP_NOTIFY:
Kristian Høgsberg194ea542012-05-30 10:05:41 -04001214 weston_wm_handle_unmap_notify(wm, event);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001215 break;
Kristian Høgsberg029539b2012-05-30 11:28:24 -04001216 case XCB_REPARENT_NOTIFY:
1217 weston_wm_handle_reparent_notify(wm, event);
1218 break;
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001219 case XCB_CONFIGURE_REQUEST:
1220 weston_wm_handle_configure_request(wm, event);
1221 break;
1222 case XCB_CONFIGURE_NOTIFY:
1223 weston_wm_handle_configure_notify(wm, event);
1224 break;
1225 case XCB_DESTROY_NOTIFY:
1226 weston_wm_handle_destroy_notify(wm, event);
1227 break;
1228 case XCB_MAPPING_NOTIFY:
Martin Minarik6d118362012-06-07 18:01:59 +02001229 weston_log("XCB_MAPPING_NOTIFY\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001230 break;
1231 case XCB_PROPERTY_NOTIFY:
1232 weston_wm_handle_property_notify(wm, event);
1233 break;
1234 case XCB_CLIENT_MESSAGE:
1235 weston_wm_handle_client_message(wm, event);
1236 break;
1237 }
1238
1239 free(event);
1240 count++;
1241 }
1242
1243 xcb_flush(wm->conn);
1244
1245 return count;
1246}
1247
1248static void
1249wxs_wm_get_resources(struct weston_wm *wm)
1250{
1251
1252#define F(field) offsetof(struct weston_wm, field)
1253
1254 static const struct { const char *name; int offset; } atoms[] = {
1255 { "WM_PROTOCOLS", F(atom.wm_protocols) },
1256 { "WM_TAKE_FOCUS", F(atom.wm_take_focus) },
1257 { "WM_DELETE_WINDOW", F(atom.wm_delete_window) },
Kristian Høgsberga6d9a5e2012-05-30 12:15:44 -04001258 { "WM_STATE", F(atom.wm_state) },
Kristian Høgsberg670b5d32012-06-04 11:00:40 -04001259 { "WM_S0", F(atom.wm_s0) },
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +03001260 { "WM_CLIENT_MACHINE", F(atom.wm_client_machine) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001261 { "_NET_WM_NAME", F(atom.net_wm_name) },
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +03001262 { "_NET_WM_PID", F(atom.net_wm_pid) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001263 { "_NET_WM_ICON", F(atom.net_wm_icon) },
1264 { "_NET_WM_STATE", F(atom.net_wm_state) },
1265 { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
1266 { "_NET_WM_USER_TIME", F(atom.net_wm_user_time) },
1267 { "_NET_WM_ICON_NAME", F(atom.net_wm_icon_name) },
1268 { "_NET_WM_WINDOW_TYPE", F(atom.net_wm_window_type) },
1269
1270 { "_NET_WM_WINDOW_TYPE_DESKTOP", F(atom.net_wm_window_type_desktop) },
1271 { "_NET_WM_WINDOW_TYPE_DOCK", F(atom.net_wm_window_type_dock) },
1272 { "_NET_WM_WINDOW_TYPE_TOOLBAR", F(atom.net_wm_window_type_toolbar) },
1273 { "_NET_WM_WINDOW_TYPE_MENU", F(atom.net_wm_window_type_menu) },
1274 { "_NET_WM_WINDOW_TYPE_UTILITY", F(atom.net_wm_window_type_utility) },
1275 { "_NET_WM_WINDOW_TYPE_SPLASH", F(atom.net_wm_window_type_splash) },
1276 { "_NET_WM_WINDOW_TYPE_DIALOG", F(atom.net_wm_window_type_dialog) },
Tiago Vignattibf1e8662012-06-12 14:07:49 +03001277 { "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", F(atom.net_wm_window_type_dropdown) },
1278 { "_NET_WM_WINDOW_TYPE_POPUP_MENU", F(atom.net_wm_window_type_popup) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001279 { "_NET_WM_WINDOW_TYPE_TOOLTIP", F(atom.net_wm_window_type_tooltip) },
1280 { "_NET_WM_WINDOW_TYPE_NOTIFICATION", F(atom.net_wm_window_type_notification) },
1281 { "_NET_WM_WINDOW_TYPE_COMBO", F(atom.net_wm_window_type_combo) },
1282 { "_NET_WM_WINDOW_TYPE_DND", F(atom.net_wm_window_type_dnd) },
1283 { "_NET_WM_WINDOW_TYPE_NORMAL", F(atom.net_wm_window_type_normal) },
1284
1285 { "_NET_WM_MOVERESIZE", F(atom.net_wm_moveresize) },
1286 { "_NET_SUPPORTING_WM_CHECK",
1287 F(atom.net_supporting_wm_check) },
1288 { "_NET_SUPPORTED", F(atom.net_supported) },
1289 { "_MOTIF_WM_HINTS", F(atom.motif_wm_hints) },
1290 { "CLIPBOARD", F(atom.clipboard) },
Kristian Høgsbergcba022a2012-06-04 10:11:45 -04001291 { "CLIPBOARD_MANAGER", F(atom.clipboard_manager) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001292 { "TARGETS", F(atom.targets) },
1293 { "UTF8_STRING", F(atom.utf8_string) },
1294 { "_WL_SELECTION", F(atom.wl_selection) },
1295 { "INCR", F(atom.incr) },
1296 { "TIMESTAMP", F(atom.timestamp) },
1297 { "MULTIPLE", F(atom.multiple) },
1298 { "UTF8_STRING" , F(atom.utf8_string) },
1299 { "COMPOUND_TEXT", F(atom.compound_text) },
1300 { "TEXT", F(atom.text) },
1301 { "STRING", F(atom.string) },
1302 { "text/plain;charset=utf-8", F(atom.text_plain_utf8) },
1303 { "text/plain", F(atom.text_plain) },
1304 };
1305#undef F
1306
1307 xcb_xfixes_query_version_cookie_t xfixes_cookie;
1308 xcb_xfixes_query_version_reply_t *xfixes_reply;
1309 xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
1310 xcb_intern_atom_reply_t *reply;
1311 xcb_render_query_pict_formats_reply_t *formats_reply;
1312 xcb_render_query_pict_formats_cookie_t formats_cookie;
1313 xcb_render_pictforminfo_t *formats;
1314 uint32_t i;
1315
1316 xcb_prefetch_extension_data (wm->conn, &xcb_xfixes_id);
1317
1318 formats_cookie = xcb_render_query_pict_formats(wm->conn);
1319
1320 for (i = 0; i < ARRAY_LENGTH(atoms); i++)
1321 cookies[i] = xcb_intern_atom (wm->conn, 0,
1322 strlen(atoms[i].name),
1323 atoms[i].name);
1324
1325 for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
1326 reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL);
1327 *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom;
1328 free(reply);
1329 }
1330
1331 wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id);
1332 if (!wm->xfixes || !wm->xfixes->present)
Martin Minarik6d118362012-06-07 18:01:59 +02001333 weston_log("xfixes not available\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001334
1335 xfixes_cookie = xcb_xfixes_query_version(wm->conn,
1336 XCB_XFIXES_MAJOR_VERSION,
1337 XCB_XFIXES_MINOR_VERSION);
1338 xfixes_reply = xcb_xfixes_query_version_reply(wm->conn,
1339 xfixes_cookie, NULL);
1340
Martin Minarik6d118362012-06-07 18:01:59 +02001341 weston_log("xfixes version: %d.%d\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001342 xfixes_reply->major_version, xfixes_reply->minor_version);
1343
1344 free(xfixes_reply);
1345
1346 formats_reply = xcb_render_query_pict_formats_reply(wm->conn,
1347 formats_cookie, 0);
1348 if (formats_reply == NULL)
1349 return;
1350
1351 formats = xcb_render_query_pict_formats_formats(formats_reply);
Kristian Høgsberge89cef32012-07-16 11:57:08 -04001352 for (i = 0; i < formats_reply->num_formats; i++) {
1353 if (formats[i].direct.red_mask != 0xff &&
1354 formats[i].direct.red_shift != 16)
1355 continue;
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001356 if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT &&
1357 formats[i].depth == 24)
Kristian Høgsberge89cef32012-07-16 11:57:08 -04001358 wm->format_rgb = formats[i];
1359 if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT &&
1360 formats[i].depth == 32 &&
1361 formats[i].direct.alpha_mask == 0xff &&
1362 formats[i].direct.alpha_shift == 24)
1363 wm->format_rgba = formats[i];
1364 }
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001365
1366 free(formats_reply);
1367}
1368
1369static void
1370weston_wm_create_wm_window(struct weston_wm *wm)
1371{
1372 static const char name[] = "Weston WM";
1373
1374 wm->wm_window = xcb_generate_id(wm->conn);
1375 xcb_create_window(wm->conn,
1376 XCB_COPY_FROM_PARENT,
1377 wm->wm_window,
1378 wm->screen->root,
1379 0, 0,
1380 10, 10,
1381 0,
1382 XCB_WINDOW_CLASS_INPUT_OUTPUT,
1383 wm->screen->root_visual,
1384 0, NULL);
1385
1386 xcb_change_property(wm->conn,
1387 XCB_PROP_MODE_REPLACE,
1388 wm->wm_window,
1389 wm->atom.net_supporting_wm_check,
1390 XCB_ATOM_WINDOW,
1391 32, /* format */
1392 1, &wm->wm_window);
1393
1394 xcb_change_property(wm->conn,
1395 XCB_PROP_MODE_REPLACE,
1396 wm->wm_window,
1397 wm->atom.net_wm_name,
1398 wm->atom.utf8_string,
1399 8, /* format */
1400 strlen(name), name);
1401
1402 xcb_change_property(wm->conn,
1403 XCB_PROP_MODE_REPLACE,
1404 wm->screen->root,
1405 wm->atom.net_supporting_wm_check,
1406 XCB_ATOM_WINDOW,
1407 32, /* format */
1408 1, &wm->wm_window);
1409
Kristian Høgsberg670b5d32012-06-04 11:00:40 -04001410 /* Claim the WM_S0 selection even though we don't suport
1411 * the --replace functionality. */
1412 xcb_set_selection_owner(wm->conn,
1413 wm->wm_window,
1414 wm->atom.wm_s0,
1415 XCB_TIME_CURRENT_TIME);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001416}
1417
1418struct weston_wm *
1419weston_wm_create(struct weston_xserver *wxs)
1420{
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001421 struct weston_wm *wm;
1422 struct wl_event_loop *loop;
1423 xcb_screen_iterator_t s;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -04001424 uint32_t values[1];
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001425 int sv[2];
1426 xcb_atom_t supported[1];
1427
1428 wm = malloc(sizeof *wm);
1429 if (wm == NULL)
1430 return NULL;
1431
1432 memset(wm, 0, sizeof *wm);
1433 wm->server = wxs;
1434 wm->window_hash = hash_table_create();
1435 if (wm->window_hash == NULL) {
1436 free(wm);
1437 return NULL;
1438 }
1439
1440 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02001441 weston_log("socketpair failed\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001442 hash_table_destroy(wm->window_hash);
1443 free(wm);
1444 return NULL;
1445 }
1446
1447 xserver_send_client(wxs->resource, sv[1]);
1448 wl_client_flush(wxs->resource->client);
1449 close(sv[1]);
1450
1451 /* xcb_connect_to_fd takes ownership of the fd. */
1452 wm->conn = xcb_connect_to_fd(sv[0], NULL);
1453 if (xcb_connection_has_error(wm->conn)) {
Martin Minarik6d118362012-06-07 18:01:59 +02001454 weston_log("xcb_connect_to_fd failed\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001455 close(sv[0]);
1456 hash_table_destroy(wm->window_hash);
1457 free(wm);
1458 return NULL;
1459 }
1460
1461 s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn));
1462 wm->screen = s.data;
1463
1464 loop = wl_display_get_event_loop(wxs->wl_display);
1465 wm->source =
1466 wl_event_loop_add_fd(loop, sv[0],
1467 WL_EVENT_READABLE,
1468 weston_wm_handle_event, wm);
1469 wl_event_source_check(wm->source);
1470
1471 wxs_wm_get_resources(wm);
1472
1473 values[0] =
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001474 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
1475 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
1476 XCB_EVENT_MASK_PROPERTY_CHANGE;
1477 xcb_change_window_attributes(wm->conn, wm->screen->root,
1478 XCB_CW_EVENT_MASK, values);
1479 wm->theme = theme_create();
1480
1481 weston_wm_create_wm_window(wm);
1482
1483 supported[0] = wm->atom.net_wm_moveresize;
1484 xcb_change_property(wm->conn,
1485 XCB_PROP_MODE_REPLACE,
1486 wm->screen->root,
1487 wm->atom.net_supported,
1488 XCB_ATOM_ATOM,
1489 32, /* format */
1490 ARRAY_LENGTH(supported), supported);
1491
Kristian Høgsberg4dec0112012-06-03 09:18:06 -04001492 weston_wm_selection_init(wm);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001493
1494 xcb_flush(wm->conn);
1495
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001496 wm->activate_listener.notify = weston_wm_window_activate;
1497 wl_signal_add(&wxs->compositor->activate_signal,
1498 &wm->activate_listener);
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +03001499 wm->kill_listener.notify = weston_wm_kill_client;
1500 wl_signal_add(&wxs->compositor->kill_signal,
1501 &wm->kill_listener);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001502
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001503 weston_wm_create_cursors(wm);
Tiago Vignattic1903232012-07-16 12:15:37 -04001504 weston_wm_window_set_cursor(wm, wm->screen->root, XWM_CURSOR_LEFT_PTR);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001505
Martin Minarik6d118362012-06-07 18:01:59 +02001506 weston_log("created wm\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001507
1508 return wm;
1509}
1510
1511void
1512weston_wm_destroy(struct weston_wm *wm)
1513{
1514 /* FIXME: Free windows in hash. */
1515 hash_table_destroy(wm->window_hash);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001516 weston_wm_destroy_cursors(wm);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001517 xcb_disconnect(wm->conn);
1518 wl_event_source_remove(wm->source);
1519 wl_list_remove(&wm->selection_listener.link);
1520 wl_list_remove(&wm->activate_listener.link);
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +03001521 wl_list_remove(&wm->kill_listener.link);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001522
1523 free(wm);
1524}
1525
1526static void
1527surface_destroy(struct wl_listener *listener, void *data)
1528{
1529 struct weston_wm_window *window =
1530 container_of(listener,
1531 struct weston_wm_window, surface_destroy_listener);
1532
Martin Minarik6d118362012-06-07 18:01:59 +02001533 weston_log("surface for xid %d destroyed\n", window->id);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001534}
1535
1536static struct weston_wm_window *
1537get_wm_window(struct weston_surface *surface)
1538{
1539 struct wl_resource *resource = &surface->surface.resource;
1540 struct wl_listener *listener;
1541
1542 listener = wl_signal_get(&resource->destroy_signal, surface_destroy);
1543 if (listener)
1544 return container_of(listener, struct weston_wm_window,
1545 surface_destroy_listener);
1546
1547 return NULL;
1548}
1549
1550static void
Kristian Høgsberga61ca062012-05-22 16:05:52 -04001551weston_wm_window_configure(void *data)
1552{
1553 struct weston_wm_window *window = data;
1554 struct weston_wm *wm = window->wm;
1555 uint32_t values[2];
1556 int width, height;
1557
1558 values[0] = window->width;
1559 values[1] = window->height;
1560 xcb_configure_window(wm->conn,
1561 window->id,
1562 XCB_CONFIG_WINDOW_WIDTH |
1563 XCB_CONFIG_WINDOW_HEIGHT,
1564 values);
1565
1566 weston_wm_window_get_frame_size(window, &width, &height);
1567 values[0] = width;
1568 values[1] = height;
1569 xcb_configure_window(wm->conn,
1570 window->frame_id,
1571 XCB_CONFIG_WINDOW_WIDTH |
1572 XCB_CONFIG_WINDOW_HEIGHT,
1573 values);
1574
1575 window->configure_source = NULL;
1576
1577 weston_wm_window_schedule_repaint(window);
1578}
1579
1580static void
1581send_configure(struct weston_surface *surface,
1582 uint32_t edges, int32_t width, int32_t height)
1583{
1584 struct weston_wm_window *window = get_wm_window(surface);
1585 struct weston_wm *wm = window->wm;
1586 struct theme *t = window->wm->theme;
1587
1588 if (window->decorate) {
1589 window->width = width - 2 * (t->margin + t->width);
1590 window->height = height - 2 * t->margin -
1591 t->titlebar_height - t->width;
1592 } else {
1593 window->width = width - 2 * t->margin;
1594 window->height = height - 2 * t->margin;
1595 }
1596
1597 if (window->configure_source)
1598 return;
1599
1600 window->configure_source =
1601 wl_event_loop_add_idle(wm->server->loop,
1602 weston_wm_window_configure, window);
1603}
1604
1605static const struct weston_shell_client shell_client = {
1606 send_configure
1607};
1608
1609static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001610xserver_map_shell_surface(struct weston_wm *wm,
1611 struct weston_wm_window *window)
1612{
1613 struct weston_shell_interface *shell_interface =
1614 &wm->server->compositor->shell_interface;
1615 struct weston_wm_window *parent;
1616 struct theme *t = window->wm->theme;
Tiago Vignattice1baa82012-07-20 23:09:55 +03001617 int parent_id, x = 0, y = 0;
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001618
1619 if (!shell_interface->create_shell_surface)
1620 return;
1621
1622 window->shsurf =
1623 shell_interface->create_shell_surface(shell_interface->shell,
Kristian Høgsberga61ca062012-05-22 16:05:52 -04001624 window->surface,
1625 &shell_client);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001626
Tiago Vignatti771241e2012-06-04 20:01:45 +03001627 /* ICCCM 4.1.1 */
Tiago Vignattice1baa82012-07-20 23:09:55 +03001628 if (!window->override_redirect) {
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001629 shell_interface->set_toplevel(window->shsurf);
1630 return;
1631 }
1632
Tiago Vignattice1baa82012-07-20 23:09:55 +03001633 /* not all non-toplevel has transient_for set. So we need this
1634 * workaround to guess a parent that will determine the relative
1635 * position of the transient surface */
1636 if (!window->transient_for)
1637 parent_id = wm->focus_latest->id;
1638 else
1639 parent_id = window->transient_for->id;
1640
1641 parent = hash_table_lookup(wm->window_hash, parent_id);
Tiago Vignattie66fcee2012-07-20 23:09:54 +03001642
1643 /* non-decorated and non-toplevel windows, e.g. sub-menus */
1644 if (!parent->decorate && parent->override_redirect) {
1645 x = parent->x + t->margin;
1646 y = parent->y + t->margin;
1647 }
1648
Kristian Høgsberg8150b192012-06-27 10:22:58 -04001649 shell_interface->set_transient(window->shsurf, parent->surface,
Tiago Vignattie66fcee2012-07-20 23:09:54 +03001650 window->x + t->margin - x,
1651 window->y + t->margin - y,
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001652 WL_SHELL_SURFACE_TRANSIENT_INACTIVE);
1653}
1654
1655static void
1656xserver_set_window_id(struct wl_client *client, struct wl_resource *resource,
1657 struct wl_resource *surface_resource, uint32_t id)
1658{
1659 struct weston_xserver *wxs = resource->data;
1660 struct weston_wm *wm = wxs->wm;
1661 struct wl_surface *surface = surface_resource->data;
1662 struct weston_wm_window *window;
1663
1664 if (client != wxs->client)
1665 return;
1666
1667 window = hash_table_lookup(wm->window_hash, id);
1668 if (window == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02001669 weston_log("set_window_id for unknown window %d\n", id);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001670 return;
1671 }
1672
Martin Minarik6d118362012-06-07 18:01:59 +02001673 weston_log("set_window_id %d for surface %p\n", id, surface);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001674
1675 weston_wm_window_read_properties(window);
1676
1677 window->surface = (struct weston_surface *) surface;
1678 window->surface_destroy_listener.notify = surface_destroy;
1679 wl_signal_add(&surface->resource.destroy_signal,
1680 &window->surface_destroy_listener);
1681
1682 weston_wm_window_schedule_repaint(window);
1683 xserver_map_shell_surface(wm, window);
1684}
1685
1686const struct xserver_interface xserver_implementation = {
1687 xserver_set_window_id
1688};