main neuswc / libswc / window.c
  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}