blob: 303ef159bbae03c5ad77b7ef31816c6c3ad36fab [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);
Tiago Vignattiac78bb12012-09-28 16:29:46 +0300209 if (!images)
210 return -1;
211
Tiago Vignatti90fada42012-07-16 12:02:08 -0400212 cursor = xcb_cursor_images_load_cursor (wm, images);
213 XcursorImagesDestroy (images);
214
215 return cursor;
216}
217
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400218void
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400219dump_property(struct weston_wm *wm,
220 xcb_atom_t property, xcb_get_property_reply_t *reply)
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400221{
222 int32_t *incr_value;
223 const char *text_value, *name;
224 xcb_atom_t *atom_value;
225 int width, len;
226 uint32_t i;
227
Martin Minarik6d118362012-06-07 18:01:59 +0200228 width = weston_log_continue("%s: ", get_atom_name(wm->conn, property));
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400229 if (reply == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200230 weston_log_continue("(no reply)\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400231 return;
232 }
233
Martin Minarik6d118362012-06-07 18:01:59 +0200234 width += weston_log_continue(
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400235 "%s/%d, length %d (value_len %d): ",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400236 get_atom_name(wm->conn, reply->type),
237 reply->format,
238 xcb_get_property_value_length(reply),
239 reply->value_len);
240
241 if (reply->type == wm->atom.incr) {
242 incr_value = xcb_get_property_value(reply);
Martin Minarik6d118362012-06-07 18:01:59 +0200243 weston_log_continue("%d\n", *incr_value);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400244 } else if (reply->type == wm->atom.utf8_string ||
245 reply->type == wm->atom.string) {
246 text_value = xcb_get_property_value(reply);
247 if (reply->value_len > 40)
248 len = 40;
249 else
250 len = reply->value_len;
Martin Minarik6d118362012-06-07 18:01:59 +0200251 weston_log_continue("\"%.*s\"\n", len, text_value);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400252 } else if (reply->type == XCB_ATOM_ATOM) {
253 atom_value = xcb_get_property_value(reply);
254 for (i = 0; i < reply->value_len; i++) {
255 name = get_atom_name(wm->conn, atom_value[i]);
256 if (width + strlen(name) + 2 > 78) {
Martin Minarik6d118362012-06-07 18:01:59 +0200257 weston_log_continue("\n ");
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400258 width = 4;
259 } else if (i > 0) {
Martin Minarik6d118362012-06-07 18:01:59 +0200260 width += weston_log_continue(", ");
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400261 }
262
Martin Minarik6d118362012-06-07 18:01:59 +0200263 width += weston_log_continue("%s", name);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400264 }
Martin Minarik6d118362012-06-07 18:01:59 +0200265 weston_log_continue("\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400266 } else {
Martin Minarik6d118362012-06-07 18:01:59 +0200267 weston_log_continue("huh?\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400268 }
269}
270
Tiago Vignatti2d129f12012-11-30 17:19:59 -0200271static void
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400272read_and_dump_property(struct weston_wm *wm,
273 xcb_window_t window, xcb_atom_t property)
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400274{
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400275 xcb_get_property_reply_t *reply;
276 xcb_get_property_cookie_t cookie;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400277
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400278 cookie = xcb_get_property(wm->conn, 0, window,
279 property, XCB_ATOM_ANY, 0, 2048);
280 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400281
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400282 dump_property(wm, property, reply);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400283
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400284 free(reply);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400285}
286
287/* We reuse some predefined, but otherwise useles atoms */
288#define TYPE_WM_PROTOCOLS XCB_ATOM_CUT_BUFFER0
289#define TYPE_MOTIF_WM_HINTS XCB_ATOM_CUT_BUFFER1
290
291static void
292weston_wm_window_read_properties(struct weston_wm_window *window)
293{
294 struct weston_wm *wm = window->wm;
295
296#define F(field) offsetof(struct weston_wm_window, field)
297 const struct {
298 xcb_atom_t atom;
299 xcb_atom_t type;
300 int offset;
301 } props[] = {
302 { XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, F(class) },
303 { XCB_ATOM_WM_NAME, XCB_ATOM_STRING, F(name) },
304 { XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, F(transient_for) },
305 { wm->atom.wm_protocols, TYPE_WM_PROTOCOLS, F(protocols) },
306 { wm->atom.net_wm_window_type, XCB_ATOM_ATOM, F(type) },
307 { wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) },
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300308 { wm->atom.net_wm_pid, XCB_ATOM_CARDINAL, F(pid) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400309 { wm->atom.motif_wm_hints, TYPE_MOTIF_WM_HINTS, 0 },
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300310 { wm->atom.wm_client_machine, XCB_ATOM_WM_CLIENT_MACHINE, F(machine) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400311 };
312#undef F
313
314 xcb_get_property_cookie_t cookie[ARRAY_LENGTH(props)];
315 xcb_get_property_reply_t *reply;
316 void *p;
317 uint32_t *xid;
318 xcb_atom_t *atom;
319 uint32_t i;
320 struct motif_wm_hints *hints;
321
322 if (!window->properties_dirty)
323 return;
324 window->properties_dirty = 0;
325
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400326 for (i = 0; i < ARRAY_LENGTH(props); i++)
327 cookie[i] = xcb_get_property(wm->conn,
328 0, /* delete */
329 window->id,
330 props[i].atom,
331 XCB_ATOM_ANY, 0, 2048);
332
Tiago Vignatti2ea74d92012-07-20 23:09:53 +0300333 window->decorate = !window->override_redirect;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400334 for (i = 0; i < ARRAY_LENGTH(props); i++) {
335 reply = xcb_get_property_reply(wm->conn, cookie[i], NULL);
336 if (!reply)
337 /* Bad window, typically */
338 continue;
339 if (reply->type == XCB_ATOM_NONE) {
340 /* No such property */
341 free(reply);
342 continue;
343 }
344
345 p = ((char *) window + props[i].offset);
346
347 switch (props[i].type) {
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300348 case XCB_ATOM_WM_CLIENT_MACHINE:
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400349 case XCB_ATOM_STRING:
350 /* FIXME: We're using this for both string and
351 utf8_string */
352 if (*(char **) p)
353 free(*(char **) p);
354
355 *(char **) p =
356 strndup(xcb_get_property_value(reply),
357 xcb_get_property_value_length(reply));
358 break;
359 case XCB_ATOM_WINDOW:
360 xid = xcb_get_property_value(reply);
361 *(struct weston_wm_window **) p =
362 hash_table_lookup(wm->window_hash, *xid);
363 break;
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300364 case XCB_ATOM_CARDINAL:
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400365 case XCB_ATOM_ATOM:
366 atom = xcb_get_property_value(reply);
367 *(xcb_atom_t *) p = *atom;
368 break;
369 case TYPE_WM_PROTOCOLS:
370 break;
371 case TYPE_MOTIF_WM_HINTS:
372 hints = xcb_get_property_value(reply);
373 if (hints->flags & MWM_HINTS_DECORATIONS)
374 window->decorate = hints->decorations > 0;
375 break;
376 default:
377 break;
378 }
379 free(reply);
380 }
381}
382
383static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400384weston_wm_window_get_frame_size(struct weston_wm_window *window,
385 int *width, int *height)
386{
387 struct theme *t = window->wm->theme;
388
389 if (window->decorate) {
390 *width = window->width + (t->margin + t->width) * 2;
391 *height = window->height +
392 t->margin * 2 + t->width + t->titlebar_height;
393 } else {
394 *width = window->width + t->margin * 2;
395 *height = window->height + t->margin * 2;
396 }
397}
398
399static void
400weston_wm_window_get_child_position(struct weston_wm_window *window,
401 int *x, int *y)
402{
403 struct theme *t = window->wm->theme;
404
405 if (window->decorate) {
406 *x = t->margin + t->width;
407 *y = t->margin + t->titlebar_height;
408 } else {
409 *x = t->margin;
410 *y = t->margin;
411 }
412}
Kristian Høgsbergeaee7842012-05-22 10:04:20 -0400413
414static void
415weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event)
416{
417 xcb_configure_request_event_t *configure_request =
418 (xcb_configure_request_event_t *) event;
419 struct weston_wm_window *window;
420 uint32_t mask, values[16];
421 int x, y, width, height, i = 0;
422
Martin Minarik6d118362012-06-07 18:01:59 +0200423 weston_log("XCB_CONFIGURE_REQUEST (window %d) %d,%d @ %dx%d\n",
Kristian Høgsbergeaee7842012-05-22 10:04:20 -0400424 configure_request->window,
425 configure_request->x, configure_request->y,
426 configure_request->width, configure_request->height);
427
428 window = hash_table_lookup(wm->window_hash, configure_request->window);
429
430 if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
431 window->width = configure_request->width;
432 if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
433 window->height = configure_request->height;
434
435 weston_wm_window_get_child_position(window, &x, &y);
436 values[i++] = x;
437 values[i++] = y;
438 values[i++] = window->width;
439 values[i++] = window->height;
440 values[i++] = 0;
441 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
442 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
443 XCB_CONFIG_WINDOW_BORDER_WIDTH;
444 if (configure_request->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
445 values[i++] = configure_request->sibling;
446 mask |= XCB_CONFIG_WINDOW_SIBLING;
447 }
448 if (configure_request->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
449 values[i++] = configure_request->stack_mode;
450 mask |= XCB_CONFIG_WINDOW_STACK_MODE;
451 }
452
453 xcb_configure_window(wm->conn, window->id, mask, values);
454
455 weston_wm_window_get_frame_size(window, &width, &height);
456 values[0] = width;
457 values[1] = height;
458 mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
459 xcb_configure_window(wm->conn, window->frame_id, mask, values);
460
461 weston_wm_window_schedule_repaint(window);
462}
463
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400464static void
465weston_wm_handle_configure_notify(struct weston_wm *wm, xcb_generic_event_t *event)
466{
467 xcb_configure_notify_event_t *configure_notify =
468 (xcb_configure_notify_event_t *) event;
469 struct weston_wm_window *window;
Tiago Vignattie66fcee2012-07-20 23:09:54 +0300470 int x, y;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400471
472 window = hash_table_lookup(wm->window_hash, configure_notify->window);
473
Martin Minarik6d118362012-06-07 18:01:59 +0200474 weston_log("XCB_CONFIGURE_NOTIFY (%s window %d) %d,%d @ %dx%d\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400475 configure_notify->window == window->id ? "client" : "frame",
476 configure_notify->window,
477 configure_notify->x, configure_notify->y,
478 configure_notify->width, configure_notify->height);
Tiago Vignattie66fcee2012-07-20 23:09:54 +0300479
480 /* resize falls here */
481 if (configure_notify->window != window->id)
482 return;
483
484 weston_wm_window_get_child_position(window, &x, &y);
485 window->x = configure_notify->x - x;
486 window->y = configure_notify->y - y;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400487}
488
489static void
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +0300490weston_wm_kill_client(struct wl_listener *listener, void *data)
491{
492 struct weston_surface *surface = data;
493 struct weston_wm_window *window = get_wm_window(surface);
494 char name[1024];
495
496 if (!window)
497 return;
498
499 gethostname(name, 1024);
500
501 /* this is only one heuristic to guess the PID of a client is valid,
502 * assuming it's compliant with icccm and ewmh. Non-compliants and
503 * remote applications of course fail. */
504 if (!strcmp(window->machine, name) && window->pid != 0)
505 kill(window->pid, SIGKILL);
506}
507
508static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400509weston_wm_window_activate(struct wl_listener *listener, void *data)
510{
511 struct weston_surface *surface = data;
512 struct weston_wm_window *window = get_wm_window(surface);
Scott Moreau85ecac02012-05-21 15:49:14 -0600513 struct weston_wm *wm =
514 container_of(listener, struct weston_wm, activate_listener);
515 xcb_client_message_event_t client_message;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400516
Scott Moreau85ecac02012-05-21 15:49:14 -0600517 if (window) {
518 client_message.response_type = XCB_CLIENT_MESSAGE;
519 client_message.format = 32;
520 client_message.window = window->id;
521 client_message.type = wm->atom.wm_protocols;
522 client_message.data.data32[0] = wm->atom.wm_take_focus;
523 client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
524
525 xcb_send_event(wm->conn, 0, window->id,
526 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
527 (char *) &client_message);
528
529 xcb_set_input_focus (wm->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
530 window->id, XCB_TIME_CURRENT_TIME);
531 } else {
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400532 xcb_set_input_focus (wm->conn,
533 XCB_INPUT_FOCUS_POINTER_ROOT,
534 XCB_NONE,
535 XCB_TIME_CURRENT_TIME);
Scott Moreau85ecac02012-05-21 15:49:14 -0600536 }
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400537
538 if (wm->focus_window)
539 weston_wm_window_schedule_repaint(wm->focus_window);
540 wm->focus_window = window;
Tiago Vignattice1baa82012-07-20 23:09:55 +0300541 if (window)
542 wm->focus_latest = window;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400543 if (wm->focus_window)
544 weston_wm_window_schedule_repaint(wm->focus_window);
545}
546
547static int
548our_resource(struct weston_wm *wm, uint32_t id)
549{
550 const xcb_setup_t *setup;
551
552 setup = xcb_get_setup(wm->conn);
553
554 return (id & ~setup->resource_id_mask) == setup->resource_id_base;
555}
556
Kristian Høgsberga6d9a5e2012-05-30 12:15:44 -0400557#define ICCCM_WITHDRAWN_STATE 0
558#define ICCCM_NORMAL_STATE 1
559#define ICCCM_ICONIC_STATE 3
560
561static void
562weston_wm_window_set_state(struct weston_wm_window *window, int32_t state)
563{
564 struct weston_wm *wm = window->wm;
565 uint32_t property[2];
566
567 property[0] = state;
568 property[1] = XCB_WINDOW_NONE;
569
570 xcb_change_property(wm->conn,
571 XCB_PROP_MODE_REPLACE,
572 window->id,
573 wm->atom.wm_state,
574 wm->atom.wm_state,
575 32, /* format */
576 2, property);
577}
578
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400579static void
580weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
581{
582 xcb_map_request_event_t *map_request =
583 (xcb_map_request_event_t *) event;
584 struct weston_wm_window *window;
585 uint32_t values[1];
586 int x, y, width, height;
587
588 if (our_resource(wm, map_request->window)) {
Martin Minarik6d118362012-06-07 18:01:59 +0200589 weston_log("XCB_MAP_REQUEST (window %d, ours)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400590 map_request->window);
591 return;
592 }
593
594 window = hash_table_lookup(wm->window_hash, map_request->window);
595
Kristian Høgsbergbc6e1622012-05-30 09:59:56 -0400596 if (window->frame_id)
597 return;
598
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400599 weston_wm_window_read_properties(window);
600
601 weston_wm_window_get_frame_size(window, &width, &height);
602 weston_wm_window_get_child_position(window, &x, &y);
603
604 values[0] =
605 XCB_EVENT_MASK_KEY_PRESS |
606 XCB_EVENT_MASK_KEY_RELEASE |
607 XCB_EVENT_MASK_BUTTON_PRESS |
608 XCB_EVENT_MASK_BUTTON_RELEASE |
Tiago Vignatti236b48d2012-07-16 12:09:19 -0400609 XCB_EVENT_MASK_POINTER_MOTION |
610 XCB_EVENT_MASK_ENTER_WINDOW |
611 XCB_EVENT_MASK_LEAVE_WINDOW |
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400612 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
Kristian Høgsberg44c20132012-05-30 10:09:21 -0400613 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400614
615 window->frame_id = xcb_generate_id(wm->conn);
616 xcb_create_window(wm->conn,
617 XCB_COPY_FROM_PARENT,
618 window->frame_id,
619 wm->screen->root,
620 0, 0,
621 width, height,
622 0,
623 XCB_WINDOW_CLASS_INPUT_OUTPUT,
624 wm->screen->root_visual,
625 XCB_CW_EVENT_MASK, values);
626 xcb_reparent_window(wm->conn, window->id, window->frame_id, x, y);
627
628 values[0] = 0;
629 xcb_configure_window(wm->conn, window->id,
630 XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
631
Martin Minarik6d118362012-06-07 18:01:59 +0200632 weston_log("XCB_MAP_REQUEST (window %d, %p, frame %d)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400633 window->id, window, window->frame_id);
634
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400635 xcb_map_window(wm->conn, map_request->window);
636 xcb_map_window(wm->conn, window->frame_id);
Kristian Høgsberga6d9a5e2012-05-30 12:15:44 -0400637 weston_wm_window_set_state(window, ICCCM_NORMAL_STATE);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400638
639 window->cairo_surface =
640 cairo_xcb_surface_create_with_xrender_format(wm->conn,
641 wm->screen,
642 window->frame_id,
Kristian Høgsberge89cef32012-07-16 11:57:08 -0400643 &wm->format_rgb,
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400644 width, height);
645
646 hash_table_insert(wm->window_hash, window->frame_id, window);
647}
648
649static void
650weston_wm_handle_map_notify(struct weston_wm *wm, xcb_generic_event_t *event)
651{
652 xcb_map_notify_event_t *map_notify = (xcb_map_notify_event_t *) event;
653
654 if (our_resource(wm, map_notify->window)) {
Martin Minarik6d118362012-06-07 18:01:59 +0200655 weston_log("XCB_MAP_NOTIFY (window %d, ours)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400656 map_notify->window);
657 return;
658 }
659
Martin Minarik6d118362012-06-07 18:01:59 +0200660 weston_log("XCB_MAP_NOTIFY (window %d)\n", map_notify->window);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400661}
662
663static void
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400664weston_wm_handle_unmap_notify(struct weston_wm *wm, xcb_generic_event_t *event)
665{
666 xcb_unmap_notify_event_t *unmap_notify =
667 (xcb_unmap_notify_event_t *) event;
668 struct weston_wm_window *window;
669
Martin Minarik6d118362012-06-07 18:01:59 +0200670 weston_log("XCB_UNMAP_NOTIFY (window %d, event %d%s)\n",
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400671 unmap_notify->window,
672 unmap_notify->event,
673 our_resource(wm, unmap_notify->window) ? ", ours" : "");
674
675 if (our_resource(wm, unmap_notify->window))
676 return;
677
Kristian Høgsbergd64bdf42012-05-31 10:33:43 -0400678 if (unmap_notify->response_type & 0x80)
679 /* We just ignore the ICCCM 4.1.4 synthetic unmap notify
680 * as it may come in after we've destroyed the window. */
Kristian Høgsbergf197e9f2012-05-30 11:46:29 -0400681 return;
Kristian Høgsbergf197e9f2012-05-30 11:46:29 -0400682
Kristian Høgsbergd64bdf42012-05-31 10:33:43 -0400683 window = hash_table_lookup(wm->window_hash, unmap_notify->window);
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400684 if (window->repaint_source)
685 wl_event_source_remove(window->repaint_source);
686 if (window->cairo_surface)
687 cairo_surface_destroy(window->cairo_surface);
688
Kristian Høgsbergbe375b32012-06-05 11:46:08 -0400689 if (window->frame_id) {
690 xcb_reparent_window(wm->conn, window->id, wm->wm_window, 0, 0);
691 xcb_destroy_window(wm->conn, window->frame_id);
692 weston_wm_window_set_state(window, ICCCM_WITHDRAWN_STATE);
693 hash_table_remove(wm->window_hash, window->frame_id);
694 window->frame_id = XCB_WINDOW_NONE;
695 }
Kristian Høgsberga6d9a5e2012-05-30 12:15:44 -0400696
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400697 if (wm->focus_window == window)
698 wm->focus_window = NULL;
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400699 if (window->surface)
700 wl_list_remove(&window->surface_destroy_listener.link);
701 window->surface = NULL;
702}
703
704static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400705weston_wm_window_draw_decoration(void *data)
706{
707 struct weston_wm_window *window = data;
708 struct weston_wm *wm = window->wm;
709 struct theme *t = wm->theme;
710 cairo_t *cr;
711 int x, y, width, height;
712 const char *title;
713 uint32_t flags = 0;
714
715 weston_wm_window_read_properties(window);
716
717 window->repaint_source = NULL;
718
719 weston_wm_window_get_frame_size(window, &width, &height);
720 weston_wm_window_get_child_position(window, &x, &y);
721
722 cairo_xcb_surface_set_size(window->cairo_surface, width, height);
723 cr = cairo_create(window->cairo_surface);
724
725 if (window->decorate) {
726 if (wm->focus_window == window)
727 flags |= THEME_FRAME_ACTIVE;
728
729 if (window->name)
730 title = window->name;
731 else
732 title = "untitled";
733
734 theme_render_frame(t, cr, width, height, title, flags);
735 } else {
736 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
737 cairo_set_source_rgba(cr, 0, 0, 0, 0);
738 cairo_paint(cr);
739
740 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
741 cairo_set_source_rgba(cr, 0, 0, 0, 0.45);
742 tile_mask(cr, t->shadow, 2, 2, width + 8, height + 8, 64, 64);
743 }
744
745 cairo_destroy(cr);
746
747 if (window->surface) {
Scott Moreau76d8fc82012-11-22 15:35:13 -0700748 pixman_region32_fini(&window->surface->pending.opaque);
749 pixman_region32_init_rect(&window->surface->pending.opaque, 0, 0,
Pekka Paalanen4f9c07b2012-09-03 16:48:41 +0300750 width, height);
751
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400752 /* We leave an extra pixel around the X window area to
753 * make sure we don't sample from the undefined alpha
754 * channel when filtering. */
Scott Moreau76d8fc82012-11-22 15:35:13 -0700755 pixman_region32_intersect_rect(&window->surface->pending.opaque,
756 &window->surface->pending.opaque,
Pekka Paalanen4f9c07b2012-09-03 16:48:41 +0300757 x - 1, y - 1,
758 window->width + 2,
759 window->height + 2);
760 window->surface->geometry.dirty = 1;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400761
762 pixman_region32_init_rect(&window->surface->input,
763 t->margin, t->margin,
764 width - 2 * t->margin,
765 height - 2 * t->margin);
766 }
767}
768
769static void
770weston_wm_window_schedule_repaint(struct weston_wm_window *window)
771{
772 struct weston_wm *wm = window->wm;
Pekka Paalanen4f9c07b2012-09-03 16:48:41 +0300773 int width, height;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400774
Kristian Høgsbergc4063f32012-07-22 15:32:45 -0400775 if (window->frame_id == XCB_WINDOW_NONE) {
776 if (window->surface != NULL) {
Pekka Paalanen4f9c07b2012-09-03 16:48:41 +0300777 weston_wm_window_get_frame_size(window, &width, &height);
Scott Moreau76d8fc82012-11-22 15:35:13 -0700778 pixman_region32_fini(&window->surface->pending.opaque);
779 pixman_region32_init_rect(&window->surface->pending.opaque, 0, 0,
Pekka Paalanen4f9c07b2012-09-03 16:48:41 +0300780 width, height);
781 window->surface->geometry.dirty = 1;
Kristian Høgsbergc4063f32012-07-22 15:32:45 -0400782 }
783 return;
784 }
785
786 if (window->repaint_source)
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400787 return;
788
789 window->repaint_source =
790 wl_event_loop_add_idle(wm->server->loop,
791 weston_wm_window_draw_decoration,
792 window);
793}
794
795static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400796weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event)
797{
798 xcb_property_notify_event_t *property_notify =
799 (xcb_property_notify_event_t *) event;
800 struct weston_wm_window *window;
801
802 window = hash_table_lookup(wm->window_hash, property_notify->window);
803 if (window)
804 window->properties_dirty = 1;
805
Martin Minarik6d118362012-06-07 18:01:59 +0200806 weston_log("XCB_PROPERTY_NOTIFY: window %d, ",
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400807 property_notify->window);
Kristian Høgsberge244cb02012-05-30 11:34:35 -0400808 if (property_notify->state == XCB_PROPERTY_DELETE)
Martin Minarik6d118362012-06-07 18:01:59 +0200809 weston_log("deleted\n");
Kristian Høgsberge244cb02012-05-30 11:34:35 -0400810 else
811 read_and_dump_property(wm, property_notify->window,
812 property_notify->atom);
Kristian Høgsberg0273b572012-05-30 09:58:02 -0400813
814 if (property_notify->atom == wm->atom.net_wm_name ||
815 property_notify->atom == XCB_ATOM_WM_NAME)
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400816 weston_wm_window_schedule_repaint(window);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400817}
818
819static void
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400820weston_wm_window_create(struct weston_wm *wm,
Tiago Vignatti771241e2012-06-04 20:01:45 +0300821 xcb_window_t id, int width, int height, int override)
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400822{
823 struct weston_wm_window *window;
824 uint32_t values[1];
825
826 window = malloc(sizeof *window);
827 if (window == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200828 weston_log("failed to allocate window\n");
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400829 return;
830 }
831
832 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
833 xcb_change_window_attributes(wm->conn, id, XCB_CW_EVENT_MASK, values);
834
835 memset(window, 0, sizeof *window);
836 window->wm = wm;
837 window->id = id;
838 window->properties_dirty = 1;
Tiago Vignatti771241e2012-06-04 20:01:45 +0300839 window->override_redirect = override;
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400840 window->width = width;
841 window->height = height;
842
843 hash_table_insert(wm->window_hash, id, window);
844}
845
846static void
847weston_wm_window_destroy(struct weston_wm_window *window)
848{
849 hash_table_remove(window->wm->window_hash, window->id);
850 free(window);
851}
852
853static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400854weston_wm_handle_create_notify(struct weston_wm *wm, xcb_generic_event_t *event)
855{
856 xcb_create_notify_event_t *create_notify =
857 (xcb_create_notify_event_t *) event;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400858
Martin Minarik6d118362012-06-07 18:01:59 +0200859 weston_log("XCB_CREATE_NOTIFY (window %d, width %d, height %d%s%s)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400860 create_notify->window,
861 create_notify->width, create_notify->height,
862 create_notify->override_redirect ? ", override" : "",
863 our_resource(wm, create_notify->window) ? ", ours" : "");
864
865 if (our_resource(wm, create_notify->window))
866 return;
867
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400868 weston_wm_window_create(wm, create_notify->window,
Tiago Vignatti771241e2012-06-04 20:01:45 +0300869 create_notify->width, create_notify->height,
870 create_notify->override_redirect);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400871}
872
873static void
874weston_wm_handle_destroy_notify(struct weston_wm *wm, xcb_generic_event_t *event)
875{
876 xcb_destroy_notify_event_t *destroy_notify =
877 (xcb_destroy_notify_event_t *) event;
878 struct weston_wm_window *window;
879
Martin Minarik6d118362012-06-07 18:01:59 +0200880 weston_log("XCB_DESTROY_NOTIFY, win %d, event %d%s\n",
Kristian Høgsberg194ea542012-05-30 10:05:41 -0400881 destroy_notify->window,
882 destroy_notify->event,
883 our_resource(wm, destroy_notify->window) ? ", ours" : "");
884
885 if (our_resource(wm, destroy_notify->window))
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400886 return;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400887
888 window = hash_table_lookup(wm->window_hash, destroy_notify->window);
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400889 weston_wm_window_destroy(window);
890}
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400891
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400892static void
893weston_wm_handle_reparent_notify(struct weston_wm *wm, xcb_generic_event_t *event)
894{
895 xcb_reparent_notify_event_t *reparent_notify =
896 (xcb_reparent_notify_event_t *) event;
897 struct weston_wm_window *window;
Kristian Høgsbergc9571fb2012-05-29 15:35:29 -0400898
Martin Minarik6d118362012-06-07 18:01:59 +0200899 weston_log("XCB_REPARENT_NOTIFY (window %d, parent %d, event %d)\n",
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400900 reparent_notify->window,
901 reparent_notify->parent,
902 reparent_notify->event);
903
904 if (reparent_notify->parent == wm->screen->root) {
Tiago Vignatti771241e2012-06-04 20:01:45 +0300905 weston_wm_window_create(wm, reparent_notify->window, 10, 10,
906 reparent_notify->override_redirect);
Kristian Høgsberg029539b2012-05-30 11:28:24 -0400907 } else if (!our_resource(wm, reparent_notify->parent)) {
908 window = hash_table_lookup(wm->window_hash,
909 reparent_notify->window);
910 weston_wm_window_destroy(window);
911 }
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400912}
913
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400914struct weston_seat *
915weston_wm_pick_seat(struct weston_wm *wm)
916{
917 return container_of(wm->server->compositor->seat_list.next,
918 struct weston_seat, link);
919}
920
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400921static void
Kristian Høgsberge68fd102012-05-22 17:09:40 -0400922weston_wm_window_handle_moveresize(struct weston_wm_window *window,
923 xcb_client_message_event_t *client_message)
924{
925 static const int map[] = {
926 THEME_LOCATION_RESIZING_TOP_LEFT,
927 THEME_LOCATION_RESIZING_TOP,
928 THEME_LOCATION_RESIZING_TOP_RIGHT,
929 THEME_LOCATION_RESIZING_RIGHT,
930 THEME_LOCATION_RESIZING_BOTTOM_RIGHT,
931 THEME_LOCATION_RESIZING_BOTTOM,
932 THEME_LOCATION_RESIZING_BOTTOM_LEFT,
933 THEME_LOCATION_RESIZING_LEFT
934 };
935
936 struct weston_wm *wm = window->wm;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -0400937 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberge68fd102012-05-22 17:09:40 -0400938 int detail;
939 struct weston_shell_interface *shell_interface =
940 &wm->server->compositor->shell_interface;
941
942 if (seat->seat.pointer->button_count != 1 ||
943 seat->seat.pointer->focus != &window->surface->surface)
944 return;
945
946 detail = client_message->data.data32[2];
947 switch (detail) {
948 case _NET_WM_MOVERESIZE_MOVE:
949 shell_interface->move(window->shsurf, seat);
950 break;
951 case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
952 case _NET_WM_MOVERESIZE_SIZE_TOP:
953 case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
954 case _NET_WM_MOVERESIZE_SIZE_RIGHT:
955 case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
956 case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
957 case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
958 case _NET_WM_MOVERESIZE_SIZE_LEFT:
959 shell_interface->resize(window->shsurf, seat, map[detail]);
960 break;
961 case _NET_WM_MOVERESIZE_CANCEL:
962 break;
963 }
964}
965
966static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400967weston_wm_handle_client_message(struct weston_wm *wm,
968 xcb_generic_event_t *event)
969{
970 xcb_client_message_event_t *client_message =
971 (xcb_client_message_event_t *) event;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400972 struct weston_wm_window *window;
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400973
974 window = hash_table_lookup(wm->window_hash, client_message->window);
975
Martin Minarik6d118362012-06-07 18:01:59 +0200976 weston_log("XCB_CLIENT_MESSAGE (%s %d %d %d %d %d)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400977 get_atom_name(wm->conn, client_message->type),
978 client_message->data.data32[0],
979 client_message->data.data32[1],
980 client_message->data.data32[2],
981 client_message->data.data32[3],
982 client_message->data.data32[4]);
983
Kristian Høgsberge68fd102012-05-22 17:09:40 -0400984 if (client_message->type == wm->atom.net_wm_moveresize)
985 weston_wm_window_handle_moveresize(window, client_message);
Kristian Høgsberg380deee2012-05-21 17:12:41 -0400986}
987
Tiago Vignatti236b48d2012-07-16 12:09:19 -0400988enum cursor_type {
989 XWM_CURSOR_TOP,
990 XWM_CURSOR_BOTTOM,
991 XWM_CURSOR_LEFT,
992 XWM_CURSOR_RIGHT,
993 XWM_CURSOR_TOP_LEFT,
994 XWM_CURSOR_TOP_RIGHT,
995 XWM_CURSOR_BOTTOM_LEFT,
996 XWM_CURSOR_BOTTOM_RIGHT,
997 XWM_CURSOR_LEFT_PTR,
998};
999
1000static const char *cursors[] = {
1001 "top_side",
1002 "bottom_side",
1003 "left_side",
1004 "right_side",
1005 "top_left_corner",
1006 "top_right_corner",
1007 "bottom_left_corner",
1008 "bottom_right_corner",
1009 "left_ptr"
1010};
1011
1012static void
1013weston_wm_create_cursors(struct weston_wm *wm)
1014{
1015 int i, count = ARRAY_LENGTH(cursors);
1016
1017 wm->cursors = malloc(count * sizeof(xcb_cursor_t));
1018 for (i = 0; i < count; i++) {
1019 wm->cursors[i] =
1020 xcb_cursor_library_load_cursor(wm, cursors[i]);
1021 }
1022
1023 wm->last_cursor = -1;
1024}
1025
1026static void
1027weston_wm_destroy_cursors(struct weston_wm *wm)
1028{
1029 uint8_t i;
1030
1031 for (i = 0; i < ARRAY_LENGTH(cursors); i++)
1032 xcb_free_cursor(wm->conn, wm->cursors[i]);
1033
1034 free(wm->cursors);
1035}
1036
1037static int
1038get_cursor_for_location(struct theme *t, int width, int height, int x, int y)
1039{
Scott Moreauc6a7e4b2012-09-28 02:45:06 -06001040 int location = theme_get_location(t, x, y, width, height, 0);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001041
1042 switch (location) {
1043 case THEME_LOCATION_RESIZING_TOP:
1044 return XWM_CURSOR_TOP;
1045 case THEME_LOCATION_RESIZING_BOTTOM:
1046 return XWM_CURSOR_BOTTOM;
1047 case THEME_LOCATION_RESIZING_LEFT:
1048 return XWM_CURSOR_LEFT;
1049 case THEME_LOCATION_RESIZING_RIGHT:
1050 return XWM_CURSOR_RIGHT;
1051 case THEME_LOCATION_RESIZING_TOP_LEFT:
1052 return XWM_CURSOR_TOP_LEFT;
1053 case THEME_LOCATION_RESIZING_TOP_RIGHT:
1054 return XWM_CURSOR_TOP_RIGHT;
1055 case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
1056 return XWM_CURSOR_BOTTOM_LEFT;
1057 case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
1058 return XWM_CURSOR_BOTTOM_RIGHT;
1059 case THEME_LOCATION_EXTERIOR:
1060 case THEME_LOCATION_TITLEBAR:
1061 default:
1062 return XWM_CURSOR_LEFT_PTR;
1063 }
1064}
1065
1066static void
Tiago Vignattic1903232012-07-16 12:15:37 -04001067weston_wm_window_set_cursor(struct weston_wm *wm, xcb_window_t window_id,
1068 int cursor)
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001069{
1070 uint32_t cursor_value_list;
1071
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001072 if (wm->last_cursor == cursor)
1073 return;
1074
1075 wm->last_cursor = cursor;
1076
1077 cursor_value_list = wm->cursors[cursor];
Tiago Vignattic1903232012-07-16 12:15:37 -04001078 xcb_change_window_attributes (wm->conn, window_id,
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001079 XCB_CW_CURSOR, &cursor_value_list);
1080 xcb_flush(wm->conn);
1081}
1082
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001083static void
1084weston_wm_handle_button(struct weston_wm *wm, xcb_generic_event_t *event)
1085{
1086 xcb_button_press_event_t *button = (xcb_button_press_event_t *) event;
1087 struct weston_shell_interface *shell_interface =
1088 &wm->server->compositor->shell_interface;
Kristian Høgsberg5ba31892012-08-10 10:06:59 -04001089 struct weston_seat *seat = weston_wm_pick_seat(wm);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001090 struct weston_wm_window *window;
Kristian Høgsbergf96e6c02012-05-22 16:38:53 -04001091 enum theme_location location;
1092 struct theme *t = wm->theme;
Kristian Høgsbergc1693f22012-05-22 16:56:23 -04001093 int width, height;
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001094
Martin Minarik6d118362012-06-07 18:01:59 +02001095 weston_log("XCB_BUTTON_%s (detail %d)\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001096 button->response_type == XCB_BUTTON_PRESS ?
1097 "PRESS" : "RELEASE", button->detail);
1098
1099 window = hash_table_lookup(wm->window_hash, button->event);
Kristian Høgsbergc1693f22012-05-22 16:56:23 -04001100 weston_wm_window_get_frame_size(window, &width, &height);
1101
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001102 if (button->response_type == XCB_BUTTON_PRESS &&
Kristian Høgsbergf96e6c02012-05-22 16:38:53 -04001103 button->detail == 1) {
1104 location = theme_get_location(t,
1105 button->event_x,
1106 button->event_y,
Scott Moreauc6a7e4b2012-09-28 02:45:06 -06001107 width, height, 0);
Kristian Høgsbergf96e6c02012-05-22 16:38:53 -04001108
1109 switch (location) {
1110 case THEME_LOCATION_TITLEBAR:
Kristian Høgsberg5ba31892012-08-10 10:06:59 -04001111 shell_interface->move(window->shsurf, seat);
Kristian Høgsbergf96e6c02012-05-22 16:38:53 -04001112 break;
Kristian Høgsbergc1693f22012-05-22 16:56:23 -04001113 case THEME_LOCATION_RESIZING_TOP:
1114 case THEME_LOCATION_RESIZING_BOTTOM:
1115 case THEME_LOCATION_RESIZING_LEFT:
1116 case THEME_LOCATION_RESIZING_RIGHT:
1117 case THEME_LOCATION_RESIZING_TOP_LEFT:
1118 case THEME_LOCATION_RESIZING_TOP_RIGHT:
1119 case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
1120 case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
1121 shell_interface->resize(window->shsurf,
Kristian Høgsberg5ba31892012-08-10 10:06:59 -04001122 seat, location);
Kristian Høgsbergc1693f22012-05-22 16:56:23 -04001123 break;
Kristian Høgsbergf96e6c02012-05-22 16:38:53 -04001124 default:
1125 break;
1126 }
1127 }
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001128}
1129
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001130static void
1131weston_wm_handle_motion(struct weston_wm *wm, xcb_generic_event_t *event)
1132{
1133 xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *) event;
1134 struct weston_wm_window *window;
1135 int cursor, width, height;
1136
1137 window = hash_table_lookup(wm->window_hash, motion->event);
Tiago Vignatti9134b772012-07-20 19:41:12 +03001138 if (!window || !window->decorate)
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001139 return;
1140
1141 weston_wm_window_get_frame_size(window, &width, &height);
1142 cursor = get_cursor_for_location(wm->theme, width, height,
1143 motion->event_x, motion->event_y);
1144
Tiago Vignattic1903232012-07-16 12:15:37 -04001145 weston_wm_window_set_cursor(wm, window->frame_id, cursor);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001146}
1147
1148static void
1149weston_wm_handle_enter(struct weston_wm *wm, xcb_generic_event_t *event)
1150{
1151 xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *) event;
1152 struct weston_wm_window *window;
1153 int cursor, width, height;
1154
1155 window = hash_table_lookup(wm->window_hash, enter->event);
Tiago Vignatti9134b772012-07-20 19:41:12 +03001156 if (!window || !window->decorate)
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001157 return;
1158
1159 weston_wm_window_get_frame_size(window, &width, &height);
1160 cursor = get_cursor_for_location(wm->theme, width, height,
1161 enter->event_x, enter->event_y);
1162
Tiago Vignattic1903232012-07-16 12:15:37 -04001163 weston_wm_window_set_cursor(wm, window->frame_id, cursor);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001164}
1165
1166static void
1167weston_wm_handle_leave(struct weston_wm *wm, xcb_generic_event_t *event)
1168{
1169 xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *) event;
1170 struct weston_wm_window *window;
1171
1172 window = hash_table_lookup(wm->window_hash, leave->event);
Tiago Vignatti9134b772012-07-20 19:41:12 +03001173 if (!window || !window->decorate)
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001174 return;
1175
Tiago Vignattic1903232012-07-16 12:15:37 -04001176 weston_wm_window_set_cursor(wm, window->frame_id, XWM_CURSOR_LEFT_PTR);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001177}
1178
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001179static int
1180weston_wm_handle_event(int fd, uint32_t mask, void *data)
1181{
1182 struct weston_wm *wm = data;
1183 xcb_generic_event_t *event;
1184 int count = 0;
1185
1186 while (event = xcb_poll_for_event(wm->conn), event != NULL) {
1187 if (weston_wm_handle_selection_event(wm, event)) {
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001188 free(event);
1189 count++;
1190 continue;
1191 }
1192
Kristian Høgsbergf197e9f2012-05-30 11:46:29 -04001193 switch (event->response_type & ~0x80) {
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001194 case XCB_BUTTON_PRESS:
1195 case XCB_BUTTON_RELEASE:
1196 weston_wm_handle_button(wm, event);
1197 break;
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001198 case XCB_ENTER_NOTIFY:
1199 weston_wm_handle_enter(wm, event);
1200 break;
1201 case XCB_LEAVE_NOTIFY:
1202 weston_wm_handle_leave(wm, event);
1203 break;
1204 case XCB_MOTION_NOTIFY:
1205 weston_wm_handle_motion(wm, event);
1206 break;
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001207 case XCB_CREATE_NOTIFY:
1208 weston_wm_handle_create_notify(wm, event);
1209 break;
1210 case XCB_MAP_REQUEST:
1211 weston_wm_handle_map_request(wm, event);
1212 break;
1213 case XCB_MAP_NOTIFY:
1214 weston_wm_handle_map_notify(wm, event);
1215 break;
1216 case XCB_UNMAP_NOTIFY:
Kristian Høgsberg194ea542012-05-30 10:05:41 -04001217 weston_wm_handle_unmap_notify(wm, event);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001218 break;
Kristian Høgsberg029539b2012-05-30 11:28:24 -04001219 case XCB_REPARENT_NOTIFY:
1220 weston_wm_handle_reparent_notify(wm, event);
1221 break;
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001222 case XCB_CONFIGURE_REQUEST:
1223 weston_wm_handle_configure_request(wm, event);
1224 break;
1225 case XCB_CONFIGURE_NOTIFY:
1226 weston_wm_handle_configure_notify(wm, event);
1227 break;
1228 case XCB_DESTROY_NOTIFY:
1229 weston_wm_handle_destroy_notify(wm, event);
1230 break;
1231 case XCB_MAPPING_NOTIFY:
Martin Minarik6d118362012-06-07 18:01:59 +02001232 weston_log("XCB_MAPPING_NOTIFY\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001233 break;
1234 case XCB_PROPERTY_NOTIFY:
1235 weston_wm_handle_property_notify(wm, event);
1236 break;
1237 case XCB_CLIENT_MESSAGE:
1238 weston_wm_handle_client_message(wm, event);
1239 break;
1240 }
1241
1242 free(event);
1243 count++;
1244 }
1245
1246 xcb_flush(wm->conn);
1247
1248 return count;
1249}
1250
1251static void
Tiago Vignatti9c4ff832012-11-30 17:19:57 -02001252weston_wm_get_resources(struct weston_wm *wm)
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001253{
1254
1255#define F(field) offsetof(struct weston_wm, field)
1256
1257 static const struct { const char *name; int offset; } atoms[] = {
1258 { "WM_PROTOCOLS", F(atom.wm_protocols) },
1259 { "WM_TAKE_FOCUS", F(atom.wm_take_focus) },
1260 { "WM_DELETE_WINDOW", F(atom.wm_delete_window) },
Kristian Høgsberga6d9a5e2012-05-30 12:15:44 -04001261 { "WM_STATE", F(atom.wm_state) },
Kristian Høgsberg670b5d32012-06-04 11:00:40 -04001262 { "WM_S0", F(atom.wm_s0) },
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +03001263 { "WM_CLIENT_MACHINE", F(atom.wm_client_machine) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001264 { "_NET_WM_NAME", F(atom.net_wm_name) },
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +03001265 { "_NET_WM_PID", F(atom.net_wm_pid) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001266 { "_NET_WM_ICON", F(atom.net_wm_icon) },
1267 { "_NET_WM_STATE", F(atom.net_wm_state) },
1268 { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) },
1269 { "_NET_WM_USER_TIME", F(atom.net_wm_user_time) },
1270 { "_NET_WM_ICON_NAME", F(atom.net_wm_icon_name) },
1271 { "_NET_WM_WINDOW_TYPE", F(atom.net_wm_window_type) },
1272
1273 { "_NET_WM_WINDOW_TYPE_DESKTOP", F(atom.net_wm_window_type_desktop) },
1274 { "_NET_WM_WINDOW_TYPE_DOCK", F(atom.net_wm_window_type_dock) },
1275 { "_NET_WM_WINDOW_TYPE_TOOLBAR", F(atom.net_wm_window_type_toolbar) },
1276 { "_NET_WM_WINDOW_TYPE_MENU", F(atom.net_wm_window_type_menu) },
1277 { "_NET_WM_WINDOW_TYPE_UTILITY", F(atom.net_wm_window_type_utility) },
1278 { "_NET_WM_WINDOW_TYPE_SPLASH", F(atom.net_wm_window_type_splash) },
1279 { "_NET_WM_WINDOW_TYPE_DIALOG", F(atom.net_wm_window_type_dialog) },
Tiago Vignattibf1e8662012-06-12 14:07:49 +03001280 { "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", F(atom.net_wm_window_type_dropdown) },
1281 { "_NET_WM_WINDOW_TYPE_POPUP_MENU", F(atom.net_wm_window_type_popup) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001282 { "_NET_WM_WINDOW_TYPE_TOOLTIP", F(atom.net_wm_window_type_tooltip) },
1283 { "_NET_WM_WINDOW_TYPE_NOTIFICATION", F(atom.net_wm_window_type_notification) },
1284 { "_NET_WM_WINDOW_TYPE_COMBO", F(atom.net_wm_window_type_combo) },
1285 { "_NET_WM_WINDOW_TYPE_DND", F(atom.net_wm_window_type_dnd) },
1286 { "_NET_WM_WINDOW_TYPE_NORMAL", F(atom.net_wm_window_type_normal) },
1287
1288 { "_NET_WM_MOVERESIZE", F(atom.net_wm_moveresize) },
1289 { "_NET_SUPPORTING_WM_CHECK",
1290 F(atom.net_supporting_wm_check) },
1291 { "_NET_SUPPORTED", F(atom.net_supported) },
1292 { "_MOTIF_WM_HINTS", F(atom.motif_wm_hints) },
1293 { "CLIPBOARD", F(atom.clipboard) },
Kristian Høgsbergcba022a2012-06-04 10:11:45 -04001294 { "CLIPBOARD_MANAGER", F(atom.clipboard_manager) },
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001295 { "TARGETS", F(atom.targets) },
1296 { "UTF8_STRING", F(atom.utf8_string) },
1297 { "_WL_SELECTION", F(atom.wl_selection) },
1298 { "INCR", F(atom.incr) },
1299 { "TIMESTAMP", F(atom.timestamp) },
1300 { "MULTIPLE", F(atom.multiple) },
1301 { "UTF8_STRING" , F(atom.utf8_string) },
1302 { "COMPOUND_TEXT", F(atom.compound_text) },
1303 { "TEXT", F(atom.text) },
1304 { "STRING", F(atom.string) },
1305 { "text/plain;charset=utf-8", F(atom.text_plain_utf8) },
1306 { "text/plain", F(atom.text_plain) },
1307 };
1308#undef F
1309
1310 xcb_xfixes_query_version_cookie_t xfixes_cookie;
1311 xcb_xfixes_query_version_reply_t *xfixes_reply;
1312 xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
1313 xcb_intern_atom_reply_t *reply;
1314 xcb_render_query_pict_formats_reply_t *formats_reply;
1315 xcb_render_query_pict_formats_cookie_t formats_cookie;
1316 xcb_render_pictforminfo_t *formats;
1317 uint32_t i;
1318
1319 xcb_prefetch_extension_data (wm->conn, &xcb_xfixes_id);
1320
1321 formats_cookie = xcb_render_query_pict_formats(wm->conn);
1322
1323 for (i = 0; i < ARRAY_LENGTH(atoms); i++)
1324 cookies[i] = xcb_intern_atom (wm->conn, 0,
1325 strlen(atoms[i].name),
1326 atoms[i].name);
1327
1328 for (i = 0; i < ARRAY_LENGTH(atoms); i++) {
1329 reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL);
1330 *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom;
1331 free(reply);
1332 }
1333
1334 wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id);
1335 if (!wm->xfixes || !wm->xfixes->present)
Martin Minarik6d118362012-06-07 18:01:59 +02001336 weston_log("xfixes not available\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001337
1338 xfixes_cookie = xcb_xfixes_query_version(wm->conn,
1339 XCB_XFIXES_MAJOR_VERSION,
1340 XCB_XFIXES_MINOR_VERSION);
1341 xfixes_reply = xcb_xfixes_query_version_reply(wm->conn,
1342 xfixes_cookie, NULL);
1343
Martin Minarik6d118362012-06-07 18:01:59 +02001344 weston_log("xfixes version: %d.%d\n",
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001345 xfixes_reply->major_version, xfixes_reply->minor_version);
1346
1347 free(xfixes_reply);
1348
1349 formats_reply = xcb_render_query_pict_formats_reply(wm->conn,
1350 formats_cookie, 0);
1351 if (formats_reply == NULL)
1352 return;
1353
1354 formats = xcb_render_query_pict_formats_formats(formats_reply);
Kristian Høgsberge89cef32012-07-16 11:57:08 -04001355 for (i = 0; i < formats_reply->num_formats; i++) {
1356 if (formats[i].direct.red_mask != 0xff &&
1357 formats[i].direct.red_shift != 16)
1358 continue;
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001359 if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT &&
1360 formats[i].depth == 24)
Kristian Høgsberge89cef32012-07-16 11:57:08 -04001361 wm->format_rgb = formats[i];
1362 if (formats[i].type == XCB_RENDER_PICT_TYPE_DIRECT &&
1363 formats[i].depth == 32 &&
1364 formats[i].direct.alpha_mask == 0xff &&
1365 formats[i].direct.alpha_shift == 24)
1366 wm->format_rgba = formats[i];
1367 }
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001368
1369 free(formats_reply);
1370}
1371
1372static void
1373weston_wm_create_wm_window(struct weston_wm *wm)
1374{
1375 static const char name[] = "Weston WM";
1376
1377 wm->wm_window = xcb_generate_id(wm->conn);
1378 xcb_create_window(wm->conn,
1379 XCB_COPY_FROM_PARENT,
1380 wm->wm_window,
1381 wm->screen->root,
1382 0, 0,
1383 10, 10,
1384 0,
1385 XCB_WINDOW_CLASS_INPUT_OUTPUT,
1386 wm->screen->root_visual,
1387 0, NULL);
1388
1389 xcb_change_property(wm->conn,
1390 XCB_PROP_MODE_REPLACE,
1391 wm->wm_window,
1392 wm->atom.net_supporting_wm_check,
1393 XCB_ATOM_WINDOW,
1394 32, /* format */
1395 1, &wm->wm_window);
1396
1397 xcb_change_property(wm->conn,
1398 XCB_PROP_MODE_REPLACE,
1399 wm->wm_window,
1400 wm->atom.net_wm_name,
1401 wm->atom.utf8_string,
1402 8, /* format */
1403 strlen(name), name);
1404
1405 xcb_change_property(wm->conn,
1406 XCB_PROP_MODE_REPLACE,
1407 wm->screen->root,
1408 wm->atom.net_supporting_wm_check,
1409 XCB_ATOM_WINDOW,
1410 32, /* format */
1411 1, &wm->wm_window);
1412
Kristian Høgsberg670b5d32012-06-04 11:00:40 -04001413 /* Claim the WM_S0 selection even though we don't suport
1414 * the --replace functionality. */
1415 xcb_set_selection_owner(wm->conn,
1416 wm->wm_window,
1417 wm->atom.wm_s0,
1418 XCB_TIME_CURRENT_TIME);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001419}
1420
1421struct weston_wm *
1422weston_wm_create(struct weston_xserver *wxs)
1423{
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001424 struct weston_wm *wm;
1425 struct wl_event_loop *loop;
1426 xcb_screen_iterator_t s;
Kristian Høgsberg4dec0112012-06-03 09:18:06 -04001427 uint32_t values[1];
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001428 int sv[2];
1429 xcb_atom_t supported[1];
1430
1431 wm = malloc(sizeof *wm);
1432 if (wm == NULL)
1433 return NULL;
1434
1435 memset(wm, 0, sizeof *wm);
1436 wm->server = wxs;
1437 wm->window_hash = hash_table_create();
1438 if (wm->window_hash == NULL) {
1439 free(wm);
1440 return NULL;
1441 }
1442
1443 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02001444 weston_log("socketpair failed\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001445 hash_table_destroy(wm->window_hash);
1446 free(wm);
1447 return NULL;
1448 }
1449
1450 xserver_send_client(wxs->resource, sv[1]);
1451 wl_client_flush(wxs->resource->client);
1452 close(sv[1]);
1453
1454 /* xcb_connect_to_fd takes ownership of the fd. */
1455 wm->conn = xcb_connect_to_fd(sv[0], NULL);
1456 if (xcb_connection_has_error(wm->conn)) {
Martin Minarik6d118362012-06-07 18:01:59 +02001457 weston_log("xcb_connect_to_fd failed\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001458 close(sv[0]);
1459 hash_table_destroy(wm->window_hash);
1460 free(wm);
1461 return NULL;
1462 }
1463
1464 s = xcb_setup_roots_iterator(xcb_get_setup(wm->conn));
1465 wm->screen = s.data;
1466
1467 loop = wl_display_get_event_loop(wxs->wl_display);
1468 wm->source =
1469 wl_event_loop_add_fd(loop, sv[0],
1470 WL_EVENT_READABLE,
1471 weston_wm_handle_event, wm);
1472 wl_event_source_check(wm->source);
1473
Tiago Vignatti9c4ff832012-11-30 17:19:57 -02001474 weston_wm_get_resources(wm);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001475
1476 values[0] =
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001477 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
1478 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
1479 XCB_EVENT_MASK_PROPERTY_CHANGE;
1480 xcb_change_window_attributes(wm->conn, wm->screen->root,
1481 XCB_CW_EVENT_MASK, values);
1482 wm->theme = theme_create();
1483
1484 weston_wm_create_wm_window(wm);
1485
1486 supported[0] = wm->atom.net_wm_moveresize;
1487 xcb_change_property(wm->conn,
1488 XCB_PROP_MODE_REPLACE,
1489 wm->screen->root,
1490 wm->atom.net_supported,
1491 XCB_ATOM_ATOM,
1492 32, /* format */
1493 ARRAY_LENGTH(supported), supported);
1494
Kristian Høgsberg4dec0112012-06-03 09:18:06 -04001495 weston_wm_selection_init(wm);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001496
1497 xcb_flush(wm->conn);
1498
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001499 wm->activate_listener.notify = weston_wm_window_activate;
1500 wl_signal_add(&wxs->compositor->activate_signal,
1501 &wm->activate_listener);
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +03001502 wm->kill_listener.notify = weston_wm_kill_client;
1503 wl_signal_add(&wxs->compositor->kill_signal,
1504 &wm->kill_listener);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001505
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001506 weston_wm_create_cursors(wm);
Tiago Vignattic1903232012-07-16 12:15:37 -04001507 weston_wm_window_set_cursor(wm, wm->screen->root, XWM_CURSOR_LEFT_PTR);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001508
Martin Minarik6d118362012-06-07 18:01:59 +02001509 weston_log("created wm\n");
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001510
1511 return wm;
1512}
1513
1514void
1515weston_wm_destroy(struct weston_wm *wm)
1516{
1517 /* FIXME: Free windows in hash. */
1518 hash_table_destroy(wm->window_hash);
Tiago Vignatti236b48d2012-07-16 12:09:19 -04001519 weston_wm_destroy_cursors(wm);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001520 xcb_disconnect(wm->conn);
1521 wl_event_source_remove(wm->source);
1522 wl_list_remove(&wm->selection_listener.link);
1523 wl_list_remove(&wm->activate_listener.link);
Tiago Vignatti0d20d7c2012-09-27 17:48:37 +03001524 wl_list_remove(&wm->kill_listener.link);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001525
1526 free(wm);
1527}
1528
1529static void
1530surface_destroy(struct wl_listener *listener, void *data)
1531{
1532 struct weston_wm_window *window =
1533 container_of(listener,
1534 struct weston_wm_window, surface_destroy_listener);
1535
Martin Minarik6d118362012-06-07 18:01:59 +02001536 weston_log("surface for xid %d destroyed\n", window->id);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001537}
1538
1539static struct weston_wm_window *
1540get_wm_window(struct weston_surface *surface)
1541{
1542 struct wl_resource *resource = &surface->surface.resource;
1543 struct wl_listener *listener;
1544
1545 listener = wl_signal_get(&resource->destroy_signal, surface_destroy);
1546 if (listener)
1547 return container_of(listener, struct weston_wm_window,
1548 surface_destroy_listener);
1549
1550 return NULL;
1551}
1552
1553static void
Kristian Høgsberga61ca062012-05-22 16:05:52 -04001554weston_wm_window_configure(void *data)
1555{
1556 struct weston_wm_window *window = data;
1557 struct weston_wm *wm = window->wm;
1558 uint32_t values[2];
1559 int width, height;
1560
1561 values[0] = window->width;
1562 values[1] = window->height;
1563 xcb_configure_window(wm->conn,
1564 window->id,
1565 XCB_CONFIG_WINDOW_WIDTH |
1566 XCB_CONFIG_WINDOW_HEIGHT,
1567 values);
1568
1569 weston_wm_window_get_frame_size(window, &width, &height);
1570 values[0] = width;
1571 values[1] = height;
1572 xcb_configure_window(wm->conn,
1573 window->frame_id,
1574 XCB_CONFIG_WINDOW_WIDTH |
1575 XCB_CONFIG_WINDOW_HEIGHT,
1576 values);
1577
1578 window->configure_source = NULL;
1579
1580 weston_wm_window_schedule_repaint(window);
1581}
1582
1583static void
1584send_configure(struct weston_surface *surface,
1585 uint32_t edges, int32_t width, int32_t height)
1586{
1587 struct weston_wm_window *window = get_wm_window(surface);
1588 struct weston_wm *wm = window->wm;
1589 struct theme *t = window->wm->theme;
1590
1591 if (window->decorate) {
1592 window->width = width - 2 * (t->margin + t->width);
1593 window->height = height - 2 * t->margin -
1594 t->titlebar_height - t->width;
1595 } else {
1596 window->width = width - 2 * t->margin;
1597 window->height = height - 2 * t->margin;
1598 }
1599
1600 if (window->configure_source)
1601 return;
1602
1603 window->configure_source =
1604 wl_event_loop_add_idle(wm->server->loop,
1605 weston_wm_window_configure, window);
1606}
1607
1608static const struct weston_shell_client shell_client = {
1609 send_configure
1610};
1611
1612static void
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001613xserver_map_shell_surface(struct weston_wm *wm,
1614 struct weston_wm_window *window)
1615{
1616 struct weston_shell_interface *shell_interface =
1617 &wm->server->compositor->shell_interface;
1618 struct weston_wm_window *parent;
1619 struct theme *t = window->wm->theme;
Tiago Vignattice1baa82012-07-20 23:09:55 +03001620 int parent_id, x = 0, y = 0;
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001621
1622 if (!shell_interface->create_shell_surface)
1623 return;
1624
1625 window->shsurf =
1626 shell_interface->create_shell_surface(shell_interface->shell,
Kristian Høgsberga61ca062012-05-22 16:05:52 -04001627 window->surface,
1628 &shell_client);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001629
Tiago Vignatti771241e2012-06-04 20:01:45 +03001630 /* ICCCM 4.1.1 */
Tiago Vignattice1baa82012-07-20 23:09:55 +03001631 if (!window->override_redirect) {
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001632 shell_interface->set_toplevel(window->shsurf);
1633 return;
1634 }
1635
Tiago Vignattice1baa82012-07-20 23:09:55 +03001636 /* not all non-toplevel has transient_for set. So we need this
1637 * workaround to guess a parent that will determine the relative
1638 * position of the transient surface */
1639 if (!window->transient_for)
1640 parent_id = wm->focus_latest->id;
1641 else
1642 parent_id = window->transient_for->id;
1643
1644 parent = hash_table_lookup(wm->window_hash, parent_id);
Tiago Vignattie66fcee2012-07-20 23:09:54 +03001645
1646 /* non-decorated and non-toplevel windows, e.g. sub-menus */
1647 if (!parent->decorate && parent->override_redirect) {
1648 x = parent->x + t->margin;
1649 y = parent->y + t->margin;
1650 }
1651
Kristian Høgsberg8150b192012-06-27 10:22:58 -04001652 shell_interface->set_transient(window->shsurf, parent->surface,
Tiago Vignattie66fcee2012-07-20 23:09:54 +03001653 window->x + t->margin - x,
1654 window->y + t->margin - y,
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001655 WL_SHELL_SURFACE_TRANSIENT_INACTIVE);
1656}
1657
1658static void
1659xserver_set_window_id(struct wl_client *client, struct wl_resource *resource,
1660 struct wl_resource *surface_resource, uint32_t id)
1661{
1662 struct weston_xserver *wxs = resource->data;
1663 struct weston_wm *wm = wxs->wm;
1664 struct wl_surface *surface = surface_resource->data;
1665 struct weston_wm_window *window;
1666
1667 if (client != wxs->client)
1668 return;
1669
1670 window = hash_table_lookup(wm->window_hash, id);
1671 if (window == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02001672 weston_log("set_window_id for unknown window %d\n", id);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001673 return;
1674 }
1675
Martin Minarik6d118362012-06-07 18:01:59 +02001676 weston_log("set_window_id %d for surface %p\n", id, surface);
Kristian Høgsberg380deee2012-05-21 17:12:41 -04001677
1678 weston_wm_window_read_properties(window);
1679
1680 window->surface = (struct weston_surface *) surface;
1681 window->surface_destroy_listener.notify = surface_destroy;
1682 wl_signal_add(&surface->resource.destroy_signal,
1683 &window->surface_destroy_listener);
1684
1685 weston_wm_window_schedule_repaint(window);
1686 xserver_map_shell_surface(wm, window);
1687}
1688
1689const struct xserver_interface xserver_implementation = {
1690 xserver_set_window_id
1691};