1/* swc: libswc/window.c
2 *
3 * Copyright (c) 2013 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 "window.h"
25#include "compositor.h"
26#include "event.h"
27#include "internal.h"
28#include "keyboard.h"
29#include "seat.h"
30#include "surface.h"
31#include "swc.h"
32#include "util.h"
33#include "view.h"
34
35#include <stdlib.h>
36#include <string.h>
37#include <sys/types.h>
38
39#define INTERNAL(w) ((struct window *)(w))
40
41static const uint32_t def_motion_throttle_ms = 16;
42
43static const struct swc_window_handler null_handler;
44
45static bool
46should_throttle_motion(uint32_t throttle_ms, uint32_t *last_time, uint32_t time)
47{
48 if (!throttle_ms) {
49 return false;
50 }
51
52 if (*last_time && time - *last_time < throttle_ms) {
53 return true;
54 }
55
56 *last_time = time;
57 return false;
58}
59
60static uint32_t
61clamp_dimension(int32_t value, uint32_t min, uint32_t max)
62{
63 if (value < 0) {
64 value = 0;
65 }
66
67 if (min && value < min) {
68 value = min;
69 }
70
71 if (max) {
72 if (min && max < min) {
73 max = min;
74 }
75
76 if (value > max) {
77 value = max;
78 }
79 }
80
81 if (value > UINT32_MAX) {
82 value = UINT32_MAX;
83 }
84
85 return value;
86}
87
88static void
89clamp_window_size(const struct window *window, uint32_t *width,
90 uint32_t *height)
91{
92 *width =
93 clamp_dimension(*width, window->base.min_width, window->base.max_width);
94 *height = clamp_dimension(*height, window->base.min_height,
95 window->base.max_height);
96}
97
98static void
99handle_window_enter(struct wl_listener *listener, void *data)
100{
101 struct event *event = data;
102 struct input_focus_event_data *event_data = event->data;
103 struct window *window;
104
105 if (event->type != INPUT_FOCUS_EVENT_CHANGED) {
106 return;
107 }
108
109 if (!event_data->new || !(window = event_data->new->window)) {
110 return;
111 }
112
113 if (window->handler->entered) {
114 window->handler->entered(window->handler_data);
115 }
116}
117
118struct wl_listener window_enter_listener = {
119 .notify = handle_window_enter,
120};
121
122static void
123begin_interaction(struct window_pointer_interaction *interaction,
124 struct button *button)
125{
126 if (button) {
127 /* Store the serial of the button press so we are able to cancel the
128 * interaction if the window changes from stacked mode. */
129 interaction->serial = button->press.serial;
130 interaction->original_handler = button->handler;
131 button->handler = &interaction->handler;
132 } else {
133 interaction->original_handler = NULL;
134 }
135
136 interaction->active = true;
137 wl_list_insert(&swc.seat->pointer->handlers, &interaction->handler.link);
138}
139
140static void
141end_interaction(struct window_pointer_interaction *interaction,
142 struct button *button)
143{
144 if (!interaction->active) {
145 return;
146 }
147
148 if (interaction->original_handler) {
149 if (!button) {
150 button = pointer_get_button(swc.seat->pointer, interaction->serial);
151
152 if (!button) {
153 WARNING("No button with serial %u\n", interaction->serial);
154 goto remove;
155 }
156 }
157
158 interaction->original_handler->button(interaction->original_handler,
159 get_time(), button,
160 WL_POINTER_BUTTON_STATE_RELEASED);
161 }
162
163remove:
164 interaction->active = false;
165 wl_list_remove(&interaction->handler.link);
166}
167
168static void
169flush(struct window *window)
170{
171 if (window->move.pending) {
172 if (window->impl->move) {
173 window->impl->move(window, window->move.x, window->move.y);
174 }
175
176 view_move(&window->view->base, window->move.x, window->move.y);
177 window->move.pending = false;
178 }
179}
180
181EXPORT void
182swc_window_set_handler(struct swc_window *base,
183 const struct swc_window_handler *handler, void *data)
184{
185 struct window *window = INTERNAL(base);
186
187 window->handler = handler;
188 window->handler_data = data;
189}
190
191EXPORT void
192swc_window_close(struct swc_window *base)
193{
194 struct window *window = INTERNAL(base);
195
196 if (window->impl->close) {
197 window->impl->close(window);
198 }
199}
200
201EXPORT void
202swc_window_show(struct swc_window *window)
203{
204 compositor_view_show(INTERNAL(window)->view);
205}
206
207EXPORT void
208swc_window_hide(struct swc_window *window)
209{
210 compositor_view_hide(INTERNAL(window)->view);
211}
212
213EXPORT void
214swc_window_focus(struct swc_window *base)
215{
216 struct window *window = INTERNAL(base);
217 struct compositor_view *new = window ? window->view : NULL,
218 *old = swc.seat->keyboard->focus.view;
219
220 if (new == old) {
221 return;
222 }
223
224 /* Focus the new window before unfocusing the old one in case both are X11
225 * windows so the xwl_window implementation can handle this transition
226 * correctly. */
227 if (window && window->impl->focus) {
228 window->impl->focus(window);
229 }
230 if (old && old->window && old->window->impl->unfocus) {
231 old->window->impl->unfocus(old->window);
232 }
233
234 keyboard_set_focus(swc.seat->keyboard, new);
235}
236
237EXPORT void
238swc_window_set_stacked(struct swc_window *base)
239{
240 struct window *window = INTERNAL(base);
241
242 flush(window);
243 window->configure.pending = false;
244 window->configure.width = 0;
245 window->configure.height = 0;
246 if (window->impl->set_mode) {
247 window->impl->set_mode(window, WINDOW_MODE_STACKED);
248 }
249 window->mode = WINDOW_MODE_STACKED;
250}
251
252EXPORT void
253swc_window_set_tiled(struct swc_window *base)
254{
255 struct window *window = INTERNAL(base);
256
257 end_interaction(&window->move.interaction, NULL);
258 end_interaction(&window->resize.interaction, NULL);
259 if (window->impl->set_mode) {
260 window->impl->set_mode(window, WINDOW_MODE_TILED);
261 }
262 window->mode = WINDOW_MODE_TILED;
263}
264
265EXPORT void
266swc_window_set_fullscreen(struct swc_window *base, struct swc_screen *screen)
267{
268 struct window *window = INTERNAL(base);
269
270 struct swc_rectangle geom;
271 swc_window_get_geometry(base, &geom);
272
273 if (window->mode != WINDOW_MODE_FULLSCREEN) {
274 window->prev.geom = geom;
275 window->prev.mode = window->mode;
276 swc_window_set_geometry(base, &screen->usable_geometry);
277
278 if (window->impl->set_mode) {
279 window->impl->set_mode(window, WINDOW_MODE_FULLSCREEN);
280 }
281 window->mode = WINDOW_MODE_FULLSCREEN;
282 }
283
284 else {
285 swc_window_set_geometry(base, &window->prev.geom);
286 window->mode = window->prev.mode;
287 }
288}
289
290EXPORT void
291swc_window_set_position(struct swc_window *base, int32_t x, int32_t y)
292{
293 struct window *window = INTERNAL(base);
294 struct swc_rectangle *geometry = &window->view->base.geometry;
295
296 if (x == geometry->x && y == geometry->y) {
297 window->move.pending = false;
298 return;
299 }
300
301 window->move.x = x;
302 window->move.y = y;
303 window->move.pending = true;
304
305 /* If we don't have a configure pending, perform the move now. */
306 if (!window->configure.pending) {
307 flush(window);
308 }
309}
310
311EXPORT void
312swc_window_set_size(struct swc_window *base, uint32_t width, uint32_t height)
313{
314 struct window *window = INTERNAL(base);
315 struct swc_rectangle *geom = &window->view->base.geometry;
316
317 clamp_window_size(window, &width, &height);
318
319 if ((window->configure.pending && width == window->configure.width &&
320 height == window->configure.height) ||
321 (!window->configure.pending && width == geom->width &&
322 height == geom->height)) {
323 return;
324 }
325
326 window->impl->configure(window, width, height);
327
328 if (window->mode == WINDOW_MODE_TILED) {
329 window->configure.width = width;
330 window->configure.height = height;
331 window->configure.pending = true;
332 }
333}
334
335EXPORT void
336swc_window_set_geometry(struct swc_window *window,
337 const struct swc_rectangle *geometry)
338{
339 swc_window_set_size(window, geometry->width, geometry->height);
340 swc_window_set_position(window, geometry->x, geometry->y);
341}
342
343EXPORT bool
344swc_window_get_geometry(const struct swc_window *base,
345 struct swc_rectangle *geometry)
346{
347 struct window *window = INTERNAL((struct swc_window *)base);
348
349 if (!window || !geometry) {
350 return false;
351 }
352
353 *geometry = window->view->base.geometry;
354 return true;
355}
356
357EXPORT void
358swc_window_set_border(struct swc_window *window, uint32_t inner_border_color,
359 uint32_t inner_border_width, uint32_t outer_border_color,
360 uint32_t outer_border_width)
361{
362 struct compositor_view *view = INTERNAL(window)->view;
363
364 compositor_view_set_border_color(view, outer_border_color,
365 inner_border_color);
366 compositor_view_set_border_width(view, outer_border_width,
367 inner_border_width);
368}
369
370EXPORT void
371swc_window_set_decor(struct swc_window *window, const struct swc_decor *decor)
372{
373 struct compositor_view *view = INTERNAL(window)->view;
374
375 compositor_view_set_decor(view, decor);
376}
377
378EXPORT void
379swc_window_begin_move(struct swc_window *window)
380{
381 window_begin_move(INTERNAL(window), NULL);
382}
383
384EXPORT void
385swc_window_end_move(struct swc_window *window)
386{
387 end_interaction(&INTERNAL(window)->move.interaction, NULL);
388}
389
390EXPORT void
391swc_window_begin_resize(struct swc_window *window, uint32_t edges)
392{
393 window_begin_resize(INTERNAL(window), edges, NULL);
394}
395
396EXPORT void
397swc_window_end_resize(struct swc_window *window)
398{
399 end_interaction(&INTERNAL(window)->resize.interaction, NULL);
400}
401
402static bool
403move_motion(struct pointer_handler *handler, uint32_t time, wl_fixed_t fx,
404 wl_fixed_t fy)
405{
406 struct window *window =
407 wl_container_of(handler, window, move.interaction.handler);
408
409 if (should_throttle_motion(window->base.motion_throttle_ms,
410 &window->move.last_time, time)) {
411 return true;
412 }
413
414 int32_t x = wl_fixed_to_int(fx) + window->move.offset.x,
415 y = wl_fixed_to_int(fy) + window->move.offset.y;
416
417 view_move(&window->view->base, x, y);
418 return true;
419}
420
421static bool
422resize_motion(struct pointer_handler *handler, uint32_t time, wl_fixed_t fx,
423 wl_fixed_t fy)
424{
425 struct window *window =
426 wl_container_of(handler, window, resize.interaction.handler);
427 const struct swc_rectangle *geometry = &window->view->base.geometry;
428 uint32_t width = geometry->width, height = geometry->height;
429
430 if (should_throttle_motion(window->base.motion_throttle_ms,
431 &window->resize.last_time, time)) {
432 return true;
433 }
434
435 if (window->resize.edges & SWC_WINDOW_EDGE_LEFT) {
436 width -= wl_fixed_to_int(fx) + window->resize.offset.x - geometry->x;
437 } else if (window->resize.edges & SWC_WINDOW_EDGE_RIGHT) {
438 width = wl_fixed_to_int(fx) + window->resize.offset.x - geometry->x;
439 }
440
441 if (window->resize.edges & SWC_WINDOW_EDGE_TOP) {
442 height -= wl_fixed_to_int(fy) + window->resize.offset.y - geometry->y;
443 } else if (window->resize.edges & SWC_WINDOW_EDGE_BOTTOM) {
444 height = wl_fixed_to_int(fy) + window->resize.offset.y - geometry->y;
445 }
446
447 clamp_window_size(window, &width, &height);
448 window->impl->configure(window, width, height);
449
450 return true;
451}
452
453static bool
454handle_button(struct pointer_handler *handler, uint32_t time,
455 struct button *button, uint32_t state)
456{
457 struct window_pointer_interaction *interaction =
458 wl_container_of(handler, interaction, handler);
459
460 if (state != WL_POINTER_BUTTON_STATE_RELEASED ||
461 !interaction->original_handler) {
462 return false;
463 }
464
465 end_interaction(interaction, button);
466 return true;
467}
468
469static void
470handle_attach(struct view_handler *handler)
471{
472 struct window *window = wl_container_of(handler, window, view_handler);
473
474 if (window->configure.acknowledged) {
475 flush(window);
476 }
477 window->configure.pending = false;
478}
479
480static void
481handle_resize(struct view_handler *handler, uint32_t old_width,
482 uint32_t old_height)
483{
484 struct window *window = wl_container_of(handler, window, view_handler);
485
486 if (window->resize.interaction.active &&
487 window->resize.edges & (SWC_WINDOW_EDGE_TOP | SWC_WINDOW_EDGE_LEFT)) {
488 const struct swc_rectangle *geometry = &window->view->base.geometry;
489 int32_t x = geometry->x, y = geometry->y;
490
491 if (window->resize.edges & SWC_WINDOW_EDGE_LEFT) {
492 x += old_width - geometry->width;
493 }
494 if (window->resize.edges & SWC_WINDOW_EDGE_TOP) {
495 y += old_height - geometry->height;
496 }
497
498 view_move(&window->view->base, x, y);
499 }
500}
501
502static const struct view_handler_impl view_handler_impl = {
503 .attach = handle_attach,
504 .resize = handle_resize,
505};
506
507bool
508window_initialize(struct window *window, const struct window_impl *impl,
509 struct surface *surface)
510{
511 DEBUG("Initializing window, %p\n", window);
512
513 window->base.title = NULL;
514 window->base.app_id = NULL;
515 window->base.parent = NULL;
516
517 if (surface->view) {
518 window->view = compositor_view(surface->view);
519 if (!window->view || window->view->window) {
520 return false;
521 }
522 } else {
523 if (!(window->view = compositor_create_view(surface))) {
524 return false;
525 }
526 }
527
528 window->impl = impl;
529 window->handler = &null_handler;
530 window->view_handler.impl = &view_handler_impl;
531 window->view->window = window;
532 window->base.motion_throttle_ms = def_motion_throttle_ms;
533 window->base.min_width = 0;
534 window->base.min_height = 0;
535 window->base.max_width = 0;
536 window->base.max_height = 0;
537 window->managed = false;
538 window->mode = WINDOW_MODE_STACKED;
539 window->move.pending = false;
540 window->move.last_time = 0;
541 window->move.interaction.active = false;
542 window->move.interaction.handler = (struct pointer_handler){
543 .motion = move_motion,
544 .button = handle_button,
545 };
546 window->configure.pending = false;
547 window->configure.width = 0;
548 window->configure.height = 0;
549 window->resize.interaction.active = false;
550 window->resize.interaction.handler = (struct pointer_handler){
551 .motion = resize_motion,
552 .button = handle_button,
553 };
554 window->resize.last_time = 0;
555
556 wl_list_insert(&window->view->base.handlers, &window->view_handler.link);
557
558 return true;
559}
560
561void
562window_finalize(struct window *window)
563{
564 DEBUG("Finalizing window, %p\n", window);
565
566 window_unmanage(window);
567 compositor_view_destroy(window->view);
568 free(window->base.title);
569 free(window->base.app_id);
570}
571
572void
573window_manage(struct window *window)
574{
575 if (window->managed) {
576 return;
577 }
578
579 swc.manager->new_window(&window->base);
580 window->managed = true;
581}
582
583void
584window_unmanage(struct window *window)
585{
586 if (!window->managed) {
587 return;
588 }
589
590 if (window->handler->destroy) {
591 window->handler->destroy(window->handler_data);
592 }
593 window->handler = &null_handler;
594 window->managed = false;
595}
596
597void
598window_set_title(struct window *window, const char *title, size_t length)
599{
600 free(window->base.title);
601 window->base.title = strndup(title, length);
602
603 if (window->handler->title_changed) {
604 window->handler->title_changed(window->handler_data);
605 }
606}
607
608void
609window_set_app_id(struct window *window, const char *app_id)
610{
611 free(window->base.app_id);
612 window->base.app_id = strdup(app_id);
613
614 if (window->handler->app_id_changed) {
615 window->handler->app_id_changed(window->handler_data);
616 }
617}
618
619void
620window_set_parent(struct window *window, struct window *parent)
621{
622 if (window->base.parent == &parent->base) {
623 return;
624 }
625
626 compositor_view_set_parent(window->view, parent->view);
627 window->base.parent = &parent->base;
628
629 if (window->handler->parent_changed) {
630 window->handler->parent_changed(window->handler_data);
631 }
632}
633
634void
635window_begin_move(struct window *window, struct button *button)
636{
637 if (window->mode != WINDOW_MODE_STACKED && window->handler->move) {
638 window->handler->move(window->handler_data);
639 }
640
641 if (window->mode != WINDOW_MODE_STACKED ||
642 window->move.interaction.active) {
643 return;
644 }
645
646 struct swc_rectangle *geometry = &window->view->base.geometry;
647 int32_t px = wl_fixed_to_int(swc.seat->pointer->x),
648 py = wl_fixed_to_int(swc.seat->pointer->y);
649
650 begin_interaction(&window->move.interaction, button);
651 window->move.last_time = 0;
652 window->move.offset.x = geometry->x - px;
653 window->move.offset.y = geometry->y - py;
654}
655
656void
657window_begin_resize(struct window *window, uint32_t edges,
658 struct button *button)
659{
660 if (window->mode != WINDOW_MODE_STACKED && window->handler->resize) {
661 window->handler->resize(window->handler_data);
662 }
663
664 if (window->mode != WINDOW_MODE_STACKED ||
665 window->resize.interaction.active) {
666 return;
667 }
668
669 struct swc_rectangle *geometry = &window->view->base.geometry;
670 int32_t px = wl_fixed_to_int(swc.seat->pointer->x),
671 py = wl_fixed_to_int(swc.seat->pointer->y);
672
673 begin_interaction(&window->resize.interaction, button);
674 window->resize.last_time = 0;
675
676 if (!edges) {
677 edges |= (px < geometry->x + geometry->width / 2)
678 ? SWC_WINDOW_EDGE_LEFT
679 : SWC_WINDOW_EDGE_RIGHT;
680 edges |= (py < geometry->y + geometry->height / 2)
681 ? SWC_WINDOW_EDGE_TOP
682 : SWC_WINDOW_EDGE_BOTTOM;
683 }
684
685 window->resize.offset.x =
686 geometry->x - px +
687 ((edges & SWC_WINDOW_EDGE_RIGHT) ? geometry->width : 0);
688 window->resize.offset.y =
689 geometry->y - py +
690 ((edges & SWC_WINDOW_EDGE_BOTTOM) ? geometry->height : 0);
691 window->resize.edges = edges;
692}
693
694EXPORT pid_t
695swc_window_get_pid(struct swc_window *base)
696{
697 struct window *window = INTERNAL(base);
698 struct surface *surface;
699 struct wl_client *client;
700 pid_t pid;
701 uid_t uid;
702 gid_t gid;
703
704 if (!window || !window->view || !window->view->surface) {
705 return 0;
706 }
707
708 surface = window->view->surface;
709 if (!surface->resource) {
710 return 0;
711 }
712
713 client = wl_resource_get_client(surface->resource);
714 wl_client_get_credentials(client, &pid, &uid, &gid);
715
716 return pid;
717}