1/* swc: libswc/xdg_shell.c
2 *
3 * Copyright (c) 2014, 2018 Michael Forney
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include "xdg_shell.h"
25#include "compositor.h"
26#include "internal.h"
27#include "seat.h"
28#include "surface.h"
29#include "util.h"
30#include "window.h"
31
32#include "xdg-shell-server-protocol.h"
33#include <assert.h>
34#include <stdlib.h>
35#include <wayland-server.h>
36
37struct xdg_surface {
38 struct wl_resource *resource, *role;
39 struct surface *surface;
40 struct wl_listener surface_destroy_listener, role_destroy_listener;
41 uint32_t configure_serial;
42};
43
44struct xdg_positioner {
45 int32_t width, height;
46 int32_t anchor_x, anchor_y;
47 int32_t anchor_width, anchor_height;
48 enum xdg_positioner_anchor anchor;
49 enum xdg_positioner_gravity gravity;
50 enum xdg_positioner_constraint_adjustment constraint;
51 int32_t offset_x, offset_y;
52};
53
54struct xdg_toplevel {
55 struct window window;
56 struct wl_resource *resource;
57 struct wl_array states;
58 struct xdg_surface *xdg_surface;
59};
60
61struct xdg_popup {
62 struct wl_resource *resource;
63 struct xdg_surface *xdg_surface;
64 struct xdg_positioner positioner;
65 struct compositor_view *view;
66};
67
68/* xdg_positioner */
69static void
70destroy_positioner(struct wl_resource *resource)
71{
72 struct xdg_positioner *positioner = wl_resource_get_user_data(resource);
73
74 free(positioner);
75}
76
77static void
78set_size(struct wl_client *client, struct wl_resource *resource, int32_t width,
79 int32_t height)
80{
81 struct xdg_positioner *positioner = wl_resource_get_user_data(resource);
82
83 if (width <= 0 || height <= 0) {
84 wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT,
85 "invalid size");
86 return;
87 }
88 positioner->width = width;
89 positioner->height = height;
90}
91
92static void
93set_anchor_rect(struct wl_client *client, struct wl_resource *resource,
94 int32_t x, int32_t y, int32_t width, int32_t height)
95{
96 struct xdg_positioner *positioner = wl_resource_get_user_data(resource);
97
98 if (width <= 0 || height <= 0) {
99 wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT,
100 "invalid anchor size");
101 return;
102 }
103 positioner->anchor_x = x;
104 positioner->anchor_y = y;
105 positioner->anchor_width = width;
106 positioner->anchor_height = height;
107}
108
109static void
110set_anchor(struct wl_client *client, struct wl_resource *resource,
111 uint32_t anchor)
112{
113 struct xdg_positioner *positioner = wl_resource_get_user_data(resource);
114
115 positioner->anchor = anchor;
116}
117
118static void
119set_gravity(struct wl_client *client, struct wl_resource *resource,
120 uint32_t gravity)
121{
122 struct xdg_positioner *positioner = wl_resource_get_user_data(resource);
123
124 positioner->gravity = gravity;
125}
126
127static void
128set_constraint_adjustment(struct wl_client *client,
129 struct wl_resource *resource, uint32_t constraint)
130{
131 struct xdg_positioner *positioner = wl_resource_get_user_data(resource);
132
133 positioner->constraint = constraint;
134}
135
136static void
137set_offset(struct wl_client *client, struct wl_resource *resource, int32_t x,
138 int32_t y)
139{
140 struct xdg_positioner *positioner = wl_resource_get_user_data(resource);
141
142 positioner->offset_x = x;
143 positioner->offset_y = y;
144}
145
146static const struct xdg_positioner_interface positioner_impl = {
147 .destroy = destroy_resource,
148 .set_size = set_size,
149 .set_anchor_rect = set_anchor_rect,
150 .set_anchor = set_anchor,
151 .set_gravity = set_gravity,
152 .set_constraint_adjustment = set_constraint_adjustment,
153 .set_offset = set_offset,
154};
155
156static struct swc_rectangle
157calculate_position(struct xdg_positioner *positioner)
158{
159 struct swc_rectangle r = {
160 .x = positioner->offset_x,
161 .y = positioner->offset_y,
162 .width = positioner->width,
163 .height = positioner->height,
164 };
165
166 switch (positioner->anchor) {
167 case XDG_POSITIONER_ANCHOR_TOP:
168 case XDG_POSITIONER_ANCHOR_TOP_LEFT:
169 case XDG_POSITIONER_ANCHOR_TOP_RIGHT:
170 r.y += positioner->anchor_y;
171 break;
172 case XDG_POSITIONER_ANCHOR_BOTTOM:
173 case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT:
174 case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT:
175 r.y += positioner->anchor_y + positioner->anchor_height;
176 break;
177 default:
178 r.y += positioner->anchor_y + positioner->anchor_height / 2;
179 }
180 switch (positioner->anchor) {
181 case XDG_POSITIONER_ANCHOR_LEFT:
182 case XDG_POSITIONER_ANCHOR_TOP_LEFT:
183 case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT:
184 r.x += positioner->anchor_x;
185 break;
186 case XDG_POSITIONER_ANCHOR_RIGHT:
187 case XDG_POSITIONER_ANCHOR_TOP_RIGHT:
188 case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT:
189 r.x += positioner->anchor_x + positioner->anchor_width;
190 break;
191 default:
192 r.x += positioner->anchor_x + positioner->anchor_width / 2;
193 }
194
195 switch (positioner->gravity) {
196 case XDG_POSITIONER_GRAVITY_TOP:
197 case XDG_POSITIONER_GRAVITY_TOP_LEFT:
198 case XDG_POSITIONER_GRAVITY_TOP_RIGHT:
199 r.y -= r.height;
200 break;
201 case XDG_POSITIONER_GRAVITY_BOTTOM:
202 case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT:
203 case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT:
204 break;
205 default:
206 r.y -= r.height / 2;
207 }
208 switch (positioner->gravity) {
209 case XDG_POSITIONER_GRAVITY_LEFT:
210 case XDG_POSITIONER_GRAVITY_TOP_LEFT:
211 case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT:
212 r.x -= r.width;
213 break;
214 case XDG_POSITIONER_GRAVITY_RIGHT:
215 case XDG_POSITIONER_GRAVITY_TOP_RIGHT:
216 case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT:
217 break;
218 default:
219 r.x -= r.width / 2;
220 }
221
222 return r;
223}
224
225/* xdg_toplevel */
226static void
227destroy_toplevel(struct wl_resource *resource)
228{
229 struct xdg_toplevel *toplevel = wl_resource_get_user_data(resource);
230
231 window_finalize(&toplevel->window);
232 free(toplevel);
233}
234
235static bool
236add_state(struct xdg_toplevel *toplevel, uint32_t state)
237{
238 uint32_t *current_state;
239
240 wl_array_for_each(current_state, &toplevel->states)
241 {
242 if (*current_state == state) {
243 return false;
244 }
245 }
246
247 if (!(current_state = wl_array_add(&toplevel->states, sizeof(state)))) {
248 WARNING("xdg_toplevel: Failed to allocate new state\n");
249 return false;
250 }
251
252 *current_state = state;
253 return true;
254}
255
256static bool
257remove_state(struct xdg_toplevel *toplevel, uint32_t state)
258{
259 uint32_t *current_state;
260
261 wl_array_for_each(current_state, &toplevel->states)
262 {
263 if (*current_state == state) {
264 array_remove(&toplevel->states, current_state, sizeof(state));
265 return true;
266 }
267 }
268
269 return false;
270}
271
272static uint32_t
273send_configure(struct xdg_toplevel *toplevel, int32_t width, int32_t height)
274{
275 uint32_t serial = wl_display_next_serial(swc.display);
276
277 if (width < 0) {
278 width = toplevel->window.configure.width;
279 }
280 if (height < 0) {
281 height = toplevel->window.configure.height;
282 }
283
284 xdg_toplevel_send_configure(toplevel->resource, width, height,
285 &toplevel->states);
286 xdg_surface_send_configure(toplevel->xdg_surface->resource, serial);
287
288 return serial;
289}
290
291static void
292configure(struct window *window, uint32_t width, uint32_t height)
293{
294 struct xdg_toplevel *toplevel = wl_container_of(window, toplevel, window);
295
296 window->configure.acknowledged = false;
297 toplevel->xdg_surface->configure_serial =
298 send_configure(toplevel, width, height);
299}
300
301static void
302focus(struct window *window)
303{
304 struct xdg_toplevel *toplevel = wl_container_of(window, toplevel, window);
305 uint32_t width = window->view->base.geometry.width;
306 uint32_t height = window->view->base.geometry.height;
307
308 add_state(toplevel, XDG_TOPLEVEL_STATE_ACTIVATED);
309 /* dont send 0x0 on focus change */
310 send_configure(toplevel, width ? (int32_t)width : -1,
311 height ? (int32_t)height : -1);
312}
313
314static void
315unfocus(struct window *window)
316{
317 struct xdg_toplevel *toplevel = wl_container_of(window, toplevel, window);
318 uint32_t width = window->view->base.geometry.width;
319 uint32_t height = window->view->base.geometry.height;
320
321 remove_state(toplevel, XDG_TOPLEVEL_STATE_ACTIVATED);
322 send_configure(toplevel, width ? (int32_t)width : -1,
323 height ? (int32_t)height : -1);
324}
325
326static void
327close_(struct window *window)
328{
329 struct xdg_toplevel *toplevel = wl_container_of(window, toplevel, window);
330
331 xdg_toplevel_send_close(toplevel->resource);
332}
333
334static void
335set_mode(struct window *window, unsigned mode)
336{
337 struct xdg_toplevel *toplevel = wl_container_of(window, toplevel, window);
338
339 switch (window->mode) {
340 case WINDOW_MODE_TILED:
341 remove_state(toplevel, XDG_TOPLEVEL_STATE_MAXIMIZED);
342 break;
343 case WINDOW_MODE_FULLSCREEN:
344 remove_state(toplevel, XDG_TOPLEVEL_STATE_FULLSCREEN);
345 break;
346 }
347
348 switch (mode) {
349 case WINDOW_MODE_TILED:
350 add_state(toplevel, XDG_TOPLEVEL_STATE_MAXIMIZED);
351 break;
352 case WINDOW_MODE_FULLSCREEN:
353 add_state(toplevel, XDG_TOPLEVEL_STATE_FULLSCREEN);
354 break;
355 }
356
357 send_configure(toplevel, -1, -1);
358}
359
360static const struct window_impl toplevel_window_impl = {
361 .configure = configure,
362 .focus = focus,
363 .unfocus = unfocus,
364 .close = close_,
365 .set_mode = set_mode,
366};
367
368static void
369set_parent(struct wl_client *client, struct wl_resource *resource,
370 struct wl_resource *parent_resource)
371{
372 struct xdg_toplevel *toplevel = wl_resource_get_user_data(resource),
373 *parent = NULL;
374
375 if (parent_resource) {
376 parent = wl_resource_get_user_data(parent_resource);
377 }
378 window_set_parent(&toplevel->window, parent ? &parent->window : NULL);
379}
380
381static void
382set_title(struct wl_client *client, struct wl_resource *resource,
383 const char *title)
384{
385 struct xdg_toplevel *toplevel = wl_resource_get_user_data(resource);
386 window_set_title(&toplevel->window, title, -1);
387}
388
389static void
390set_app_id(struct wl_client *client, struct wl_resource *resource,
391 const char *app_id)
392{
393 struct xdg_toplevel *toplevel = wl_resource_get_user_data(resource);
394 window_set_app_id(&toplevel->window, app_id);
395}
396
397static void
398show_window_menu(struct wl_client *client, struct wl_resource *resource,
399 struct wl_resource *seat, uint32_t serial, int32_t x,
400 int32_t y)
401{
402}
403
404static void
405move(struct wl_client *client, struct wl_resource *resource,
406 struct wl_resource *seat, uint32_t serial)
407{
408 struct xdg_toplevel *toplevel = wl_resource_get_user_data(resource);
409 struct button *button;
410
411 button = pointer_get_button(swc.seat->pointer, serial);
412 if (button) {
413 window_begin_move(&toplevel->window, button);
414 }
415}
416
417static void
418resize(struct wl_client *client, struct wl_resource *resource,
419 struct wl_resource *seat, uint32_t serial, uint32_t edges)
420{
421 struct xdg_toplevel *toplevel = wl_resource_get_user_data(resource);
422 struct button *button;
423
424 button = pointer_get_button(swc.seat->pointer, serial);
425 if (button) {
426 window_begin_resize(&toplevel->window, edges, button);
427 }
428}
429
430static void
431set_max_size(struct wl_client *client, struct wl_resource *resource,
432 int32_t width, int32_t height)
433{
434}
435
436static void
437set_min_size(struct wl_client *client, struct wl_resource *resource,
438 int32_t width, int32_t height)
439{
440}
441
442static void
443set_maximized(struct wl_client *client, struct wl_resource *resource)
444{
445}
446
447static void
448unset_maximized(struct wl_client *client, struct wl_resource *resource)
449{
450}
451
452static void
453set_fullscreen(struct wl_client *client, struct wl_resource *resource,
454 struct wl_resource *output)
455{
456}
457
458static void
459unset_fullscreen(struct wl_client *client, struct wl_resource *resource)
460{
461}
462
463static void
464set_minimized(struct wl_client *client, struct wl_resource *resource)
465{
466}
467
468static const struct xdg_toplevel_interface toplevel_impl = {
469 .destroy = destroy_resource,
470 .set_parent = set_parent,
471 .set_title = set_title,
472 .set_app_id = set_app_id,
473 .show_window_menu = show_window_menu,
474 .move = move,
475 .resize = resize,
476 .set_max_size = set_max_size,
477 .set_min_size = set_min_size,
478 .set_maximized = set_maximized,
479 .unset_maximized = unset_maximized,
480 .set_fullscreen = set_fullscreen,
481 .unset_fullscreen = unset_fullscreen,
482 .set_minimized = set_minimized,
483};
484
485static struct xdg_toplevel *
486xdg_toplevel_new(struct wl_client *client, uint32_t version, uint32_t id,
487 struct xdg_surface *xdg_surface)
488{
489 struct xdg_toplevel *toplevel;
490
491 toplevel = malloc(sizeof(*toplevel));
492 if (!toplevel) {
493 goto error0;
494 }
495 toplevel->xdg_surface = xdg_surface;
496 toplevel->resource =
497 wl_resource_create(client, &xdg_toplevel_interface, version, id);
498 if (!toplevel->resource) {
499 goto error1;
500 }
501 if (!surface_set_role(xdg_surface->surface, toplevel->resource)) {
502 goto error2;
503 }
504 if (!window_initialize(&toplevel->window, &toplevel_window_impl,
505 xdg_surface->surface)) {
506 goto error2;
507 }
508 wl_array_init(&toplevel->states);
509 wl_resource_set_implementation(toplevel->resource, &toplevel_impl, toplevel,
510 &destroy_toplevel);
511 window_manage(&toplevel->window);
512
513 return toplevel;
514
515error2:
516 wl_resource_destroy(toplevel->resource);
517 free(toplevel);
518error1:
519error0:
520 return NULL;
521}
522
523/* xdg_popup */
524static void
525destroy_popup(struct wl_resource *resource)
526{
527 struct xdg_popup *popup = wl_resource_get_user_data(resource);
528
529 compositor_view_destroy(popup->view);
530 free(popup);
531}
532
533static void
534grab(struct wl_client *client, struct wl_resource *resource,
535 struct wl_resource *seat, uint32_t serial)
536{
537}
538
539static const struct xdg_popup_interface popup_impl = {
540 .destroy = destroy_resource,
541 .grab = grab,
542};
543
544static struct xdg_popup *
545xdg_popup_new(struct wl_client *client, uint32_t version, uint32_t id,
546 struct xdg_surface *xdg_surface, struct xdg_surface *parent,
547 struct xdg_positioner *positioner)
548{
549 struct xdg_popup *popup;
550 struct compositor_view *parent_view =
551 compositor_view(parent->surface->view);
552 uint32_t serial = wl_display_next_serial(swc.display);
553 struct swc_rectangle rect;
554
555 if (!parent_view) {
556 goto error0;
557 }
558 popup = malloc(sizeof(*popup));
559 if (!popup) {
560 goto error0;
561 }
562 popup->xdg_surface = xdg_surface;
563 popup->positioner = *positioner;
564 popup->resource =
565 wl_resource_create(client, &xdg_popup_interface, version, id);
566 if (!popup->resource) {
567 goto error1;
568 }
569 if (!surface_set_role(xdg_surface->surface, popup->resource)) {
570 goto error2;
571 }
572 popup->view = compositor_create_view(xdg_surface->surface);
573 if (!popup->view) {
574 goto error3;
575 }
576 wl_resource_set_implementation(popup->resource, &popup_impl, popup,
577 &destroy_popup);
578
579 rect = calculate_position(positioner);
580 compositor_view_set_parent(popup->view, parent_view);
581 view_move(&popup->view->base, parent_view->base.geometry.x + rect.x,
582 parent_view->base.geometry.y + rect.y);
583 xdg_popup_send_configure(popup->resource, rect.x, rect.y, rect.width,
584 rect.height);
585 xdg_surface_send_configure(xdg_surface->resource, serial);
586
587 return popup;
588
589error3:
590 wl_resource_destroy(popup->resource);
591error2:
592 free(popup);
593error1:
594error0:
595 return NULL;
596}
597
598/* xdg_surface */
599static void
600get_toplevel(struct wl_client *client, struct wl_resource *resource,
601 uint32_t id)
602{
603 struct xdg_surface *xdg_surface = wl_resource_get_user_data(resource);
604 struct xdg_toplevel *toplevel;
605
606 if (xdg_surface->role) {
607 wl_resource_post_error(resource, XDG_WM_BASE_ERROR_ROLE,
608 "surface already has a role");
609 return;
610 }
611 if (xdg_surface->surface->role) {
612 wl_resource_post_error(resource, XDG_WM_BASE_ERROR_ROLE,
613 "surface already has a role");
614 return;
615 }
616 toplevel = xdg_toplevel_new(client, wl_resource_get_version(resource), id,
617 xdg_surface);
618 if (!toplevel) {
619 wl_client_post_no_memory(client);
620 return;
621 }
622 xdg_surface->role = toplevel->resource;
623 wl_resource_add_destroy_listener(xdg_surface->role,
624 &xdg_surface->role_destroy_listener);
625}
626
627static void
628get_popup(struct wl_client *client, struct wl_resource *resource, uint32_t id,
629 struct wl_resource *parent_resource,
630 struct wl_resource *positioner_resource)
631{
632 struct xdg_surface *xdg_surface = wl_resource_get_user_data(resource);
633 struct xdg_surface *parent = wl_resource_get_user_data(parent_resource);
634 struct xdg_positioner *positioner =
635 wl_resource_get_user_data(positioner_resource);
636 struct xdg_popup *popup;
637
638 if (xdg_surface->role) {
639 wl_resource_post_error(resource, XDG_WM_BASE_ERROR_ROLE,
640 "surface already has a role");
641 return;
642 }
643 if (xdg_surface->surface->role) {
644 wl_resource_post_error(resource, XDG_WM_BASE_ERROR_ROLE,
645 "surface already has a role");
646 return;
647 }
648 popup = xdg_popup_new(client, wl_resource_get_version(resource), id,
649 xdg_surface, parent, positioner);
650 if (!popup) {
651 wl_client_post_no_memory(client);
652 return;
653 }
654 xdg_surface->role = popup->resource;
655 wl_resource_add_destroy_listener(xdg_surface->role,
656 &xdg_surface->role_destroy_listener);
657}
658
659static void
660ack_configure(struct wl_client *client, struct wl_resource *resource,
661 uint32_t serial)
662{
663 struct xdg_surface *xdg_surface = wl_resource_get_user_data(resource);
664 struct window *window;
665
666 if (!xdg_surface->role) {
667 return;
668 }
669 window = wl_resource_get_user_data(xdg_surface->role);
670 if (window && serial == xdg_surface->configure_serial) {
671 window->configure.acknowledged = true;
672 }
673}
674
675static void
676set_window_geometry(struct wl_client *client, struct wl_resource *resource,
677 int32_t x, int32_t y, int32_t width, int32_t height)
678{
679 (void)client;
680 struct xdg_surface *xdg_surface = wl_resource_get_user_data(resource);
681 struct surface *surface = xdg_surface->surface;
682
683 if (width <= 0 || height <= 0) {
684 surface->has_window_geometry = false;
685 return;
686 }
687
688 surface->has_window_geometry = true;
689 surface->window_x = x;
690 surface->window_y = y;
691 surface->window_width = width;
692 surface->window_height = height;
693 if (!surface->window_geometry_applied && surface->view &&
694 (x != 0 || y != 0)) {
695 struct swc_rectangle *geom = &surface->view->geometry;
696 view_move(surface->view, geom->x - x, geom->y - y);
697 surface->window_geometry_applied = true;
698 }
699}
700
701static const struct xdg_surface_interface xdg_surface_impl = {
702 .destroy = destroy_resource,
703 .get_toplevel = get_toplevel,
704 .get_popup = get_popup,
705 .ack_configure = ack_configure,
706 .set_window_geometry = set_window_geometry,
707};
708
709static void
710handle_surface_destroy(struct wl_listener *listener, void *data)
711{
712 struct xdg_surface *xdg_surface =
713 wl_container_of(listener, xdg_surface, surface_destroy_listener);
714
715 wl_resource_destroy(xdg_surface->resource);
716}
717
718static void
719handle_role_destroy(struct wl_listener *listener, void *data)
720{
721 struct xdg_surface *xdg_surface =
722 wl_container_of(listener, xdg_surface, role_destroy_listener);
723
724 xdg_surface->role = NULL;
725}
726
727static void
728destroy_xdg_surface(struct wl_resource *resource)
729{
730 struct xdg_surface *xdg_surface = wl_resource_get_user_data(resource);
731
732 wl_list_remove(&xdg_surface->surface_destroy_listener.link);
733 if (xdg_surface->role) {
734 wl_resource_destroy(xdg_surface->role);
735 }
736 free(xdg_surface);
737}
738
739static struct xdg_surface *
740xdg_surface_new(struct wl_client *client, uint32_t version, uint32_t id,
741 struct surface *surface)
742{
743 struct xdg_surface *xdg_surface;
744
745 xdg_surface = malloc(sizeof(*xdg_surface));
746 if (!xdg_surface) {
747 goto error0;
748 }
749 xdg_surface->resource =
750 wl_resource_create(client, &xdg_surface_interface, version, id);
751 if (!xdg_surface->resource) {
752 goto error1;
753 }
754 xdg_surface->surface = surface;
755 xdg_surface->surface_destroy_listener.notify = &handle_surface_destroy;
756 xdg_surface->role = NULL;
757 xdg_surface->role_destroy_listener.notify = &handle_role_destroy;
758 wl_resource_add_destroy_listener(surface->resource,
759 &xdg_surface->surface_destroy_listener);
760 wl_resource_set_implementation(xdg_surface->resource, &xdg_surface_impl,
761 xdg_surface, destroy_xdg_surface);
762
763 return xdg_surface;
764
765error1:
766 free(xdg_surface);
767error0:
768 return NULL;
769}
770
771/* xdg_shell */
772static void
773create_positioner(struct wl_client *client, struct wl_resource *resource,
774 uint32_t id)
775{
776 struct xdg_positioner *positioner;
777 struct wl_resource *positioner_resource;
778 uint32_t version;
779
780 positioner = calloc(1, sizeof(*positioner));
781 if (!positioner) {
782 goto error0;
783 }
784
785 version = wl_resource_get_version(resource);
786 positioner_resource =
787 wl_resource_create(client, &xdg_positioner_interface, version, id);
788 if (!positioner_resource) {
789 goto error1;
790 }
791 wl_resource_set_implementation(positioner_resource, &positioner_impl,
792 positioner, &destroy_positioner);
793 return;
794
795error1:
796 free(positioner);
797error0:
798 wl_resource_post_no_memory(resource);
799}
800
801static void
802get_xdg_surface(struct wl_client *client, struct wl_resource *resource,
803 uint32_t id, struct wl_resource *surface_resource)
804{
805 struct xdg_surface *xdg_surface;
806 struct surface *surface = wl_resource_get_user_data(surface_resource);
807
808 xdg_surface =
809 xdg_surface_new(client, wl_resource_get_version(resource), id, surface);
810 if (!xdg_surface) {
811 wl_client_post_no_memory(client);
812 }
813}
814
815static void
816pong(struct wl_client *client, struct wl_resource *resource, uint32_t serial)
817{
818}
819
820static const struct xdg_wm_base_interface wm_base_impl = {
821 .destroy = destroy_resource,
822 .create_positioner = create_positioner,
823 .get_xdg_surface = get_xdg_surface,
824 .pong = pong,
825};
826
827static void
828bind_wm_base(struct wl_client *client, void *data, uint32_t version,
829 uint32_t id)
830{
831 struct wl_resource *resource;
832
833 resource = wl_resource_create(client, &xdg_wm_base_interface, version, id);
834 if (!resource) {
835 wl_client_post_no_memory(client);
836 return;
837 }
838 wl_resource_set_implementation(resource, &wm_base_impl, NULL, NULL);
839}
840
841struct wl_global *
842xdg_shell_create(struct wl_display *display)
843{
844 return wl_global_create(display, &xdg_wm_base_interface, 1, NULL,
845 &bind_wm_base);
846}