summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/pango.c9
-rw-r--r--include/sway/config.h27
-rw-r--r--include/sway/input/input-manager.h23
-rw-r--r--include/sway/input/seat.h37
-rw-r--r--include/sway/output.h3
-rw-r--r--include/sway/tree/container.h76
-rw-r--r--include/sway/tree/layout.h26
-rw-r--r--include/sway/tree/view.h59
-rw-r--r--include/swaybar/ipc.h2
-rw-r--r--include/swaybar/status_line.h53
-rw-r--r--protocols/desktop-shell.xml138
-rw-r--r--protocols/gamma-control.xml57
-rw-r--r--protocols/server-decoration.xml94
-rw-r--r--protocols/swaylock.xml18
-rw-r--r--protocols/xdg-shell.xml430
-rw-r--r--sway/commands.c25
-rw-r--r--sway/commands/focus.c11
-rw-r--r--sway/commands/kill.c27
-rw-r--r--sway/commands/move.c184
-rw-r--r--sway/commands/seat.c11
-rw-r--r--sway/commands/split.c76
-rw-r--r--sway/commands/workspace.c2
-rw-r--r--sway/config.c2
-rw-r--r--sway/desktop/layer_shell.c27
-rw-r--r--sway/desktop/output.c100
-rw-r--r--sway/desktop/wl_shell.c61
-rw-r--r--sway/desktop/xdg_shell_v6.c67
-rw-r--r--sway/desktop/xwayland.c136
-rw-r--r--sway/input/cursor.c32
-rw-r--r--sway/input/input-manager.c144
-rw-r--r--sway/input/keyboard.c6
-rw-r--r--sway/input/seat.c251
-rw-r--r--sway/ipc-json.c8
-rw-r--r--sway/ipc-server.c6
-rw-r--r--sway/main.c2
-rw-r--r--sway/meson.build2
-rw-r--r--sway/server.c2
-rw-r--r--sway/tree/container.c56
-rw-r--r--sway/tree/layout.c181
-rw-r--r--sway/tree/output.c12
-rw-r--r--sway/tree/view.c188
-rw-r--r--sway/tree/workspace.c16
-rw-r--r--swaybar/bar.c4
-rw-r--r--swaybar/i3bar.c211
-rw-r--r--swaybar/ipc.c2
-rw-r--r--swaybar/meson.build1
-rw-r--r--swaybar/render.c238
-rw-r--r--swaybar/status_line.c69
48 files changed, 1854 insertions, 1358 deletions
diff --git a/common/pango.c b/common/pango.c
index 2ae7883c..658d2876 100644
--- a/common/pango.c
+++ b/common/pango.c
@@ -6,6 +6,7 @@
6#include <stdio.h> 6#include <stdio.h>
7#include <stdlib.h> 7#include <stdlib.h>
8#include <string.h> 8#include <string.h>
9#include "log.h"
9 10
10PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, 11PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
11 const char *text, int32_t scale, bool markup) { 12 const char *text, int32_t scale, bool markup) {
@@ -13,7 +14,13 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
13 PangoAttrList *attrs; 14 PangoAttrList *attrs;
14 if (markup) { 15 if (markup) {
15 char *buf; 16 char *buf;
16 pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL); 17 GError *error = NULL;
18 if (!sway_assert(pango_parse_markup(
19 text, -1, 0, &attrs, &buf, NULL, &error),
20 "pango_parse_markup '%s' -> error %s", text,
21 error ? error->message : NULL)) {
22 return NULL;
23 }
17 pango_layout_set_markup(layout, buf, -1); 24 pango_layout_set_markup(layout, buf, -1);
18 free(buf); 25 free(buf);
19 } else { 26 } else {
diff --git a/include/sway/config.h b/include/sway/config.h
index 03b51948..91f772b5 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -377,6 +377,7 @@ void free_config(struct sway_config *config);
377void config_clear_handler_context(struct sway_config *config); 377void config_clear_handler_context(struct sway_config *config);
378 378
379void free_sway_variable(struct sway_variable *var); 379void free_sway_variable(struct sway_variable *var);
380
380/** 381/**
381 * Does variable replacement for a string based on the config's currently loaded variables. 382 * Does variable replacement for a string based on the config's currently loaded variables.
382 */ 383 */
@@ -385,44 +386,68 @@ char *do_var_replacement(char *str);
385struct cmd_results *check_security_config(); 386struct cmd_results *check_security_config();
386 387
387int input_identifier_cmp(const void *item, const void *data); 388int input_identifier_cmp(const void *item, const void *data);
389
388struct input_config *new_input_config(const char* identifier); 390struct input_config *new_input_config(const char* identifier);
391
389void merge_input_config(struct input_config *dst, struct input_config *src); 392void merge_input_config(struct input_config *dst, struct input_config *src);
393
390struct input_config *copy_input_config(struct input_config *ic); 394struct input_config *copy_input_config(struct input_config *ic);
395
391void free_input_config(struct input_config *ic); 396void free_input_config(struct input_config *ic);
397
392void apply_input_config(struct input_config *input); 398void apply_input_config(struct input_config *input);
393 399
394int seat_name_cmp(const void *item, const void *data); 400int seat_name_cmp(const void *item, const void *data);
401
395struct seat_config *new_seat_config(const char* name); 402struct seat_config *new_seat_config(const char* name);
403
396void merge_seat_config(struct seat_config *dst, struct seat_config *src); 404void merge_seat_config(struct seat_config *dst, struct seat_config *src);
405
397struct seat_config *copy_seat_config(struct seat_config *seat); 406struct seat_config *copy_seat_config(struct seat_config *seat);
407
398void free_seat_config(struct seat_config *ic); 408void free_seat_config(struct seat_config *ic);
409
399struct seat_attachment_config *seat_attachment_config_new(); 410struct seat_attachment_config *seat_attachment_config_new();
411
400struct seat_attachment_config *seat_config_get_attachment( 412struct seat_attachment_config *seat_config_get_attachment(
401 struct seat_config *seat_config, char *identifier); 413 struct seat_config *seat_config, char *identifier);
414
402void apply_seat_config(struct seat_config *seat); 415void apply_seat_config(struct seat_config *seat);
403 416
404int output_name_cmp(const void *item, const void *data); 417int output_name_cmp(const void *item, const void *data);
418
405void output_get_identifier(char *identifier, size_t len, 419void output_get_identifier(char *identifier, size_t len,
406 struct sway_output *output); 420 struct sway_output *output);
421
407struct output_config *new_output_config(const char *name); 422struct output_config *new_output_config(const char *name);
423
408void merge_output_config(struct output_config *dst, struct output_config *src); 424void merge_output_config(struct output_config *dst, struct output_config *src);
425
409void apply_output_config(struct output_config *oc, 426void apply_output_config(struct output_config *oc,
410 struct sway_container *output); 427 struct sway_container *output);
428
411void free_output_config(struct output_config *oc); 429void free_output_config(struct output_config *oc);
412 430
413int workspace_output_cmp_workspace(const void *a, const void *b); 431int workspace_output_cmp_workspace(const void *a, const void *b);
414 432
415int sway_binding_cmp(const void *a, const void *b); 433int sway_binding_cmp(const void *a, const void *b);
434
416int sway_binding_cmp_qsort(const void *a, const void *b); 435int sway_binding_cmp_qsort(const void *a, const void *b);
436
417int sway_binding_cmp_keys(const void *a, const void *b); 437int sway_binding_cmp_keys(const void *a, const void *b);
438
418void free_sway_binding(struct sway_binding *sb); 439void free_sway_binding(struct sway_binding *sb);
440
419struct sway_binding *sway_binding_dup(struct sway_binding *sb); 441struct sway_binding *sway_binding_dup(struct sway_binding *sb);
420 442
421/* Bar stuff */
422void load_swaybars(); 443void load_swaybars();
444
423void invoke_swaybar(struct bar_config *bar); 445void invoke_swaybar(struct bar_config *bar);
446
424void terminate_swaybg(pid_t pid); 447void terminate_swaybg(pid_t pid);
448
425struct bar_config *default_bar_config(void); 449struct bar_config *default_bar_config(void);
450
426void free_bar_config(struct bar_config *bar); 451void free_bar_config(struct bar_config *bar);
427 452
428/* Global config singleton. */ 453/* Global config singleton. */
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index c6c73dba..8e39a4a7 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -14,7 +14,6 @@ extern struct sway_input_manager *input_manager;
14struct sway_input_device { 14struct sway_input_device {
15 char *identifier; 15 char *identifier;
16 struct wlr_input_device *wlr_device; 16 struct wlr_input_device *wlr_device;
17 struct input_config *config;
18 struct wl_list link; 17 struct wl_list link;
19 struct wl_listener device_destroy; 18 struct wl_listener device_destroy;
20}; 19};
@@ -27,30 +26,34 @@ struct sway_input_manager {
27 struct wl_listener new_input; 26 struct wl_listener new_input;
28}; 27};
29 28
30struct sway_input_manager *sway_input_manager_create( 29struct sway_input_manager *input_manager_create(struct sway_server *server);
31 struct sway_server *server);
32 30
33bool sway_input_manager_has_focus(struct sway_input_manager *input, 31bool input_manager_has_focus(struct sway_input_manager *input,
34 struct sway_container *container); 32 struct sway_container *container);
35 33
36void sway_input_manager_set_focus(struct sway_input_manager *input, 34void input_manager_set_focus(struct sway_input_manager *input,
37 struct sway_container *container); 35 struct sway_container *container);
38 36
39void sway_input_manager_configure_xcursor(struct sway_input_manager *input); 37void input_manager_configure_xcursor(struct sway_input_manager *input);
40 38
41void sway_input_manager_apply_input_config(struct sway_input_manager *input, 39void input_manager_apply_input_config(struct sway_input_manager *input,
42 struct input_config *input_config); 40 struct input_config *input_config);
43 41
44void sway_input_manager_apply_seat_config(struct sway_input_manager *input, 42void input_manager_apply_seat_config(struct sway_input_manager *input,
45 struct seat_config *seat_config); 43 struct seat_config *seat_config);
46 44
47struct sway_seat *sway_input_manager_get_default_seat( 45struct sway_seat *input_manager_get_default_seat(
48 struct sway_input_manager *input); 46 struct sway_input_manager *input);
49 47
50struct sway_seat *input_manager_get_seat(struct sway_input_manager *input, 48struct sway_seat *input_manager_get_seat(struct sway_input_manager *input,
51 const char *seat_name); 49 const char *seat_name);
52 50
53/** Gets the last seat the user interacted with */ 51/**
52 * Gets the last seat the user interacted with
53 */
54struct sway_seat *input_manager_current_seat(struct sway_input_manager *input); 54struct sway_seat *input_manager_current_seat(struct sway_input_manager *input);
55 55
56struct input_config *input_device_get_config(struct sway_input_device *device);
57
58
56#endif 59#endif
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 31210a5a..137fcd22 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -1,6 +1,7 @@
1#ifndef _SWAY_INPUT_SEAT_H 1#ifndef _SWAY_INPUT_SEAT_H
2#define _SWAY_INPUT_SEAT_H 2#define _SWAY_INPUT_SEAT_H
3 3
4#include <wlr/types/wlr_layer_shell.h>
4#include <wlr/types/wlr_seat.h> 5#include <wlr/types/wlr_seat.h>
5#include "sway/input/input-manager.h" 6#include "sway/input/input-manager.h"
6 7
@@ -8,7 +9,6 @@ struct sway_seat_device {
8 struct sway_seat *sway_seat; 9 struct sway_seat *sway_seat;
9 struct sway_input_device *input_device; 10 struct sway_input_device *input_device;
10 struct sway_keyboard *keyboard; 11 struct sway_keyboard *keyboard;
11 struct seat_attachment_config *attachment_config;
12 struct wl_list link; // sway_seat::devices 12 struct wl_list link; // sway_seat::devices
13}; 13};
14 14
@@ -23,13 +23,15 @@ struct sway_seat_container {
23 23
24struct sway_seat { 24struct sway_seat {
25 struct wlr_seat *wlr_seat; 25 struct wlr_seat *wlr_seat;
26 struct seat_config *config;
27 struct sway_cursor *cursor; 26 struct sway_cursor *cursor;
28 struct sway_input_manager *input; 27 struct sway_input_manager *input;
29 28
30 bool has_focus; 29 bool has_focus;
31 struct wl_list focus_stack; // list of containers in focus order 30 struct wl_list focus_stack; // list of containers in focus order
32 31
32 // If the focused layer is set, views cannot receive keyboard focus
33 struct wlr_layer_surface *focused_layer;
34
33 struct wl_listener focus_destroy; 35 struct wl_listener focus_destroy;
34 struct wl_listener new_container; 36 struct wl_listener new_container;
35 37
@@ -38,28 +40,31 @@ struct sway_seat {
38 struct wl_list link; // input_manager::seats 40 struct wl_list link; // input_manager::seats
39}; 41};
40 42
41struct sway_seat *sway_seat_create(struct sway_input_manager *input, 43struct sway_seat *seat_create(struct sway_input_manager *input,
42 const char *seat_name); 44 const char *seat_name);
43 45
44void sway_seat_destroy(struct sway_seat *seat); 46void seat_destroy(struct sway_seat *seat);
45 47
46void sway_seat_add_device(struct sway_seat *seat, 48void seat_add_device(struct sway_seat *seat,
47 struct sway_input_device *device); 49 struct sway_input_device *device);
48 50
49void sway_seat_configure_device(struct sway_seat *seat, 51void seat_configure_device(struct sway_seat *seat,
50 struct sway_input_device *device); 52 struct sway_input_device *device);
51 53
52void sway_seat_remove_device(struct sway_seat *seat, 54void seat_remove_device(struct sway_seat *seat,
53 struct sway_input_device *device); 55 struct sway_input_device *device);
54 56
55void sway_seat_configure_xcursor(struct sway_seat *seat); 57void seat_configure_xcursor(struct sway_seat *seat);
56 58
57void sway_seat_set_focus(struct sway_seat *seat, struct sway_container *container); 59void seat_set_focus(struct sway_seat *seat, struct sway_container *container);
58 60
59void sway_seat_set_focus_warp(struct sway_seat *seat, 61void seat_set_focus_warp(struct sway_seat *seat,
60 struct sway_container *container, bool warp); 62 struct sway_container *container, bool warp);
61 63
62struct sway_container *sway_seat_get_focus(struct sway_seat *seat); 64void seat_set_focus_layer(struct sway_seat *seat,
65 struct wlr_layer_surface *layer);
66
67struct sway_container *seat_get_focus(struct sway_seat *seat);
63 68
64/** 69/**
65 * Return the last container to be focused for the seat (or the most recently 70 * Return the last container to be focused for the seat (or the most recently
@@ -70,12 +75,14 @@ struct sway_container *sway_seat_get_focus(struct sway_seat *seat);
70 * is destroyed, or focus moves to a container with children and we need to 75 * is destroyed, or focus moves to a container with children and we need to
71 * descend into the next leaf in focus order. 76 * descend into the next leaf in focus order.
72 */ 77 */
73struct sway_container *sway_seat_get_focus_inactive(struct sway_seat *seat, 78struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
74 struct sway_container *container); 79 struct sway_container *container);
75 80
76struct sway_container *sway_seat_get_focus_by_type(struct sway_seat *seat, 81struct sway_container *seat_get_focus_by_type(struct sway_seat *seat,
77 enum sway_container_type type); 82 struct sway_container *container, enum sway_container_type type);
83
84void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config);
78 85
79void sway_seat_set_config(struct sway_seat *seat, struct seat_config *seat_config); 86struct seat_config *seat_get_config(struct sway_seat *seat);
80 87
81#endif 88#endif
diff --git a/include/sway/output.h b/include/sway/output.h
index b4980cd8..98d0f83f 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -24,6 +24,7 @@ struct sway_output {
24 struct wl_listener destroy; 24 struct wl_listener destroy;
25 struct wl_listener mode; 25 struct wl_listener mode;
26 struct wl_listener transform; 26 struct wl_listener transform;
27 struct wl_listener scale;
27 28
28 struct wl_listener damage_destroy; 29 struct wl_listener damage_destroy;
29 struct wl_listener damage_frame; 30 struct wl_listener damage_frame;
@@ -36,4 +37,6 @@ void output_damage_whole(struct sway_output *output);
36void output_damage_whole_view(struct sway_output *output, 37void output_damage_whole_view(struct sway_output *output,
37 struct sway_view *view); 38 struct sway_view *view);
38 39
40struct sway_container *output_by_name(const char *name);
41
39#endif 42#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 6aa66da0..464f80c4 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -70,9 +70,10 @@ struct sway_container {
70 enum sway_container_layout prev_layout; 70 enum sway_container_layout prev_layout;
71 enum sway_container_layout workspace_layout; 71 enum sway_container_layout workspace_layout;
72 72
73 // TODO convert to layout coordinates 73 // For C_ROOT, this has no meaning
74 // For C_OUTPUT, this is the output position in layout coordinates
75 // For other types, this is the position in output-local coordinates
74 double x, y; 76 double x, y;
75
76 // does not include borders or gaps. 77 // does not include borders or gaps.
77 double width, height; 78 double width, height;
78 79
@@ -84,38 +85,67 @@ struct sway_container {
84 85
85 struct { 86 struct {
86 struct wl_signal destroy; 87 struct wl_signal destroy;
88 // Raised after the tree updates, but before arrange_windows
89 // Passed the previous parent
90 struct wl_signal reparent;
87 } events; 91 } events;
88}; 92};
89 93
94// TODO make private and use the container-specific create functions
95struct sway_container *container_create(enum sway_container_type type);
96
97const char *container_type_to_str(enum sway_container_type type);
98
90// TODO only one container create function and pass the type? 99// TODO only one container create function and pass the type?
91struct sway_container *container_output_create( 100struct sway_container *container_output_create(
92 struct sway_output *sway_output); 101 struct sway_output *sway_output);
93 102
94struct sway_container *container_workspace_create( 103/**
95 struct sway_container *output, const char *name); 104 * Create a new container container. A container container can be a a child of
105 * a workspace container or another container container.
106 */
107struct sway_container *container_container_create();
96 108
97struct sway_container *container_view_create( 109/**
98 struct sway_container *sibling, struct sway_view *sway_view); 110 * Create a new output. Outputs are children of the root container and have no
111 * order in the tree structure.
112 */
113struct sway_container *container_output_create(struct sway_output *sway_output);
99 114
100struct sway_container *container_output_destroy(struct sway_container *output); 115/**
116 * Create a new workspace container. Workspaces are children of an output
117 * container and are ordered alphabetically by name.
118 */
119struct sway_container *container_workspace_create(struct sway_container *output, const char *name);
101 120
102struct sway_container *container_workspace_destroy( 121/*
103 struct sway_container *workspace); 122 * Create a new view container. A view can be a child of a workspace container
123 * or a container container and are rendered in the order and structure of
124 * how they are attached to the tree.
125 */
126// TODO view containers should be created in a detached state.
127struct sway_container *container_view_create(
128 struct sway_container *sibling, struct sway_view *sway_view);
104 129
105struct sway_container *container_view_destroy(struct sway_container *view); 130// TODO don't return the parent on destroy
131struct sway_container *container_destroy(struct sway_container *container);
106 132
107struct sway_container *container_destroy(struct sway_container *cont); 133struct sway_container *container_workspace_destroy(struct sway_container *container);
134struct sway_container *container_output_destroy(struct sway_container *container);
135struct sway_container *container_view_destroy(struct sway_container *container);
108 136
137// TODO move to layout.c
109struct sway_container *container_set_layout(struct sway_container *container, 138struct sway_container *container_set_layout(struct sway_container *container,
110 enum sway_container_layout layout); 139 enum sway_container_layout layout);
111 140
141// TODO rename to container_descendants_for_each()
112void container_descendants(struct sway_container *root, 142void container_descendants(struct sway_container *root,
113 enum sway_container_type type, 143 enum sway_container_type type,
114 void (*func)(struct sway_container *item, void *data), void *data); 144 void (*func)(struct sway_container *item, void *data), void *data);
115 145
116/** 146/**
117 * Finds a container based on test criteria. Returns the first container that 147 * Search a container's descendants a container based on test criteria. Returns
118 * passes the test. 148 * the first container that passes the test.
119 */ 149 */
120struct sway_container *container_find(struct sway_container *container, 150struct sway_container *container_find(struct sway_container *container,
121 bool (*test)(struct sway_container *view, void *data), void *data); 151 bool (*test)(struct sway_container *view, void *data), void *data);
@@ -123,18 +153,21 @@ struct sway_container *container_find(struct sway_container *container,
123/** 153/**
124 * Finds a parent container with the given struct sway_containerype. 154 * Finds a parent container with the given struct sway_containerype.
125 */ 155 */
156// TODO rename to container_parent_of_type()
126struct sway_container *container_parent(struct sway_container *container, 157struct sway_container *container_parent(struct sway_container *container,
127 enum sway_container_type type); 158 enum sway_container_type type);
128 159
129/** 160/**
130 * Find a container at the given coordinates. 161 * Find a container at the given coordinates. Returns the the surface and
162 * surface-local coordinates of the given layout coordinates if the container
163 * is a view and the view contains a surface at those coordinates.
131 */ 164 */
132struct sway_container *container_at(struct sway_container *parent, 165struct sway_container *container_at(struct sway_container *container,
133 double lx, double ly, struct wlr_surface **surface, 166 double lx, double ly, struct wlr_surface **surface,
134 double *sx, double *sy); 167 double *sx, double *sy);
135 168
136/** 169/**
137 * Apply the function for each child of the container breadth first. 170 * Apply the function for each descendant of the container breadth first.
138 */ 171 */
139void container_for_each_descendant_bfs(struct sway_container *container, 172void container_for_each_descendant_bfs(struct sway_container *container,
140 void (*f)(struct sway_container *container, void *data), void *data); 173 void (*f)(struct sway_container *container, void *data), void *data);
@@ -145,7 +178,16 @@ void container_for_each_descendant_bfs(struct sway_container *container,
145void container_for_each_descendant_dfs(struct sway_container *container, 178void container_for_each_descendant_dfs(struct sway_container *container,
146 void (*f)(struct sway_container *container, void *data), void *data); 179 void (*f)(struct sway_container *container, void *data), void *data);
147 180
148bool container_has_anscestor(struct sway_container *descendant, 181/**
182 * Returns true if the given container is an ancestor of this container.
183 */
184bool container_has_anscestor(struct sway_container *container,
149 struct sway_container *anscestor); 185 struct sway_container *anscestor);
150 186
187/**
188 * Returns true if the given container is a child descendant of this container.
189 */
190bool container_has_child(struct sway_container *con,
191 struct sway_container *child);
192
151#endif 193#endif
diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h
index 0a904c4b..8badb244 100644
--- a/include/sway/tree/layout.h
+++ b/include/sway/tree/layout.h
@@ -11,9 +11,6 @@ enum movement_direction {
11 MOVE_DOWN, 11 MOVE_DOWN,
12 MOVE_PARENT, 12 MOVE_PARENT,
13 MOVE_CHILD, 13 MOVE_CHILD,
14 MOVE_NEXT,
15 MOVE_PREV,
16 MOVE_FIRST
17}; 14};
18 15
19struct sway_container; 16struct sway_container;
@@ -23,7 +20,7 @@ struct sway_root {
23 20
24 struct wl_listener output_layout_change; 21 struct wl_listener output_layout_change;
25 22
26 struct wl_list unmanaged_views; // sway_view::unmanaged_view_link 23 struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link
27 24
28 struct { 25 struct {
29 struct wl_signal new_container; 26 struct wl_signal new_container;
@@ -32,26 +29,43 @@ struct sway_root {
32 29
33void layout_init(void); 30void layout_init(void);
34 31
35void container_add_child(struct sway_container *parent, struct sway_container *child); 32// TODO move to tree.h
33void container_add_child(struct sway_container *parent,
34 struct sway_container *child);
36 35
36// TODO move to tree.h
37struct sway_container *container_add_sibling(struct sway_container *parent, 37struct sway_container *container_add_sibling(struct sway_container *parent,
38 struct sway_container *child); 38 struct sway_container *child);
39 39
40// TODO move to tree.h
40struct sway_container *container_remove_child(struct sway_container *child); 41struct sway_container *container_remove_child(struct sway_container *child);
41 42
43// TODO PRIVATE in tree.h
42struct sway_container *container_reap_empty(struct sway_container *container); 44struct sway_container *container_reap_empty(struct sway_container *container);
43 45
46// TODO move to tree.h
44void container_move_to(struct sway_container* container, 47void container_move_to(struct sway_container* container,
45 struct sway_container* destination); 48 struct sway_container* destination);
46 49
47enum sway_container_layout container_get_default_layout(struct sway_container *output); 50void container_move(struct sway_container *container,
51 enum movement_direction dir, int move_amt);
52
53// TODO move to output.c
54enum sway_container_layout container_get_default_layout(
55 struct sway_container *output);
48 56
57// TODO move to output.c
49void container_sort_workspaces(struct sway_container *output); 58void container_sort_workspaces(struct sway_container *output);
50 59
51void arrange_windows(struct sway_container *container, 60void arrange_windows(struct sway_container *container,
52 double width, double height); 61 double width, double height);
53 62
63// TODO move to container.h
54struct sway_container *container_get_in_direction(struct sway_container 64struct sway_container *container_get_in_direction(struct sway_container
55 *container, struct sway_seat *seat, enum movement_direction dir); 65 *container, struct sway_seat *seat, enum movement_direction dir);
56 66
67// TODO move to tree.h
68struct sway_container *container_split(struct sway_container *child,
69 enum sway_container_layout layout);
70
57#endif 71#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 3965d2b7..4b84205e 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -4,6 +4,8 @@
4#include <wlr/types/wlr_surface.h> 4#include <wlr/types/wlr_surface.h>
5#include <wlr/types/wlr_xdg_shell_v6.h> 5#include <wlr/types/wlr_xdg_shell_v6.h>
6#include <wlr/xwayland.h> 6#include <wlr/xwayland.h>
7#include "sway/input/input-manager.h"
8#include "sway/input/seat.h"
7 9
8struct sway_container; 10struct sway_container;
9struct sway_view; 11struct sway_view;
@@ -37,6 +39,13 @@ struct sway_xwayland_surface {
37 int pending_width, pending_height; 39 int pending_width, pending_height;
38}; 40};
39 41
42struct sway_xwayland_unmanaged {
43 struct wlr_xwayland_surface *wlr_xwayland_surface;
44 struct wl_list link;
45
46 struct wl_listener destroy;
47};
48
40struct sway_wl_shell_surface { 49struct sway_wl_shell_surface {
41 struct sway_view *view; 50 struct sway_view *view;
42 51
@@ -64,10 +73,21 @@ enum sway_view_prop {
64 VIEW_PROP_INSTANCE, 73 VIEW_PROP_INSTANCE,
65}; 74};
66 75
76struct sway_view_impl {
77 const char *(*get_prop)(struct sway_view *view,
78 enum sway_view_prop prop);
79 void (*configure)(struct sway_view *view, double ox, double oy, int width,
80 int height);
81 void (*set_activated)(struct sway_view *view, bool activated);
82 void (*close)(struct sway_view *view);
83};
84
67struct sway_view { 85struct sway_view {
68 enum sway_view_type type; 86 enum sway_view_type type;
69 struct sway_container *swayc; 87 const struct sway_view_impl *impl;
70 struct wlr_surface *surface; 88
89 struct sway_container *swayc; // NULL for unmanaged views
90 struct wlr_surface *surface; // NULL for unmapped views
71 int width, height; 91 int width, height;
72 92
73 union { 93 union {
@@ -82,21 +102,15 @@ struct sway_view {
82 struct sway_wl_shell_surface *sway_wl_shell_surface; 102 struct sway_wl_shell_surface *sway_wl_shell_surface;
83 }; 103 };
84 104
85 struct {
86 const char *(*get_prop)(struct sway_view *view,
87 enum sway_view_prop prop);
88 void (*set_size)(struct sway_view *view,
89 int width, int height);
90 void (*set_position)(struct sway_view *view,
91 double ox, double oy);
92 void (*set_activated)(struct sway_view *view, bool activated);
93 void (*close)(struct sway_view *view);
94 } iface;
95
96 // only used for unmanaged views (shell specific) 105 // only used for unmanaged views (shell specific)
97 struct wl_list unmanaged_view_link; // sway_root::unmanaged views 106 struct wl_list unmanaged_view_link; // sway_root::unmanaged_views
98}; 107};
99 108
109struct sway_view *view_create(enum sway_view_type type,
110 const struct sway_view_impl *impl);
111
112void view_destroy(struct sway_view *view);
113
100const char *view_get_title(struct sway_view *view); 114const char *view_get_title(struct sway_view *view);
101 115
102const char *view_get_app_id(struct sway_view *view); 116const char *view_get_app_id(struct sway_view *view);
@@ -105,18 +119,25 @@ const char *view_get_class(struct sway_view *view);
105 119
106const char *view_get_instance(struct sway_view *view); 120const char *view_get_instance(struct sway_view *view);
107 121
108void view_set_size(struct sway_view *view, int width, int height); 122void view_configure(struct sway_view *view, double ox, double oy, int width,
109 123 int height);
110void view_set_position(struct sway_view *view, double ox, double oy);
111 124
112void view_set_activated(struct sway_view *view, bool activated); 125void view_set_activated(struct sway_view *view, bool activated);
113 126
114void view_close(struct sway_view *view); 127void view_close(struct sway_view *view);
115 128
116void view_update_outputs(struct sway_view *view, const struct wlr_box *before);
117
118void view_damage_whole(struct sway_view *view); 129void view_damage_whole(struct sway_view *view);
119 130
120void view_damage_from(struct sway_view *view); 131void view_damage_from(struct sway_view *view);
121 132
133// view implementation
134
135void view_map(struct sway_view *view, struct wlr_surface *wlr_surface);
136
137void view_unmap(struct sway_view *view);
138
139void view_update_position(struct sway_view *view, double ox, double oy);
140
141void view_update_size(struct sway_view *view, int width, int height);
142
122#endif 143#endif
diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h
index 6ea7c4d6..a1696bcf 100644
--- a/include/swaybar/ipc.h
+++ b/include/swaybar/ipc.h
@@ -4,7 +4,7 @@
4#include "swaybar/bar.h" 4#include "swaybar/bar.h"
5 5
6void ipc_initialize(struct swaybar *bar, const char *bar_id); 6void ipc_initialize(struct swaybar *bar, const char *bar_id);
7bool handle_ipc_event(struct swaybar *bar); 7bool handle_ipc_readable(struct swaybar *bar);
8void ipc_get_workspaces(struct swaybar *bar); 8void ipc_get_workspaces(struct swaybar *bar);
9void ipc_send_workspace_command(struct swaybar *bar, const char *ws); 9void ipc_send_workspace_command(struct swaybar *bar, const char *ws);
10 10
diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h
index 6c595df0..3538f49c 100644
--- a/include/swaybar/status_line.h
+++ b/include/swaybar/status_line.h
@@ -7,10 +7,52 @@
7 7
8enum status_protocol { 8enum status_protocol {
9 PROTOCOL_UNDEF, 9 PROTOCOL_UNDEF,
10 PROTOCOL_ERROR,
10 PROTOCOL_TEXT, 11 PROTOCOL_TEXT,
11 PROTOCOL_I3BAR, 12 PROTOCOL_I3BAR,
12}; 13};
13 14
15struct text_protocol_state {
16 char *buffer;
17 size_t buffer_size;
18};
19
20enum json_node_type {
21 JSON_NODE_UNKNOWN,
22 JSON_NODE_ARRAY,
23 JSON_NODE_STRING,
24};
25
26struct i3bar_protocol_state {
27 bool click_events;
28 char *buffer;
29 size_t buffer_size;
30 size_t buffer_index;
31 const char *current_node;
32 bool escape;
33 size_t depth;
34 enum json_node_type nodes[16];
35};
36
37struct i3bar_block {
38 struct wl_list link;
39 char *full_text, *short_text, *align;
40 bool urgent;
41 uint32_t *color;
42 int min_width;
43 char *name, *instance;
44 bool separator;
45 int separator_block_width;
46 bool markup;
47 // Airblader features
48 uint32_t background;
49 uint32_t border;
50 int border_top;
51 int border_bottom;
52 int border_left;
53 int border_right;
54};
55
14struct status_line { 56struct status_line {
15 pid_t pid; 57 pid_t pid;
16 int read_fd, write_fd; 58 int read_fd, write_fd;
@@ -18,13 +60,18 @@ struct status_line {
18 60
19 enum status_protocol protocol; 61 enum status_protocol protocol;
20 const char *text; 62 const char *text;
63 struct wl_list blocks; // i3bar_block::link
21 64
22 char *buffer; 65 struct text_protocol_state text_state;
23 size_t buffer_size; 66 struct i3bar_protocol_state i3bar_state;
24}; 67};
25 68
26struct status_line *status_line_init(char *cmd); 69struct status_line *status_line_init(char *cmd);
70void status_error(struct status_line *status, const char *text);
71bool status_handle_readable(struct status_line *status);
27void status_line_free(struct status_line *status); 72void status_line_free(struct status_line *status);
28bool handle_status_readable(struct status_line *status); 73bool i3bar_handle_readable(struct status_line *status);
74void i3bar_block_send_click(struct status_line *status,
75 struct i3bar_block *block, int x, int y, uint32_t button);
29 76
30#endif 77#endif
diff --git a/protocols/desktop-shell.xml b/protocols/desktop-shell.xml
deleted file mode 100644
index 581f0c5d..00000000
--- a/protocols/desktop-shell.xml
+++ /dev/null
@@ -1,138 +0,0 @@
1<protocol name="desktop">
2
3 <interface name="desktop_shell" version="3">
4 <description summary="create desktop widgets and helpers">
5 Traditional user interfaces can rely on this interface to define the
6 foundations of typical desktops. Currently it's possible to set up
7 background, panels and locking surfaces.
8 </description>
9
10 <request name="set_background">
11 <arg name="output" type="object" interface="wl_output"/>
12 <arg name="surface" type="object" interface="wl_surface"/>
13 </request>
14
15 <request name="set_panel">
16 <arg name="output" type="object" interface="wl_output"/>
17 <arg name="surface" type="object" interface="wl_surface"/>
18 </request>
19
20 <request name="set_lock_surface">
21 <arg name="surface" type="object" interface="wl_surface"/>
22 </request>
23
24 <request name="unlock"/>
25
26 <request name="set_grab_surface">
27 <description summary="set grab surface">
28 The surface set by this request will receive a fake
29 pointer.enter event during grabs at position 0, 0 and is
30 expected to set an appropriate cursor image as described by
31 the grab_cursor event sent just before the enter event.
32 </description>
33 <arg name="surface" type="object" interface="wl_surface"/>
34 </request>
35
36 <!-- We'll fold most of wl_shell into this interface and then
37 they'll share the configure event. -->
38 <event name="configure">
39 <arg name="edges" type="uint"/>
40 <arg name="surface" type="object" interface="wl_surface"/>
41 <arg name="width" type="int"/>
42 <arg name="height" type="int"/>
43 </event>
44
45 <event name="prepare_lock_surface">
46 <description summary="tell the client to create, set the lock surface">
47 Tell the client we want it to create and set the lock surface, which is
48 a GUI asking the user to unlock the screen. The lock surface is
49 announced with 'set_lock_surface'. Whether or not the client actually
50 implements locking, it MUST send 'unlock' request to let the normal
51 desktop resume.
52 </description>
53 </event>
54
55 <event name="grab_cursor">
56 <description summary="tell client what cursor to show during a grab">
57 This event will be sent immediately before a fake enter event on the
58 grab surface.
59 </description>
60 <arg name="cursor" type="uint"/>
61 </event>
62
63 <enum name="cursor">
64 <entry name="none" value="0"/>
65
66 <entry name="resize_top" value="1"/>
67 <entry name="resize_bottom" value="2"/>
68
69 <entry name="arrow" value="3"/>
70
71 <entry name="resize_left" value="4"/>
72 <entry name="resize_top_left" value="5"/>
73 <entry name="resize_bottom_left" value="6"/>
74
75 <entry name="move" value="7"/>
76
77 <entry name="resize_right" value="8"/>
78 <entry name="resize_top_right" value="9"/>
79 <entry name="resize_bottom_right" value="10"/>
80
81 <entry name="busy" value="11"/>
82 </enum>
83
84 <!-- Version 2 additions -->
85
86 <request name="desktop_ready" since="2">
87 <description summary="desktop is ready to be shown">
88 Tell the server, that enough desktop elements have been drawn
89 to make the desktop look ready for use. During start-up, the
90 server can wait for this request with a black screen before
91 starting to fade in the desktop, for instance. If the client
92 parts of a desktop take a long time to initialize, we avoid
93 showing temporary garbage.
94 </description>
95 </request>
96
97 <!-- Version 3 additions -->
98
99 <enum name="panel_position">
100 <entry name="top" value="0"/>
101 <entry name="bottom" value="1"/>
102 <entry name="left" value="2"/>
103 <entry name="right" value="3"/>
104 </enum>
105
106 <enum name="error">
107 <entry name="invalid_argument" value="0"
108 summary="an invalid argument was provided in a request"/>
109 </enum>
110
111 <request name="set_panel_position" since="3">
112 <description summary="set panel position">
113 Tell the shell which side of the screen the panel is
114 located. This is so that new windows do not overlap the panel
115 and maximized windows maximize properly.
116 </description>
117 <arg name="position" type="uint"/>
118 </request>
119
120 </interface>
121
122 <interface name="screensaver" version="1">
123 <description summary="interface for implementing screensavers">
124 Only one client can bind this interface at a time.
125 </description>
126
127 <request name="set_surface">
128 <description summary="set the surface type as a screensaver">
129 A screensaver surface is normally hidden, and only visible after an
130 idle timeout.
131 </description>
132
133 <arg name="surface" type="object" interface="wl_surface"/>
134 <arg name="output" type="object" interface="wl_output"/>
135 </request>
136
137 </interface>
138</protocol>
diff --git a/protocols/gamma-control.xml b/protocols/gamma-control.xml
deleted file mode 100644
index e6e33265..00000000
--- a/protocols/gamma-control.xml
+++ /dev/null
@@ -1,57 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="gamma_control">
3
4 <copyright>
5 Copyright © 2015 Giulio camuffo
6
7 Permission to use, copy, modify, distribute, and sell this
8 software and its documentation for any purpose is hereby granted
9 without fee, provided that the above copyright notice appear in
10 all copies and that both that copyright notice and this permission
11 notice appear in supporting documentation, and that the name of
12 the copyright holders not be used in advertising or publicity
13 pertaining to distribution of the software without specific,
14 written prior permission. The copyright holders make no
15 representations about the suitability of this software for any
16 purpose. It is provided "as is" without express or implied
17 warranty.
18
19 THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
20 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
23 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
24 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
25 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
26 THIS SOFTWARE.
27 </copyright>
28
29 <interface name="gamma_control_manager" version="1">
30 <request name="destroy" type="destructor"/>
31
32 <request name="get_gamma_control">
33 <arg name="id" type="new_id" interface="gamma_control"/>
34 <arg name="output" type="object" interface="wl_output"/>
35 </request>
36 </interface>
37
38 <interface name="gamma_control" version="1">
39 <enum name="error">
40 <entry name="invalid_gamma" value="0"/>
41 </enum>
42
43 <request name="destroy" type="destructor"/>
44
45 <request name="set_gamma">
46 <arg name="red" type="array"/>
47 <arg name="green" type="array"/>
48 <arg name="blue" type="array"/>
49 </request>
50
51 <request name="reset_gamma"/>
52
53 <event name="gamma_size">
54 <arg name="size" type="uint"/>
55 </event>
56 </interface>
57</protocol>
diff --git a/protocols/server-decoration.xml b/protocols/server-decoration.xml
deleted file mode 100644
index 8bc106c7..00000000
--- a/protocols/server-decoration.xml
+++ /dev/null
@@ -1,94 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="server_decoration">
3 <copyright><![CDATA[
4 Copyright (C) 2015 Martin Gräßlin
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 2.1 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 ]]></copyright>
19 <interface name="org_kde_kwin_server_decoration_manager" version="1">
20 <description summary="Server side window decoration manager">
21 This interface allows to coordinate whether the server should create
22 a server-side window decoration around a wl_surface representing a
23 shell surface (wl_shell_surface or similar). By announcing support
24 for this interface the server indicates that it supports server
25 side decorations.
26 </description>
27 <request name="create">
28 <description summary="Create a server-side decoration object for a given surface">
29 When a client creates a server-side decoration object it indicates
30 that it supports the protocol. The client is supposed to tell the
31 server whether it wants server-side decorations or will provide
32 client-side decorations.
33
34 If the client does not create a server-side decoration object for
35 a surface the server interprets this as lack of support for this
36 protocol and considers it as client-side decorated. Nevertheless a
37 client-side decorated surface should use this protocol to indicate
38 to the server that it does not want a server-side deco.
39 </description>
40 <arg name="id" type="new_id" interface="org_kde_kwin_server_decoration"/>
41 <arg name="surface" type="object" interface="wl_surface"/>
42 </request>
43 <enum name="mode">
44 <description summary="Possible values to use in request_mode and the event mode."/>
45 <entry name="None" value="0" summary="Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated."/>
46 <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/>
47 <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/>
48 </enum>
49 <event name="default_mode">
50 <description summary="The default mode used on the server">
51 This event is emitted directly after binding the interface. It contains
52 the default mode for the decoration. When a new server decoration object
53 is created this new object will be in the default mode until the first
54 request_mode is requested.
55
56 The server may change the default mode at any time.
57 </description>
58 <arg name="mode" type="uint" summary="The default decoration mode applied to newly created server decorations."/>
59 </event>
60 </interface>
61 <interface name="org_kde_kwin_server_decoration" version="1">
62 <request name="release" type="destructor">
63 <description summary="release the server decoration object"/>
64 </request>
65 <enum name="mode">
66 <description summary="Possible values to use in request_mode and the event mode."/>
67 <entry name="None" value="0" summary="Undecorated: The surface is not decorated at all, neither server nor client-side. An example is a popup surface which should not be decorated."/>
68 <entry name="Client" value="1" summary="Client-side decoration: The decoration is part of the surface and the client."/>
69 <entry name="Server" value="2" summary="Server-side decoration: The server embeds the surface into a decoration frame."/>
70 </enum>
71 <request name="request_mode">
72 <description summary="The decoration mode the surface wants to use."/>
73 <arg name="mode" type="uint" summary="The mode this surface wants to use."/>
74 </request>
75 <event name="mode">
76 <description summary="The new decoration mode applied by the server">
77 This event is emitted directly after the decoration is created and
78 represents the base decoration policy by the server. E.g. a server
79 which wants all surfaces to be client-side decorated will send Client,
80 a server which wants server-side decoration will send Server.
81
82 The client can request a different mode through the decoration request.
83 The server will acknowledge this by another event with the same mode. So
84 even if a server prefers server-side decoration it's possible to force a
85 client-side decoration.
86
87 The server may emit this event at any time. In this case the client can
88 again request a different mode. It's the responsibility of the server to
89 prevent a feedback loop.
90 </description>
91 <arg name="mode" type="uint" summary="The decoration mode applied to the surface by the server."/>
92 </event>
93 </interface>
94</protocol>
diff --git a/protocols/swaylock.xml b/protocols/swaylock.xml
deleted file mode 100644
index c7a102dd..00000000
--- a/protocols/swaylock.xml
+++ /dev/null
@@ -1,18 +0,0 @@
1<protocol name="lock">
2
3 <interface name="lock" version="1">
4 <description summary="create lock screen UIs">
5 The Weston desktop-shell protocol's locking functionality depends more
6 on the behavior of the compositor than of a screen locking client, so
7 another protocol is necessary.
8 </description>
9
10 <request name="set_lock_surface">
11 <arg name="output" type="object" interface="wl_output"/>
12 <arg name="surface" type="object" interface="wl_surface"/>
13 </request>
14
15 <request name="unlock"/>
16
17 </interface>
18</protocol>
diff --git a/protocols/xdg-shell.xml b/protocols/xdg-shell.xml
deleted file mode 100644
index 7bf4ae3a..00000000
--- a/protocols/xdg-shell.xml
+++ /dev/null
@@ -1,430 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="xdg_shell">
3
4 <copyright>
5 Copyright © 2008-2013 Kristian Høgsberg
6 Copyright © 2013 Rafael Antognolli
7 Copyright © 2013 Jasper St. Pierre
8 Copyright © 2010-2013 Intel Corporation
9
10 Permission to use, copy, modify, distribute, and sell this
11 software and its documentation for any purpose is hereby granted
12 without fee, provided that the above copyright notice appear in
13 all copies and that both that copyright notice and this permission
14 notice appear in supporting documentation, and that the name of
15 the copyright holders not be used in advertising or publicity
16 pertaining to distribution of the software without specific,
17 written prior permission. The copyright holders make no
18 representations about the suitability of this software for any
19 purpose. It is provided "as is" without express or implied
20 warranty.
21
22 THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
23 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
24 FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
26 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
27 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
29 THIS SOFTWARE.
30 </copyright>
31
32 <interface name="xdg_shell" version="1">
33 <description summary="create desktop-style surfaces">
34 This interface is implemented by servers that provide
35 desktop-style user interfaces.
36
37 It allows clients to associate a xdg_surface with
38 a basic surface.
39 </description>
40
41 <enum name="version">
42 <description summary="latest protocol version">
43 The 'current' member of this enum gives the version of the
44 protocol. Implementations can compare this to the version
45 they implement using static_assert to ensure the protocol and
46 implementation versions match.
47 </description>
48 <entry name="current" value="4" summary="Always the latest version"/>
49 </enum>
50
51 <enum name="error">
52 <entry name="role" value="0" summary="given wl_surface has another role"/>
53 </enum>
54
55 <request name="use_unstable_version">
56 <description summary="enable use of this unstable version">
57 Negotiate the unstable version of the interface. This
58 mechanism is in place to ensure client and server agree on the
59 unstable versions of the protocol that they speak or exit
60 cleanly if they don't agree. This request will go away once
61 the xdg-shell protocol is stable.
62 </description>
63 <arg name="version" type="int"/>
64 </request>
65
66 <request name="get_xdg_surface">
67 <description summary="create a shell surface from a surface">
68 Create a shell surface for an existing surface.
69
70 This request gives the surface the role of xdg_surface. If the
71 surface already has another role, it raises a protocol error.
72
73 Only one shell or popup surface can be associated with a given
74 surface.
75 </description>
76 <arg name="id" type="new_id" interface="xdg_surface"/>
77 <arg name="surface" type="object" interface="wl_surface"/>
78 </request>
79
80 <request name="get_xdg_popup">
81 <description summary="create a shell surface from a surface">
82 Create a popup surface for an existing surface.
83
84 This request gives the surface the role of xdg_popup. If the
85 surface already has another role, it raises a protocol error.
86
87 Only one shell or popup surface can be associated with a given
88 surface.
89 </description>
90 <arg name="id" type="new_id" interface="xdg_popup"/>
91 <arg name="surface" type="object" interface="wl_surface"/>
92 <arg name="parent" type="object" interface="wl_surface"/>
93 <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
94 <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
95 <arg name="x" type="int"/>
96 <arg name="y" type="int"/>
97 <arg name="flags" type="uint"/>
98 </request>
99
100 <event name="ping">
101 <description summary="check if the client is alive">
102 The ping event asks the client if it's still alive. Pass the
103 serial specified in the event back to the compositor by sending
104 a "pong" request back with the specified serial.
105
106 Compositors can use this to determine if the client is still
107 alive. It's unspecified what will happen if the client doesn't
108 respond to the ping request, or in what timeframe. Clients should
109 try to respond in a reasonable amount of time.
110 </description>
111 <arg name="serial" type="uint" summary="pass this to the callback"/>
112 </event>
113
114 <request name="pong">
115 <description summary="respond to a ping event">
116 A client must respond to a ping event with a pong request or
117 the client may be deemed unresponsive.
118 </description>
119 <arg name="serial" type="uint" summary="serial of the ping event"/>
120 </request>
121 </interface>
122
123 <interface name="xdg_surface" version="1">
124
125 <description summary="desktop-style metadata interface">
126 An interface that may be implemented by a wl_surface, for
127 implementations that provide a desktop-style user interface.
128
129 It provides requests to treat surfaces like windows, allowing to set
130 properties like maximized, fullscreen, minimized, and to move and resize
131 them, and associate metadata like title and app id.
132
133 On the server side the object is automatically destroyed when
134 the related wl_surface is destroyed. On client side,
135 xdg_surface.destroy() must be called before destroying
136 the wl_surface object.
137 </description>
138
139 <request name="destroy" type="destructor">
140 <description summary="remove xdg_surface interface">
141 The xdg_surface interface is removed from the wl_surface object
142 that was turned into a xdg_surface with
143 xdg_shell.get_xdg_surface request. The xdg_surface properties,
144 like maximized and fullscreen, are lost. The wl_surface loses
145 its role as a xdg_surface. The wl_surface is unmapped.
146 </description>
147 </request>
148
149 <request name="set_parent">
150 <description summary="surface is a child of another surface">
151 Child surfaces are stacked above their parents, and will be
152 unmapped if the parent is unmapped too. They should not appear
153 on task bars and alt+tab.
154 </description>
155 <arg name="parent" type="object" interface="wl_surface" allow-null="true"/>
156 </request>
157
158 <request name="set_title">
159 <description summary="set surface title">
160 Set a short title for the surface.
161
162 This string may be used to identify the surface in a task bar,
163 window list, or other user interface elements provided by the
164 compositor.
165
166 The string must be encoded in UTF-8.
167 </description>
168 <arg name="title" type="string"/>
169 </request>
170
171 <request name="set_app_id">
172 <description summary="set surface class">
173 Set an id for the surface.
174
175 The app id identifies the general class of applications to which
176 the surface belongs.
177
178 It should be the ID that appears in the new desktop entry
179 specification, the interface name.
180 </description>
181 <arg name="app_id" type="string"/>
182 </request>
183
184 <request name="show_window_menu">
185 <description summary="show the window menu">
186 Clients implementing client-side decorations might want to show
187 a context menu when right-clicking on the decorations, giving the
188 user a menu that they can use to maximize or minimize the window.
189
190 This request asks the compositor to pop up such a window menu at
191 the given position, relative to the parent surface. There are
192 no guarantees as to what the window menu contains.
193
194 Your surface must have focus on the seat passed in to pop up the
195 window menu.
196 </description>
197
198 <arg name="seat" type="object" interface="wl_seat" summary="the seat to pop the window up on"/>
199 <arg name="serial" type="uint" summary="serial of the event to pop up the window for"/>
200 <arg name="x" type="int" summary="the x position to pop up the window menu at"/>
201 <arg name="y" type="int" summary="the y position to pop up the window menu at"/>
202 </request>
203
204 <request name="move">
205 <description summary="start an interactive move">
206 Start a pointer-driven move of the surface.
207
208 This request must be used in response to a button press event.
209 The server may ignore move requests depending on the state of
210 the surface (e.g. fullscreen or maximized).
211 </description>
212 <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
213 <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
214 </request>
215
216 <enum name="resize_edge">
217 <description summary="edge values for resizing">
218 These values are used to indicate which edge of a surface
219 is being dragged in a resize operation. The server may
220 use this information to adapt its behavior, e.g. choose
221 an appropriate cursor image.
222 </description>
223 <entry name="none" value="0"/>
224 <entry name="top" value="1"/>
225 <entry name="bottom" value="2"/>
226 <entry name="left" value="4"/>
227 <entry name="top_left" value="5"/>
228 <entry name="bottom_left" value="6"/>
229 <entry name="right" value="8"/>
230 <entry name="top_right" value="9"/>
231 <entry name="bottom_right" value="10"/>
232 </enum>
233
234 <request name="resize">
235 <description summary="start an interactive resize">
236 Start a pointer-driven resizing of the surface.
237
238 This request must be used in response to a button press event.
239 The server may ignore resize requests depending on the state of
240 the surface (e.g. fullscreen or maximized).
241 </description>
242 <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat whose pointer is used"/>
243 <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
244 <arg name="edges" type="uint" summary="which edge or corner is being dragged"/>
245 </request>
246
247 <enum name="state">
248 <description summary="types of state on the surface">
249 The different state values used on the surface. This is designed for
250 state values like maximized, fullscreen. It is paired with the
251 configure event to ensure that both the client and the compositor
252 setting the state can be synchronized.
253
254 States set in this way are double-buffered. They will get applied on
255 the next commit.
256
257 Desktop environments may extend this enum by taking up a range of
258 values and documenting the range they chose in this description.
259 They are not required to document the values for the range that they
260 chose. Ideally, any good extensions from a desktop environment should
261 make its way into standardization into this enum.
262
263 The current reserved ranges are:
264
265 0x0000 - 0x0FFF: xdg-shell core values, documented below.
266 0x1000 - 0x1FFF: GNOME
267 </description>
268 <entry name="maximized" value="1">
269 <description summary="the surface is maximized">
270 The surface is maximized. The window geometry specified in the configure
271 event must be obeyed by the client.
272 </description>
273 </entry>
274 <entry name="fullscreen" value="2">
275 <description summary="the surface is fullscreen">
276 The surface is fullscreen. The window geometry specified in the configure
277 event must be obeyed by the client.
278 </description>
279 </entry>
280 <entry name="resizing" value="3">
281 <description summary="the surface is being resized">
282 The surface is being resized. The window geometry specified in the
283 configure event is a maximum; the client cannot resize beyond it.
284 Clients that have aspect ratio or cell sizing configuration can use
285 a smaller size, however.
286 </description>
287 </entry>
288 <entry name="activated" value="4">
289 <description summary="the client window is active">
290 Client window decorations should be painted as if the window is
291 active. Do not assume this means that the window actually has
292 keyboard or pointer focus.
293 </description>
294 </entry>
295 </enum>
296
297 <event name="configure">
298 <description summary="suggest a surface change">
299 The configure event asks the client to resize its surface.
300
301 The width and height arguments specify a hint to the window
302 about how its surface should be resized in window geometry
303 coordinates. The states listed in the event specify how the
304 width/height arguments should be interpreted.
305
306 A client should arrange a new surface, and then send a
307 ack_configure request with the serial sent in this configure
308 event before attaching a new surface.
309
310 If the client receives multiple configure events before it
311 can respond to one, it is free to discard all but the last
312 event it received.
313 </description>
314
315 <arg name="width" type="int"/>
316 <arg name="height" type="int"/>
317 <arg name="states" type="array"/>
318 <arg name="serial" type="uint"/>
319 </event>
320
321 <request name="ack_configure">
322 <description summary="ack a configure event">
323 When a configure event is received, a client should then ack it
324 using the ack_configure request to ensure that the compositor
325 knows the client has seen the event.
326
327 By this point, the state is confirmed, and the next attach should
328 contain the buffer drawn for the configure event you are acking.
329 </description>
330 <arg name="serial" type="uint" summary="a serial to configure for"/>
331 </request>
332
333 <request name="set_window_geometry">
334 <description summary="set the new window geometry">
335 The window geometry of a window is its "visible bounds" from the
336 user's perspective. Client-side decorations often have invisible
337 portions like drop-shadows which should be ignored for the
338 purposes of aligning, placing and constraining windows.
339
340 The default value is the full bounds of the surface, including any
341 subsurfaces. Once the window geometry of the surface is set once,
342 it is not possible to unset it, and it will remain the same until
343 set_window_geometry is called again, even if a new subsurface or
344 buffer is attached.
345
346 If responding to a configure event, the window geometry in here
347 must respect the sizing negotiations specified by the states in
348 the configure event.
349 </description>
350 <arg name="x" type="int"/>
351 <arg name="y" type="int"/>
352 <arg name="width" type="int"/>
353 <arg name="height" type="int"/>
354 </request>
355
356 <request name="set_maximized" />
357 <request name="unset_maximized" />
358
359 <request name="set_fullscreen">
360 <description summary="set the window as fullscreen on a monitor">
361 Make the surface fullscreen.
362
363 You can specify an output that you would prefer to be fullscreen.
364 If this value is NULL, it's up to the compositor to choose which
365 display will be used to map this surface.
366 </description>
367 <arg name="output" type="object" interface="wl_output" allow-null="true"/>
368 </request>
369 <request name="unset_fullscreen" />
370
371 <request name="set_minimized" />
372
373 <event name="close">
374 <description summary="surface wants to be closed">
375 The close event is sent by the compositor when the user
376 wants the surface to be closed. This should be equivalent to
377 the user clicking the close button in client-side decorations,
378 if your application has any...
379
380 This is only a request that the user intends to close your
381 window. The client may choose to ignore this request, or show
382 a dialog to ask the user to save their data...
383 </description>
384 </event>
385 </interface>
386
387 <interface name="xdg_popup" version="1">
388 <description summary="desktop-style metadata interface">
389 An interface that may be implemented by a wl_surface, for
390 implementations that provide a desktop-style popups/menus. A popup
391 surface is a transient surface with an added pointer grab.
392
393 An existing implicit grab will be changed to owner-events mode,
394 and the popup grab will continue after the implicit grab ends
395 (i.e. releasing the mouse button does not cause the popup to be
396 unmapped).
397
398 The popup grab continues until the window is destroyed or a mouse
399 button is pressed in any other clients window. A click in any of
400 the clients surfaces is reported as normal, however, clicks in
401 other clients surfaces will be discarded and trigger the callback.
402
403 The x and y arguments specify the locations of the upper left
404 corner of the surface relative to the upper left corner of the
405 parent surface, in surface local coordinates.
406
407 xdg_popup surfaces are always transient for another surface.
408 </description>
409
410 <request name="destroy" type="destructor">
411 <description summary="remove xdg_surface interface">
412 The xdg_surface interface is removed from the wl_surface object
413 that was turned into a xdg_surface with
414 xdg_shell.get_xdg_surface request. The xdg_surface properties,
415 like maximized and fullscreen, are lost. The wl_surface loses
416 its role as a xdg_surface. The wl_surface is unmapped.
417 </description>
418 </request>
419
420 <event name="popup_done">
421 <description summary="popup interaction is done">
422 The popup_done event is sent out when a popup grab is broken,
423 that is, when the users clicks a surface that doesn't belong
424 to the client owning the popup surface.
425 </description>
426 <arg name="serial" type="uint" summary="serial of the implicit grab on the pointer"/>
427 </event>
428
429 </interface>
430</protocol>
diff --git a/sway/commands.c b/sway/commands.c
index 90544220..8156a08e 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -72,23 +72,23 @@ void apply_input_config(struct input_config *input) {
72 list_add(config->input_configs, input); 72 list_add(config->input_configs, input);
73 } 73 }
74 74
75 sway_input_manager_apply_input_config(input_manager, input); 75 input_manager_apply_input_config(input_manager, input);
76} 76}
77 77
78void apply_seat_config(struct seat_config *seat) { 78void apply_seat_config(struct seat_config *seat_config) {
79 int i; 79 int i;
80 i = list_seq_find(config->seat_configs, seat_name_cmp, seat->name); 80 i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name);
81 if (i >= 0) { 81 if (i >= 0) {
82 // merge existing config 82 // merge existing config
83 struct seat_config *sc = config->seat_configs->items[i]; 83 struct seat_config *sc = config->seat_configs->items[i];
84 merge_seat_config(sc, seat); 84 merge_seat_config(sc, seat_config);
85 free_seat_config(seat); 85 free_seat_config(seat_config);
86 seat = sc; 86 seat_config = sc;
87 } else { 87 } else {
88 list_add(config->seat_configs, seat); 88 list_add(config->seat_configs, seat_config);
89 } 89 }
90 90
91 sway_input_manager_apply_seat_config(input_manager, seat); 91 input_manager_apply_seat_config(input_manager, seat_config);
92} 92}
93 93
94/* Keep alphabetized */ 94/* Keep alphabetized */
@@ -162,7 +162,12 @@ static struct cmd_handler command_handlers[] = {
162 { "focus", cmd_focus }, 162 { "focus", cmd_focus },
163 { "kill", cmd_kill }, 163 { "kill", cmd_kill },
164 { "layout", cmd_layout }, 164 { "layout", cmd_layout },
165 { "move", cmd_move },
165 { "reload", cmd_reload }, 166 { "reload", cmd_reload },
167 { "split", cmd_split },
168 { "splith", cmd_splith },
169 { "splitt", cmd_splitt },
170 { "splitv", cmd_splitv },
166}; 171};
167 172
168static int handler_compare(const void *_a, const void *_b) { 173static int handler_compare(const void *_a, const void *_b) {
@@ -262,7 +267,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
262 267
263 if (seat == NULL) { 268 if (seat == NULL) {
264 // passing a NULL seat means we just pick the default seat 269 // passing a NULL seat means we just pick the default seat
265 seat = sway_input_manager_get_default_seat(input_manager); 270 seat = input_manager_get_default_seat(input_manager);
266 if (!sway_assert(seat, "could not find a seat to run the command on")) { 271 if (!sway_assert(seat, "could not find a seat to run the command on")) {
267 return NULL; 272 return NULL;
268 } 273 }
@@ -340,7 +345,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
340 // without criteria, the command acts upon the focused 345 // without criteria, the command acts upon the focused
341 // container 346 // container
342 config->handler_context.current_container = 347 config->handler_context.current_container =
343 sway_seat_get_focus_inactive(seat, &root_container); 348 seat_get_focus_inactive(seat, &root_container);
344 if (!sway_assert(config->handler_context.current_container, 349 if (!sway_assert(config->handler_context.current_container,
345 "could not get focus-inactive for root container")) { 350 "could not get focus-inactive for root container")) {
346 return NULL; 351 return NULL;
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 64f079f4..74d9d535 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -20,10 +20,6 @@ static bool parse_movement_direction(const char *name,
20 *out = MOVE_PARENT; 20 *out = MOVE_PARENT;
21 } else if (strcasecmp(name, "child") == 0) { 21 } else if (strcasecmp(name, "child") == 0) {
22 *out = MOVE_CHILD; 22 *out = MOVE_CHILD;
23 } else if (strcasecmp(name, "next") == 0) {
24 *out = MOVE_NEXT;
25 } else if (strcasecmp(name, "prev") == 0) {
26 *out = MOVE_PREV;
27 } else { 23 } else {
28 return false; 24 return false;
29 } 25 }
@@ -40,7 +36,7 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
40 } 36 }
41 37
42 if (argc == 0) { 38 if (argc == 0) {
43 sway_seat_set_focus(seat, con); 39 seat_set_focus(seat, con);
44 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 40 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
45 } 41 }
46 42
@@ -51,9 +47,10 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
51 "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'"); 47 "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'");
52 } 48 }
53 49
54 struct sway_container *next_focus = container_get_in_direction(con, seat, direction); 50 struct sway_container *next_focus = container_get_in_direction(
51 con, seat, direction);
55 if (next_focus) { 52 if (next_focus) {
56 sway_seat_set_focus(seat, next_focus); 53 seat_set_focus(seat, next_focus);
57 } 54 }
58 55
59 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 56 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/kill.c b/sway/commands/kill.c
index f6774767..46d6e98e 100644
--- a/sway/commands/kill.c
+++ b/sway/commands/kill.c
@@ -3,21 +3,28 @@
3#include "sway/input/input-manager.h" 3#include "sway/input/input-manager.h"
4#include "sway/input/seat.h" 4#include "sway/input/seat.h"
5#include "sway/tree/view.h" 5#include "sway/tree/view.h"
6#include "sway/tree/container.h"
6#include "sway/commands.h" 7#include "sway/commands.h"
7 8
8struct cmd_results *cmd_kill(int argc, char **argv) { 9struct cmd_results *cmd_kill(int argc, char **argv) {
9 enum sway_container_type type = config->handler_context.current_container->type; 10 struct sway_container *con =
10 if (type != C_VIEW && type != C_CONTAINER) { 11 config->handler_context.current_container;
12
13 switch (con->type) {
14 case C_ROOT:
15 case C_OUTPUT:
16 case C_WORKSPACE:
17 case C_TYPES:
11 return cmd_results_new(CMD_INVALID, NULL, 18 return cmd_results_new(CMD_INVALID, NULL,
12 "Can only kill views and containers with this command"); 19 "Can only kill views and containers with this command");
13 } 20 break;
14 21 case C_CONTAINER:
15 // TODO close arbitrary containers without a view 22 con = container_destroy(con);
16 struct sway_view *view = 23 arrange_windows(con, -1, -1);
17 config->handler_context.current_container->sway_view; 24 break;
18 25 case C_VIEW:
19 if (view) { 26 view_close(con->sway_view);
20 view_close(view); 27 break;
21 } 28 }
22 29
23 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 30 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/move.c b/sway/commands/move.c
new file mode 100644
index 00000000..644c622b
--- /dev/null
+++ b/sway/commands/move.c
@@ -0,0 +1,184 @@
1#include <string.h>
2#include <strings.h>
3#include <wlr/types/wlr_output.h>
4#include <wlr/types/wlr_output_layout.h>
5#include <wlr/util/log.h>
6#include "sway/commands.h"
7#include "sway/input/seat.h"
8#include "sway/output.h"
9#include "sway/tree/container.h"
10#include "sway/tree/layout.h"
11#include "sway/tree/workspace.h"
12#include "stringop.h"
13#include "list.h"
14
15static const char* expected_syntax =
16 "Expected 'move <left|right|up|down> <[px] px>' or "
17 "'move <container|window> to workspace <name>' or "
18 "'move <container|window|workspace> to output <name|direction>' or "
19 "'move position mouse'";
20
21static struct sway_container *output_in_direction(const char *direction,
22 struct wlr_output *reference, int ref_ox, int ref_oy) {
23 int ref_lx = ref_ox + reference->lx,
24 ref_ly = ref_oy + reference->ly;
25 struct {
26 char *name;
27 enum wlr_direction direction;
28 } names[] = {
29 { "up", WLR_DIRECTION_UP },
30 { "down", WLR_DIRECTION_DOWN },
31 { "left", WLR_DIRECTION_LEFT },
32 { "right", WLR_DIRECTION_RIGHT },
33 };
34 for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
35 if (strcasecmp(names[i].name, direction) == 0) {
36 struct wlr_output *adjacent = wlr_output_layout_adjacent_output(
37 root_container.sway_root->output_layout,
38 names[i].direction, reference, ref_lx, ref_ly);
39 if (adjacent) {
40 struct sway_output *sway_output = adjacent->data;
41 return sway_output->swayc;
42 }
43 break;
44 }
45 }
46 return output_by_name(direction);
47}
48
49static struct cmd_results *cmd_move_container(struct sway_container *current,
50 int argc, char **argv) {
51 struct cmd_results *error = NULL;
52 if ((error = checkarg(argc, "move container/window",
53 EXPECTED_AT_LEAST, 4))) {
54 return error;
55 } else if (strcasecmp(argv[1], "to") == 0
56 && strcasecmp(argv[2], "workspace") == 0) {
57 // move container to workspace x
58 if (current->type == C_WORKSPACE) {
59 // TODO: Wrap children in a container and move that
60 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
61 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
62 return cmd_results_new(CMD_FAILURE, "move",
63 "Can only move containers and views.");
64 }
65 struct sway_container *ws;
66 const char *num_name = NULL;
67 char *ws_name = NULL;
68 if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
69 // move "container to workspace number x"
70 num_name = argv[4];
71 ws = workspace_by_number(num_name);
72 } else {
73 ws_name = join_args(argv + 3, argc - 3);
74 ws = workspace_by_name(ws_name);
75 }
76 if (!ws) {
77 ws = workspace_create(ws_name ? ws_name : num_name);
78 }
79 free(ws_name);
80 struct sway_container *old_parent = current->parent;
81 struct sway_container *focus = seat_get_focus_inactive(
82 config->handler_context.seat, ws);
83 container_move_to(current, focus);
84 seat_set_focus(config->handler_context.seat, old_parent);
85 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
86 } else if (strcasecmp(argv[1], "to") == 0
87 && strcasecmp(argv[2], "output") == 0) {
88 if (current->type == C_WORKSPACE) {
89 // TODO: Wrap children in a container and move that
90 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
91 } else if (current->type != C_CONTAINER
92 && current->type != C_VIEW) {
93 return cmd_results_new(CMD_FAILURE, "move",
94 "Can only move containers and views.");
95 }
96 struct sway_container *source = container_parent(current, C_OUTPUT);
97 struct sway_container *destination = output_in_direction(argv[3],
98 source->sway_output->wlr_output, current->x, current->y);
99 if (!destination) {
100 return cmd_results_new(CMD_FAILURE, "move workspace",
101 "Can't find output with name/direction '%s'", argv[3]);
102 }
103 struct sway_container *focus = seat_get_focus_inactive(
104 config->handler_context.seat, destination);
105 if (!focus) {
106 // We've never been to this output before
107 focus = destination->children->items[0];
108 }
109 struct sway_container *old_parent = current->parent;
110 container_move_to(current, focus);
111 seat_set_focus(config->handler_context.seat, old_parent);
112 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
113 }
114 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
115}
116
117static struct cmd_results *cmd_move_workspace(struct sway_container *current,
118 int argc, char **argv) {
119 struct cmd_results *error = NULL;
120 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
121 return error;
122 } else if (strcasecmp(argv[1], "to") != 0
123 || strcasecmp(argv[2], "output") != 0) {
124 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
125 }
126 struct sway_container *source = container_parent(current, C_OUTPUT);
127 int center_x = current->width / 2 + current->x,
128 center_y = current->height / 2 + current->y;
129 struct sway_container *destination = output_in_direction(argv[3],
130 source->sway_output->wlr_output, center_x, center_y);
131 if (!destination) {
132 return cmd_results_new(CMD_FAILURE, "move workspace",
133 "Can't find output with name/direction '%s'", argv[3]);
134 }
135 if (current->type != C_WORKSPACE) {
136 current = container_parent(current, C_WORKSPACE);
137 }
138 container_move_to(current, destination);
139 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
140}
141
142struct cmd_results *cmd_move(int argc, char **argv) {
143 struct cmd_results *error = NULL;
144 int move_amt = 10;
145 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
146 return error;
147 }
148 struct sway_container *current = config->handler_context.current_container;
149
150 if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) {
151 char *inv;
152 move_amt = (int)strtol(argv[1], &inv, 10);
153 if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
154 return cmd_results_new(CMD_FAILURE, "move",
155 "Invalid distance specified");
156 }
157 }
158
159 if (strcasecmp(argv[0], "left") == 0) {
160 container_move(current, MOVE_LEFT, move_amt);
161 } else if (strcasecmp(argv[0], "right") == 0) {
162 container_move(current, MOVE_RIGHT, move_amt);
163 } else if (strcasecmp(argv[0], "up") == 0) {
164 container_move(current, MOVE_UP, move_amt);
165 } else if (strcasecmp(argv[0], "down") == 0) {
166 container_move(current, MOVE_DOWN, move_amt);
167 } else if (strcasecmp(argv[0], "container") == 0
168 || strcasecmp(argv[0], "window") == 0) {
169 return cmd_move_container(current, argc, argv);
170 } else if (strcasecmp(argv[0], "workspace") == 0) {
171 return cmd_move_workspace(current, argc, argv);
172 } else if (strcasecmp(argv[0], "scratchpad") == 0
173 || (strcasecmp(argv[0], "to") == 0
174 && strcasecmp(argv[1], "scratchpad") == 0)) {
175 // TODO: scratchpad
176 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
177 } else if (strcasecmp(argv[0], "position") == 0) {
178 // TODO: floating
179 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
180 } else {
181 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
182 }
183 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
184}
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
index 45079616..819b769c 100644
--- a/sway/commands/seat.c
+++ b/sway/commands/seat.c
@@ -14,7 +14,8 @@ struct cmd_results *cmd_seat(int argc, char **argv) {
14 free_seat_config(config->handler_context.seat_config); 14 free_seat_config(config->handler_context.seat_config);
15 config->handler_context.seat_config = new_seat_config(argv[0]); 15 config->handler_context.seat_config = new_seat_config(argv[0]);
16 if (!config->handler_context.seat_config) { 16 if (!config->handler_context.seat_config) {
17 return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config"); 17 return cmd_results_new(CMD_FAILURE, NULL,
18 "Couldn't allocate config");
18 } 19 }
19 wlr_log(L_DEBUG, "entering seat block: %s", argv[0]); 20 wlr_log(L_DEBUG, "entering seat block: %s", argv[0]);
20 return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL); 21 return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL);
@@ -28,7 +29,8 @@ struct cmd_results *cmd_seat(int argc, char **argv) {
28 if (!has_context) { 29 if (!has_context) {
29 config->handler_context.seat_config = new_seat_config(argv[0]); 30 config->handler_context.seat_config = new_seat_config(argv[0]);
30 if (!config->handler_context.seat_config) { 31 if (!config->handler_context.seat_config) {
31 return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config"); 32 return cmd_results_new(CMD_FAILURE, NULL,
33 "Couldn't allocate config");
32 } 34 }
33 } 35 }
34 36
@@ -41,7 +43,10 @@ struct cmd_results *cmd_seat(int argc, char **argv) {
41 } else if (strcasecmp("fallback", argv[1]) == 0) { 43 } else if (strcasecmp("fallback", argv[1]) == 0) {
42 res = seat_cmd_fallback(argc_new, argv_new); 44 res = seat_cmd_fallback(argc_new, argv_new);
43 } else { 45 } else {
44 res = cmd_results_new(CMD_INVALID, "seat <name>", "Unknown command %s", argv[1]); 46 res =
47 cmd_results_new(CMD_INVALID,
48 "seat <name>", "Unknown command %s",
49 argv[1]);
45 } 50 }
46 51
47 if (!has_context) { 52 if (!has_context) {
diff --git a/sway/commands/split.c b/sway/commands/split.c
new file mode 100644
index 00000000..ab8565a9
--- /dev/null
+++ b/sway/commands/split.c
@@ -0,0 +1,76 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "sway/tree/container.h"
5#include "sway/tree/layout.h"
6#include "sway/tree/view.h"
7#include "sway/input/input-manager.h"
8#include "sway/input/seat.h"
9#include "log.h"
10
11static struct cmd_results *do_split(int layout) {
12 struct sway_container *con = config->handler_context.current_container;
13 struct sway_container *parent = container_split(con, layout);
14 arrange_windows(parent, -1, -1);
15
16 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
17}
18
19struct cmd_results *cmd_split(int argc, char **argv) {
20 struct cmd_results *error = NULL;
21 if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
22 return error;
23 }
24 if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) {
25 do_split(L_VERT);
26 } else if (strcasecmp(argv[0], "h") == 0 ||
27 strcasecmp(argv[0], "horizontal") == 0) {
28 do_split(L_HORIZ);
29 } else if (strcasecmp(argv[0], "t") == 0 ||
30 strcasecmp(argv[0], "toggle") == 0) {
31 struct sway_container *focused =
32 config->handler_context.current_container;
33
34 if (focused->parent->layout == L_VERT) {
35 do_split(L_HORIZ);
36 } else {
37 do_split(L_VERT);
38 }
39 } else {
40 error = cmd_results_new(CMD_FAILURE, "split",
41 "Invalid split command (expected either horizontal or vertical).");
42 return error;
43 }
44 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
45}
46
47struct cmd_results *cmd_splitv(int argc, char **argv) {
48 struct cmd_results *error = NULL;
49 if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
50 return error;
51 }
52 return do_split(L_VERT);
53}
54
55struct cmd_results *cmd_splith(int argc, char **argv) {
56 struct cmd_results *error = NULL;
57 if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
58 return error;
59 }
60 return do_split(L_HORIZ);
61}
62
63struct cmd_results *cmd_splitt(int argc, char **argv) {
64 struct cmd_results *error = NULL;
65 if ((error = checkarg(argc, "splitv", EXPECTED_EQUAL_TO, 0))) {
66 return error;
67 }
68
69 struct sway_container *con = config->handler_context.current_container;
70
71 if (con->parent->layout == L_VERT) {
72 return do_split(L_HORIZ);
73 } else {
74 return do_split(L_VERT);
75 }
76}
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 8f39e5fc..aa4096f7 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -91,7 +91,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
91 } 91 }
92 workspace_switch(ws); 92 workspace_switch(ws);
93 current_container = 93 current_container =
94 sway_seat_get_focus(config->handler_context.seat); 94 seat_get_focus(config->handler_context.seat);
95 struct sway_container *new_output = container_parent(current_container, C_OUTPUT); 95 struct sway_container *new_output = container_parent(current_container, C_OUTPUT);
96 96
97 if (config->mouse_warping && old_output != new_output) { 97 if (config->mouse_warping && old_output != new_output) {
diff --git a/sway/config.c b/sway/config.c
index 0eecf7f6..90b833ab 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -125,7 +125,7 @@ static void destroy_removed_seats(struct sway_config *old_config,
125 seat_name_cmp, seat_config->name) < 0) { 125 seat_name_cmp, seat_config->name) < 0) {
126 seat = input_manager_get_seat(input_manager, 126 seat = input_manager_get_seat(input_manager,
127 seat_config->name); 127 seat_config->name);
128 sway_seat_destroy(seat); 128 seat_destroy(seat);
129 } 129 }
130 } 130 }
131} 131}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index c18f51c7..3c7b45f7 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -7,6 +7,8 @@
7#include <wlr/types/wlr_output_damage.h> 7#include <wlr/types/wlr_output_damage.h>
8#include <wlr/types/wlr_output.h> 8#include <wlr/types/wlr_output.h>
9#include <wlr/util/log.h> 9#include <wlr/util/log.h>
10#include "sway/input/input-manager.h"
11#include "sway/input/seat.h"
10#include "sway/layers.h" 12#include "sway/layers.h"
11#include "sway/output.h" 13#include "sway/output.h"
12#include "sway/server.h" 14#include "sway/server.h"
@@ -187,6 +189,31 @@ void arrange_layers(struct sway_output *output) {
187 &usable_area, false); 189 &usable_area, false);
188 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], 190 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
189 &usable_area, false); 191 &usable_area, false);
192
193 // Find topmost keyboard interactive layer, if such a layer exists
194 uint32_t layers_above_shell[] = {
195 ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
196 ZWLR_LAYER_SHELL_V1_LAYER_TOP,
197 };
198 size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
199 struct sway_layer_surface *layer, *topmost = NULL;
200 for (size_t i = 0; i < nlayers; ++i) {
201 wl_list_for_each_reverse(layer,
202 &output->layers[layers_above_shell[i]], link) {
203 if (layer->layer_surface->current.keyboard_interactive) {
204 topmost = layer;
205 break;
206 }
207 }
208 if (topmost != NULL) {
209 break;
210 }
211 }
212
213 struct sway_seat *seat;
214 wl_list_for_each(seat, &input_manager->seats, link) {
215 seat_set_focus_layer(seat, topmost ? topmost->layer_surface : NULL);
216 }
190} 217}
191 218
192static void handle_output_destroy(struct wl_listener *listener, void *data) { 219static void handle_output_destroy(struct wl_listener *listener, void *data) {
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 0d706c52..10ed1f6d 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -39,6 +39,32 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh,
39 } 39 }
40} 40}
41 41
42/**
43 * Checks whether a surface at (lx, ly) intersects an output. If `box` is not
44 * NULL, it populates it with the surface box in the output, in output-local
45 * coordinates.
46 */
47static bool surface_intersect_output(struct wlr_surface *surface,
48 struct wlr_output_layout *output_layout, struct wlr_output *wlr_output,
49 double lx, double ly, float rotation, struct wlr_box *box) {
50 double ox = lx, oy = ly;
51 wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy);
52
53 if (box != NULL) {
54 box->x = ox * wlr_output->scale;
55 box->y = oy * wlr_output->scale;
56 box->width = surface->current->width * wlr_output->scale;
57 box->height = surface->current->height * wlr_output->scale;
58 }
59
60 struct wlr_box layout_box = {
61 .x = lx, .y = ly,
62 .width = surface->current->width, .height = surface->current->height,
63 };
64 wlr_box_rotated_bounds(&layout_box, rotation, &layout_box);
65 return wlr_output_layout_intersects(output_layout, wlr_output, &layout_box);
66}
67
42static void render_surface(struct wlr_surface *surface, 68static void render_surface(struct wlr_surface *surface,
43 struct wlr_output *wlr_output, struct timespec *when, 69 struct wlr_output *wlr_output, struct timespec *when,
44 double lx, double ly, float rotation) { 70 double lx, double ly, float rotation) {
@@ -48,29 +74,21 @@ static void render_surface(struct wlr_surface *surface,
48 if (!wlr_surface_has_buffer(surface)) { 74 if (!wlr_surface_has_buffer(surface)) {
49 return; 75 return;
50 } 76 }
77
51 struct wlr_output_layout *layout = root_container.sway_root->output_layout; 78 struct wlr_output_layout *layout = root_container.sway_root->output_layout;
52 int width = surface->current->width; 79
53 int height = surface->current->height; 80 struct wlr_box box;
54 int render_width = width * wlr_output->scale; 81 bool intersects = surface_intersect_output(surface, layout, wlr_output,
55 int render_height = height * wlr_output->scale; 82 lx, ly, rotation, &box);
56 int owidth, oheight; 83 if (intersects) {
57 wlr_output_effective_resolution(wlr_output, &owidth, &oheight);
58
59 // FIXME: view coords are inconsistently assumed to be in output or layout coords
60 struct wlr_box layout_box = {
61 .x = lx + wlr_output->lx, .y = ly + wlr_output->ly,
62 .width = render_width, .height = render_height,
63 };
64 if (wlr_output_layout_intersects(layout, wlr_output, &layout_box)) {
65 struct wlr_box render_box = {
66 .x = lx, .y = ly,
67 .width = render_width, .height = render_height
68 };
69 float matrix[9]; 84 float matrix[9];
70 wlr_matrix_project_box(matrix, &render_box, surface->current->transform, 85 enum wl_output_transform transform =
71 0, wlr_output->transform_matrix); 86 wlr_output_transform_invert(surface->current->transform);
72 wlr_render_texture_with_matrix(renderer, surface->texture, matrix, 87 wlr_matrix_project_box(matrix, &box, transform, rotation,
73 1.0f); // TODO: configurable alpha 88 wlr_output->transform_matrix);
89
90 // TODO: configurable alpha
91 wlr_render_texture_with_matrix(renderer, surface->texture, matrix, 1.0f);
74 92
75 wlr_surface_send_frame_done(surface, when); 93 wlr_surface_send_frame_done(surface, when);
76 } 94 }
@@ -80,9 +98,8 @@ static void render_surface(struct wlr_surface *surface,
80 struct wlr_surface_state *state = subsurface->surface->current; 98 struct wlr_surface_state *state = subsurface->surface->current;
81 double sx = state->subsurface_position.x; 99 double sx = state->subsurface_position.x;
82 double sy = state->subsurface_position.y; 100 double sy = state->subsurface_position.y;
83 double sw = state->buffer_width / state->scale; 101 rotate_child_position(&sx, &sy, state->width, state->height,
84 double sh = state->buffer_height / state->scale; 102 surface->current->width, surface->current->height, rotation);
85 rotate_child_position(&sx, &sy, sw, sh, width, height, rotation);
86 103
87 render_surface(subsurface->surface, wlr_output, when, 104 render_surface(subsurface->surface, wlr_output, when,
88 lx + sx, ly + sy, rotation); 105 lx + sx, ly + sy, rotation);
@@ -228,10 +245,13 @@ static void render_output(struct sway_output *output, struct timespec *when,
228 245
229 struct sway_seat *seat = input_manager_current_seat(input_manager); 246 struct sway_seat *seat = input_manager_current_seat(input_manager);
230 struct sway_container *focus = 247 struct sway_container *focus =
231 sway_seat_get_focus_inactive(seat, output->swayc); 248 seat_get_focus_inactive(seat, output->swayc);
232 struct sway_container *workspace = (focus->type == C_WORKSPACE ? 249 if (!focus) {
233 focus : 250 // We've never been to this output before
234 container_parent(focus, C_WORKSPACE)); 251 focus = output->swayc->children->items[0];
252 }
253 struct sway_container *workspace = focus->type == C_WORKSPACE ?
254 focus : container_parent(focus, C_WORKSPACE);
235 255
236 struct render_data rdata = { 256 struct render_data rdata = {
237 .output = output, 257 .output = output,
@@ -240,15 +260,15 @@ static void render_output(struct sway_output *output, struct timespec *when,
240 container_descendants(workspace, C_VIEW, render_view, &rdata); 260 container_descendants(workspace, C_VIEW, render_view, &rdata);
241 261
242 // render unmanaged views on top 262 // render unmanaged views on top
243 struct sway_view *view; 263 struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged;
244 wl_list_for_each(view, &root_container.sway_root->unmanaged_views, 264 struct sway_xwayland_unmanaged *sway_surface;
245 unmanaged_view_link) { 265 wl_list_for_each(sway_surface, unmanaged, link) {
246 if (view->type != SWAY_XWAYLAND_VIEW) { 266 struct wlr_xwayland_surface *xsurface =
267 sway_surface->wlr_xwayland_surface;
268 if (xsurface->surface == NULL) {
247 continue; 269 continue;
248 } 270 }
249 271
250 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
251
252 const struct wlr_box view_box = { 272 const struct wlr_box view_box = {
253 .x = xsurface->x, 273 .x = xsurface->x,
254 .y = xsurface->y, 274 .y = xsurface->y,
@@ -260,7 +280,7 @@ static void render_output(struct sway_output *output, struct timespec *when,
260 continue; 280 continue;
261 } 281 }
262 282
263 render_surface(view->surface, wlr_output, &output->last_frame, 283 render_surface(xsurface->surface, wlr_output, &output->last_frame,
264 view_box.x - output_box->x, view_box.y - output_box->y, 0); 284 view_box.x - output_box->x, view_box.y - output_box->y, 0);
265 } 285 }
266 286
@@ -338,6 +358,12 @@ static void handle_transform(struct wl_listener *listener, void *data) {
338 arrange_windows(output->swayc, -1, -1); 358 arrange_windows(output->swayc, -1, -1);
339} 359}
340 360
361static void handle_scale(struct wl_listener *listener, void *data) {
362 struct sway_output *output = wl_container_of(listener, output, scale);
363 arrange_layers(output);
364 arrange_windows(output->swayc, -1, -1);
365}
366
341void handle_new_output(struct wl_listener *listener, void *data) { 367void handle_new_output(struct wl_listener *listener, void *data) {
342 struct sway_server *server = wl_container_of(listener, server, new_output); 368 struct sway_server *server = wl_container_of(listener, server, new_output);
343 struct wlr_output *wlr_output = data; 369 struct wlr_output *wlr_output = data;
@@ -370,7 +396,7 @@ void handle_new_output(struct wl_listener *listener, void *data) {
370 wl_list_init(&output->layers[i]); 396 wl_list_init(&output->layers[i]);
371 } 397 }
372 398
373 sway_input_manager_configure_xcursor(input_manager); 399 input_manager_configure_xcursor(input_manager);
374 400
375 wl_signal_add(&wlr_output->events.destroy, &output->destroy); 401 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
376 output->destroy.notify = handle_destroy; 402 output->destroy.notify = handle_destroy;
@@ -378,6 +404,8 @@ void handle_new_output(struct wl_listener *listener, void *data) {
378 output->mode.notify = handle_mode; 404 output->mode.notify = handle_mode;
379 wl_signal_add(&wlr_output->events.transform, &output->transform); 405 wl_signal_add(&wlr_output->events.transform, &output->transform);
380 output->transform.notify = handle_transform; 406 output->transform.notify = handle_transform;
407 wl_signal_add(&wlr_output->events.scale, &output->scale);
408 output->scale.notify = handle_scale;
381 409
382 wl_signal_add(&output->damage->events.frame, &output->damage_frame); 410 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
383 output->damage_frame.notify = damage_handle_frame; 411 output->damage_frame.notify = damage_handle_frame;
diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c
index 4fcc6317..6528a397 100644
--- a/sway/desktop/wl_shell.c
+++ b/sway/desktop/wl_shell.c
@@ -30,28 +30,18 @@ static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
30 } 30 }
31} 31}
32 32
33static void set_size(struct sway_view *view, int width, int height) { 33static void configure(struct sway_view *view, double ox, double oy, int width,
34 int height) {
34 if (!assert_wl_shell(view)) { 35 if (!assert_wl_shell(view)) {
35 return; 36 return;
36 } 37 }
38 view_update_position(view, ox, oy);
37 view->sway_wl_shell_surface->pending_width = width; 39 view->sway_wl_shell_surface->pending_width = width;
38 view->sway_wl_shell_surface->pending_height = height; 40 view->sway_wl_shell_surface->pending_height = height;
39 wlr_wl_shell_surface_configure(view->wlr_wl_shell_surface, 0, width, height); 41 wlr_wl_shell_surface_configure(view->wlr_wl_shell_surface, 0, width, height);
40} 42}
41 43
42static void set_position(struct sway_view *view, double ox, double oy) { 44static void _close(struct sway_view *view) {
43 if (!assert_wl_shell(view)) {
44 return;
45 }
46 view->swayc->x = ox;
47 view->swayc->y = oy;
48}
49
50static void set_activated(struct sway_view *view, bool activated) {
51 // no way to activate wl_shell
52}
53
54static void close(struct sway_view *view) {
55 if (!assert_wl_shell(view)) { 45 if (!assert_wl_shell(view)) {
56 return; 46 return;
57 } 47 }
@@ -59,14 +49,20 @@ static void close(struct sway_view *view) {
59 wl_client_destroy(view->wlr_wl_shell_surface->client); 49 wl_client_destroy(view->wlr_wl_shell_surface->client);
60} 50}
61 51
52static const struct sway_view_impl view_impl = {
53 .get_prop = get_prop,
54 .configure = configure,
55 .close = _close,
56};
57
62static void handle_commit(struct wl_listener *listener, void *data) { 58static void handle_commit(struct wl_listener *listener, void *data) {
63 struct sway_wl_shell_surface *sway_surface = 59 struct sway_wl_shell_surface *sway_surface =
64 wl_container_of(listener, sway_surface, commit); 60 wl_container_of(listener, sway_surface, commit);
65 struct sway_view *view = sway_surface->view; 61 struct sway_view *view = sway_surface->view;
66 // NOTE: We intentionally discard the view's desired width here 62 // NOTE: We intentionally discard the view's desired width here
67 // TODO: Let floating views do whatever 63 // TODO: Let floating views do whatever
68 view->width = sway_surface->pending_width; 64 view_update_size(view, sway_surface->pending_width,
69 view->height = sway_surface->pending_height; 65 sway_surface->pending_height);
70 view_damage_from(view); 66 view_damage_from(view);
71} 67}
72 68
@@ -75,15 +71,13 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
75 wl_container_of(listener, sway_surface, destroy); 71 wl_container_of(listener, sway_surface, destroy);
76 wl_list_remove(&sway_surface->commit.link); 72 wl_list_remove(&sway_surface->commit.link);
77 wl_list_remove(&sway_surface->destroy.link); 73 wl_list_remove(&sway_surface->destroy.link);
78 struct sway_container *parent = container_view_destroy(sway_surface->view->swayc); 74 view_destroy(sway_surface->view);
79 free(sway_surface->view);
80 free(sway_surface); 75 free(sway_surface);
81 arrange_windows(parent, -1, -1);
82} 76}
83 77
84void handle_wl_shell_surface(struct wl_listener *listener, void *data) { 78void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
85 struct sway_server *server = wl_container_of( 79 struct sway_server *server = wl_container_of(listener, server,
86 listener, server, wl_shell_surface); 80 wl_shell_surface);
87 struct wlr_wl_shell_surface *shell_surface = data; 81 struct wlr_wl_shell_surface *shell_surface = data;
88 82
89 if (shell_surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) { 83 if (shell_surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) {
@@ -103,20 +97,13 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
103 return; 97 return;
104 } 98 }
105 99
106 struct sway_view *sway_view = calloc(1, sizeof(struct sway_view)); 100 struct sway_view *view = view_create(SWAY_WL_SHELL_VIEW, &view_impl);
107 if (!sway_assert(sway_view, "Failed to allocate view!")) { 101 if (!sway_assert(view, "Failed to allocate view")) {
108 return; 102 return;
109 } 103 }
110 sway_view->type = SWAY_WL_SHELL_VIEW; 104 view->wlr_wl_shell_surface = shell_surface;
111 sway_view->iface.get_prop = get_prop; 105 view->sway_wl_shell_surface = sway_surface;
112 sway_view->iface.set_size = set_size; 106 sway_surface->view = view;
113 sway_view->iface.set_position = set_position;
114 sway_view->iface.set_activated = set_activated;
115 sway_view->iface.close = close;
116 sway_view->wlr_wl_shell_surface = shell_surface;
117 sway_view->sway_wl_shell_surface = sway_surface;
118 sway_view->surface = shell_surface->surface;
119 sway_surface->view = sway_view;
120 107
121 // TODO: 108 // TODO:
122 // - Wire up listeners 109 // - Wire up listeners
@@ -132,11 +119,5 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
132 sway_surface->destroy.notify = handle_destroy; 119 sway_surface->destroy.notify = handle_destroy;
133 wl_signal_add(&shell_surface->events.destroy, &sway_surface->destroy); 120 wl_signal_add(&shell_surface->events.destroy, &sway_surface->destroy);
134 121
135 struct sway_seat *seat = input_manager_current_seat(input_manager); 122 view_map(view, shell_surface->surface);
136 struct sway_container *focus = sway_seat_get_focus_inactive(seat, &root_container);
137 struct sway_container *cont = container_view_create(focus, sway_view);
138 sway_view->swayc = cont;
139
140 arrange_windows(cont->parent, -1, -1);
141 sway_input_manager_set_focus(input_manager, cont);
142} 123}
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 713437f2..49305b39 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -30,23 +30,18 @@ static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
30 } 30 }
31} 31}
32 32
33static void set_size(struct sway_view *view, int width, int height) { 33static void configure(struct sway_view *view, double ox, double oy, int width,
34 int height) {
34 if (!assert_xdg(view)) { 35 if (!assert_xdg(view)) {
35 return; 36 return;
36 } 37 }
38
39 view_update_position(view, ox, oy);
37 view->sway_xdg_surface_v6->pending_width = width; 40 view->sway_xdg_surface_v6->pending_width = width;
38 view->sway_xdg_surface_v6->pending_height = height; 41 view->sway_xdg_surface_v6->pending_height = height;
39 wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height); 42 wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height);
40} 43}
41 44
42static void set_position(struct sway_view *view, double ox, double oy) {
43 if (!assert_xdg(view)) {
44 return;
45 }
46 view->swayc->x = ox;
47 view->swayc->y = oy;
48}
49
50static void set_activated(struct sway_view *view, bool activated) { 45static void set_activated(struct sway_view *view, bool activated) {
51 if (!assert_xdg(view)) { 46 if (!assert_xdg(view)) {
52 return; 47 return;
@@ -57,7 +52,7 @@ static void set_activated(struct sway_view *view, bool activated) {
57 } 52 }
58} 53}
59 54
60static void close(struct sway_view *view) { 55static void _close(struct sway_view *view) {
61 if (!assert_xdg(view)) { 56 if (!assert_xdg(view)) {
62 return; 57 return;
63 } 58 }
@@ -67,6 +62,13 @@ static void close(struct sway_view *view) {
67 } 62 }
68} 63}
69 64
65static const struct sway_view_impl view_impl = {
66 .get_prop = get_prop,
67 .configure = configure,
68 .set_activated = set_activated,
69 .close = _close,
70};
71
70static void handle_commit(struct wl_listener *listener, void *data) { 72static void handle_commit(struct wl_listener *listener, void *data) {
71 struct sway_xdg_surface_v6 *sway_surface = 73 struct sway_xdg_surface_v6 *sway_surface =
72 wl_container_of(listener, sway_surface, commit); 74 wl_container_of(listener, sway_surface, commit);
@@ -74,37 +76,22 @@ static void handle_commit(struct wl_listener *listener, void *data) {
74 // NOTE: We intentionally discard the view's desired width here 76 // NOTE: We intentionally discard the view's desired width here
75 // TODO: Store this for restoration when moving to floating plane 77 // TODO: Store this for restoration when moving to floating plane
76 // TODO: Let floating views do whatever 78 // TODO: Let floating views do whatever
77 view->width = sway_surface->pending_width; 79 view_update_size(view, sway_surface->pending_width,
78 view->height = sway_surface->pending_height; 80 sway_surface->pending_height);
79 view_damage_from(view); 81 view_damage_from(view);
80} 82}
81 83
82static void handle_unmap(struct wl_listener *listener, void *data) { 84static void handle_unmap(struct wl_listener *listener, void *data) {
83 struct sway_xdg_surface_v6 *sway_surface = 85 struct sway_xdg_surface_v6 *sway_surface =
84 wl_container_of(listener, sway_surface, unmap); 86 wl_container_of(listener, sway_surface, unmap);
85 view_damage_whole(sway_surface->view); 87 view_unmap(sway_surface->view);
86 container_view_destroy(sway_surface->view->swayc);
87 sway_surface->view->swayc = NULL;
88 sway_surface->view->surface = NULL;
89} 88}
90 89
91static void handle_map(struct wl_listener *listener, void *data) { 90static void handle_map(struct wl_listener *listener, void *data) {
92 struct sway_xdg_surface_v6 *sway_surface = 91 struct sway_xdg_surface_v6 *sway_surface =
93 wl_container_of(listener, sway_surface, map); 92 wl_container_of(listener, sway_surface, map);
94 struct sway_view *view = sway_surface->view; 93 struct sway_view *view = sway_surface->view;
95 94 view_map(view, view->wlr_xdg_surface_v6->surface);
96 sway_surface->view->surface = view->wlr_xdg_surface_v6->surface;
97
98 container_view_destroy(view->swayc);
99
100 struct sway_seat *seat = input_manager_current_seat(input_manager);
101 struct sway_container *focus = sway_seat_get_focus_inactive(seat, &root_container);
102 struct sway_container *cont = container_view_create(focus, view);
103 view->swayc = cont;
104 arrange_windows(cont->parent, -1, -1);
105 sway_input_manager_set_focus(input_manager, cont);
106
107 view_damage_whole(sway_surface->view);
108} 95}
109 96
110static void handle_destroy(struct wl_listener *listener, void *data) { 97static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -112,8 +99,9 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
112 wl_container_of(listener, sway_xdg_surface, destroy); 99 wl_container_of(listener, sway_xdg_surface, destroy);
113 wl_list_remove(&sway_xdg_surface->commit.link); 100 wl_list_remove(&sway_xdg_surface->commit.link);
114 wl_list_remove(&sway_xdg_surface->destroy.link); 101 wl_list_remove(&sway_xdg_surface->destroy.link);
115 container_view_destroy(sway_xdg_surface->view->swayc); 102 wl_list_remove(&sway_xdg_surface->map.link);
116 free(sway_xdg_surface->view); 103 wl_list_remove(&sway_xdg_surface->unmap.link);
104 view_destroy(sway_xdg_surface->view);
117 free(sway_xdg_surface); 105 free(sway_xdg_surface);
118} 106}
119 107
@@ -138,23 +126,16 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
138 return; 126 return;
139 } 127 }
140 128
141 struct sway_view *sway_view = calloc(1, sizeof(struct sway_view)); 129 struct sway_view *view = view_create(SWAY_XDG_SHELL_V6_VIEW, &view_impl);
142 if (!sway_assert(sway_view, "Failed to allocate view!")) { 130 if (!sway_assert(view, "Failed to allocate view")) {
143 return; 131 return;
144 } 132 }
145 sway_view->type = SWAY_XDG_SHELL_V6_VIEW; 133 view->wlr_xdg_surface_v6 = xdg_surface;
146 sway_view->iface.get_prop = get_prop; 134 view->sway_xdg_surface_v6 = sway_surface;
147 sway_view->iface.set_size = set_size; 135 sway_surface->view = view;
148 sway_view->iface.set_position = set_position;
149 sway_view->iface.set_activated = set_activated;
150 sway_view->iface.close = close;
151 sway_view->wlr_xdg_surface_v6 = xdg_surface;
152 sway_view->sway_xdg_surface_v6 = sway_surface;
153 sway_surface->view = sway_view;
154 136
155 // TODO: 137 // TODO:
156 // - Look up pid and open on appropriate workspace 138 // - Look up pid and open on appropriate workspace
157 // - Set new view to maximized so it behaves nicely
158 // - Criteria 139 // - Criteria
159 140
160 sway_surface->commit.notify = handle_commit; 141 sway_surface->commit.notify = handle_commit;
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 273ca2bf..bfef68cf 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -14,6 +14,33 @@
14#include "sway/input/input-manager.h" 14#include "sway/input/input-manager.h"
15#include "log.h" 15#include "log.h"
16 16
17static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
18 struct sway_xwayland_unmanaged *sway_surface =
19 wl_container_of(listener, sway_surface, destroy);
20 wl_list_remove(&sway_surface->destroy.link);
21 wl_list_remove(&sway_surface->link);
22 free(sway_surface);
23}
24
25static void create_unmanaged(struct wlr_xwayland_surface *xsurface) {
26 struct sway_xwayland_unmanaged *sway_surface =
27 calloc(1, sizeof(struct sway_xwayland_unmanaged));
28 if (!sway_assert(sway_surface, "Failed to allocate surface")) {
29 return;
30 }
31
32 sway_surface->wlr_xwayland_surface = xsurface;
33
34 wl_signal_add(&xsurface->events.destroy, &sway_surface->destroy);
35 sway_surface->destroy.notify = unmanaged_handle_destroy;
36
37 wl_list_insert(&root_container.sway_root->xwayland_unmanaged,
38 &sway_surface->link);
39
40 // TODO: damage tracking
41}
42
43
17static bool assert_xwayland(struct sway_view *view) { 44static bool assert_xwayland(struct sway_view *view) {
18 return sway_assert(view->type == SWAY_XWAYLAND_VIEW, 45 return sway_assert(view->type == SWAY_XWAYLAND_VIEW,
19 "Expected xwayland view!"); 46 "Expected xwayland view!");
@@ -33,22 +60,13 @@ static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
33 } 60 }
34} 61}
35 62
36static void set_size(struct sway_view *view, int width, int height) { 63static void configure(struct sway_view *view, double ox, double oy, int width,
64 int height) {
37 if (!assert_xwayland(view)) { 65 if (!assert_xwayland(view)) {
38 return; 66 return;
39 } 67 }
40 view->sway_xwayland_surface->pending_width = width;
41 view->sway_xwayland_surface->pending_height = height;
42
43 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 68 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
44 wlr_xwayland_surface_configure(xsurface, xsurface->x, xsurface->y,
45 width, height);
46}
47 69
48static void set_position(struct sway_view *view, double ox, double oy) {
49 if (!assert_xwayland(view)) {
50 return;
51 }
52 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 70 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
53 if (!sway_assert(output, "view must be within tree to set position")) { 71 if (!sway_assert(output, "view must be within tree to set position")) {
54 return; 72 return;
@@ -64,13 +82,12 @@ static void set_position(struct sway_view *view, double ox, double oy) {
64 return; 82 return;
65 } 83 }
66 84
67 view->swayc->x = ox; 85 view_update_position(view, ox, oy);
68 view->swayc->y = oy;
69 86
70 wlr_xwayland_surface_configure(view->wlr_xwayland_surface, 87 view->sway_xwayland_surface->pending_width = width;
71 ox + loutput->x, oy + loutput->y, 88 view->sway_xwayland_surface->pending_height = height;
72 view->wlr_xwayland_surface->width, 89 wlr_xwayland_surface_configure(xsurface, ox + loutput->x, oy + loutput->y,
73 view->wlr_xwayland_surface->height); 90 width, height);
74} 91}
75 92
76static void set_activated(struct sway_view *view, bool activated) { 93static void set_activated(struct sway_view *view, bool activated) {
@@ -81,77 +98,58 @@ static void set_activated(struct sway_view *view, bool activated) {
81 wlr_xwayland_surface_activate(surface, activated); 98 wlr_xwayland_surface_activate(surface, activated);
82} 99}
83 100
84static void close_view(struct sway_view *view) { 101static void _close(struct sway_view *view) {
85 if (!assert_xwayland(view)) { 102 if (!assert_xwayland(view)) {
86 return; 103 return;
87 } 104 }
88 wlr_xwayland_surface_close(view->wlr_xwayland_surface); 105 wlr_xwayland_surface_close(view->wlr_xwayland_surface);
89} 106}
90 107
108static const struct sway_view_impl view_impl = {
109 .get_prop = get_prop,
110 .configure = configure,
111 .set_activated = set_activated,
112 .close = _close,
113};
114
91static void handle_commit(struct wl_listener *listener, void *data) { 115static void handle_commit(struct wl_listener *listener, void *data) {
92 struct sway_xwayland_surface *sway_surface = 116 struct sway_xwayland_surface *sway_surface =
93 wl_container_of(listener, sway_surface, commit); 117 wl_container_of(listener, sway_surface, commit);
94 struct sway_view *view = sway_surface->view; 118 struct sway_view *view = sway_surface->view;
95 // NOTE: We intentionally discard the view's desired width here 119 // NOTE: We intentionally discard the view's desired width here
96 // TODO: Let floating views do whatever 120 // TODO: Let floating views do whatever
97 view->width = sway_surface->pending_width; 121 view_update_size(view, sway_surface->pending_width,
98 view->height = sway_surface->pending_height; 122 sway_surface->pending_height);
99 view_damage_from(view); 123 view_damage_from(view);
100} 124}
101 125
102static void handle_destroy(struct wl_listener *listener, void *data) { 126static void handle_destroy(struct wl_listener *listener, void *data) {
103 struct sway_xwayland_surface *sway_surface = 127 struct sway_xwayland_surface *sway_surface =
104 wl_container_of(listener, sway_surface, destroy); 128 wl_container_of(listener, sway_surface, destroy);
105
106 wl_list_remove(&sway_surface->commit.link); 129 wl_list_remove(&sway_surface->commit.link);
107 wl_list_remove(&sway_surface->destroy.link); 130 wl_list_remove(&sway_surface->destroy.link);
108 wl_list_remove(&sway_surface->request_configure.link); 131 wl_list_remove(&sway_surface->request_configure.link);
109 wl_list_remove(&sway_surface->view->unmanaged_view_link); 132 wl_list_remove(&sway_surface->map.link);
110 container_view_destroy(sway_surface->view->swayc); 133 wl_list_remove(&sway_surface->unmap.link);
111 sway_surface->view->swayc = NULL; 134 view_destroy(sway_surface->view);
112 sway_surface->view->surface = NULL; 135 free(sway_surface);
113} 136}
114 137
115static void handle_unmap(struct wl_listener *listener, void *data) { 138static void handle_unmap(struct wl_listener *listener, void *data) {
116 struct sway_xwayland_surface *sway_surface = 139 struct sway_xwayland_surface *sway_surface =
117 wl_container_of(listener, sway_surface, unmap); 140 wl_container_of(listener, sway_surface, unmap);
118 view_damage_whole(sway_surface->view); 141 view_unmap(sway_surface->view);
119 wl_list_remove(&sway_surface->view->unmanaged_view_link);
120 wl_list_init(&sway_surface->view->unmanaged_view_link);
121 container_view_destroy(sway_surface->view->swayc);
122 sway_surface->view->swayc = NULL;
123 sway_surface->view->surface = NULL;
124} 142}
125 143
126static void handle_map(struct wl_listener *listener, void *data) { 144static void handle_map(struct wl_listener *listener, void *data) {
127 struct sway_xwayland_surface *sway_surface = 145 struct sway_xwayland_surface *sway_surface =
128 wl_container_of(listener, sway_surface, map); 146 wl_container_of(listener, sway_surface, map);
129 struct wlr_xwayland_surface *xsurface = data; 147 struct wlr_xwayland_surface *xsurface = data;
130 148 struct sway_view *view = sway_surface->view;
131 sway_surface->view->surface = xsurface->surface;
132 149
133 // put it back into the tree 150 // put it back into the tree
134 if (wlr_xwayland_surface_is_unmanaged(xsurface) || 151 wlr_xwayland_surface_set_maximized(xsurface, true);
135 xsurface->override_redirect) { 152 view_map(view, xsurface->surface);
136 wl_list_remove(&sway_surface->view->unmanaged_view_link);
137 wl_list_insert(&root_container.sway_root->unmanaged_views,
138 &sway_surface->view->unmanaged_view_link);
139 } else {
140 struct sway_view *view = sway_surface->view;
141 container_view_destroy(view->swayc);
142
143 wlr_xwayland_surface_set_maximized(xsurface, true);
144
145 struct sway_seat *seat = input_manager_current_seat(input_manager);
146 struct sway_container *focus = sway_seat_get_focus_inactive(seat,
147 &root_container);
148 struct sway_container *cont = container_view_create(focus, view);
149 view->swayc = cont;
150 arrange_windows(cont->parent, -1, -1);
151 sway_input_manager_set_focus(input_manager, cont);
152 }
153
154 view_damage_whole(sway_surface->view);
155} 153}
156 154
157static void handle_request_configure(struct wl_listener *listener, void *data) { 155static void handle_request_configure(struct wl_listener *listener, void *data) {
@@ -171,34 +169,32 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
171 listener, server, xwayland_surface); 169 listener, server, xwayland_surface);
172 struct wlr_xwayland_surface *xsurface = data; 170 struct wlr_xwayland_surface *xsurface = data;
173 171
172 if (wlr_xwayland_surface_is_unmanaged(xsurface) ||
173 xsurface->override_redirect) {
174 wlr_log(L_DEBUG, "New xwayland unmanaged surface");
175 create_unmanaged(xsurface);
176 return;
177 }
178
174 wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'", 179 wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'",
175 xsurface->title, xsurface->class); 180 xsurface->title, xsurface->class);
176 181
177 struct sway_xwayland_surface *sway_surface = 182 struct sway_xwayland_surface *sway_surface =
178 calloc(1, sizeof(struct sway_xwayland_surface)); 183 calloc(1, sizeof(struct sway_xwayland_surface));
179 if (!sway_assert(sway_surface, "Failed to allocate surface!")) { 184 if (!sway_assert(sway_surface, "Failed to allocate surface")) {
180 return; 185 return;
181 } 186 }
182 187
183 struct sway_view *sway_view = calloc(1, sizeof(struct sway_view)); 188 struct sway_view *view = view_create(SWAY_XWAYLAND_VIEW, &view_impl);
184 if (!sway_assert(sway_view, "Failed to allocate view!")) { 189 if (!sway_assert(view, "Failed to allocate view")) {
185 return; 190 return;
186 } 191 }
187 sway_view->type = SWAY_XWAYLAND_VIEW; 192 view->wlr_xwayland_surface = xsurface;
188 sway_view->iface.get_prop = get_prop; 193 view->sway_xwayland_surface = sway_surface;
189 sway_view->iface.set_size = set_size; 194 sway_surface->view = view;
190 sway_view->iface.set_position = set_position;
191 sway_view->iface.set_activated = set_activated;
192 sway_view->iface.close = close_view;
193 sway_view->wlr_xwayland_surface = xsurface;
194 sway_view->sway_xwayland_surface = sway_surface;
195 sway_surface->view = sway_view;
196
197 wl_list_init(&sway_view->unmanaged_view_link);
198 195
199 // TODO: 196 // TODO:
200 // - Look up pid and open on appropriate workspace 197 // - Look up pid and open on appropriate workspace
201 // - Set new view to maximized so it behaves nicely
202 // - Criteria 198 // - Criteria
203 199
204 wl_signal_add(&xsurface->surface->events.commit, &sway_surface->commit); 200 wl_signal_add(&xsurface->surface->events.commit, &sway_surface->commit);
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index d608a9cf..9229e92d 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -47,14 +47,15 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output,
47static struct sway_container *container_at_cursor(struct sway_cursor *cursor, 47static struct sway_container *container_at_cursor(struct sway_cursor *cursor,
48 struct wlr_surface **surface, double *sx, double *sy) { 48 struct wlr_surface **surface, double *sx, double *sy) {
49 // check for unmanaged views first 49 // check for unmanaged views first
50 struct wl_list *unmanaged = &root_container.sway_root->unmanaged_views; 50 struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged;
51 struct sway_view *view; 51 struct sway_xwayland_unmanaged *sway_surface;
52 wl_list_for_each_reverse(view, unmanaged, unmanaged_view_link) { 52 wl_list_for_each_reverse(sway_surface, unmanaged, link) {
53 if (view->type != SWAY_XWAYLAND_VIEW) { 53 struct wlr_xwayland_surface *xsurface =
54 sway_surface->wlr_xwayland_surface;
55 if (xsurface->surface == NULL) {
54 continue; 56 continue;
55 } 57 }
56 58
57 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
58 struct wlr_box box = { 59 struct wlr_box box = {
59 .x = xsurface->x, 60 .x = xsurface->x,
60 .y = xsurface->y, 61 .y = xsurface->y,
@@ -84,7 +85,7 @@ static struct sway_container *container_at_cursor(struct sway_cursor *cursor,
84 85
85 // find the focused workspace on the output for this seat 86 // find the focused workspace on the output for this seat
86 struct sway_container *ws = 87 struct sway_container *ws =
87 sway_seat_get_focus_inactive(cursor->seat, output->swayc); 88 seat_get_focus_inactive(cursor->seat, output->swayc);
88 if (ws && ws->type != C_WORKSPACE) { 89 if (ws && ws->type != C_WORKSPACE) {
89 ws = container_parent(ws, C_WORKSPACE); 90 ws = container_parent(ws, C_WORKSPACE);
90 } 91 }
@@ -129,7 +130,7 @@ static void cursor_send_pointer_motion(struct sway_cursor *cursor,
129 double sx, sy; 130 double sx, sy;
130 struct sway_container *c = container_at_cursor(cursor, &surface, &sx, &sy); 131 struct sway_container *c = container_at_cursor(cursor, &surface, &sx, &sy);
131 if (c && config->focus_follows_mouse) { 132 if (c && config->focus_follows_mouse) {
132 sway_seat_set_focus_warp(cursor->seat, c, false); 133 seat_set_focus_warp(cursor->seat, c, false);
133 } 134 }
134 135
135 // reset cursor if switching between clients 136 // reset cursor if switching between clients
@@ -179,27 +180,32 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
179 double sx, sy; 180 double sx, sy;
180 struct sway_container *cont = 181 struct sway_container *cont =
181 container_at_cursor(cursor, &surface, &sx, &sy); 182 container_at_cursor(cursor, &surface, &sx, &sy);
183 if (surface && wlr_surface_is_layer_surface(surface)) {
184 struct wlr_layer_surface *layer =
185 wlr_layer_surface_from_wlr_surface(surface);
186 if (layer->current.keyboard_interactive) {
187 seat_set_focus_layer(cursor->seat, layer);
188 return;
189 }
190 }
182 // Avoid moving keyboard focus from a surface that accepts it to one 191 // Avoid moving keyboard focus from a surface that accepts it to one
183 // that does not unless the change would move us to a new workspace. 192 // that does not unless the change would move us to a new workspace.
184 // 193 //
185 // This prevents, for example, losing focus when clicking on swaybar. 194 // This prevents, for example, losing focus when clicking on swaybar.
186 //
187 // TODO: Replace this condition with something like
188 // !surface_accepts_keyboard_input
189 if (surface && cont && cont->type != C_VIEW) { 195 if (surface && cont && cont->type != C_VIEW) {
190 struct sway_container *new_ws = cont; 196 struct sway_container *new_ws = cont;
191 if (new_ws && new_ws->type != C_WORKSPACE) { 197 if (new_ws && new_ws->type != C_WORKSPACE) {
192 new_ws = container_parent(new_ws, C_WORKSPACE); 198 new_ws = container_parent(new_ws, C_WORKSPACE);
193 } 199 }
194 struct sway_container *old_ws = sway_seat_get_focus(cursor->seat); 200 struct sway_container *old_ws = seat_get_focus(cursor->seat);
195 if (old_ws && old_ws->type != C_WORKSPACE) { 201 if (old_ws && old_ws->type != C_WORKSPACE) {
196 old_ws = container_parent(old_ws, C_WORKSPACE); 202 old_ws = container_parent(old_ws, C_WORKSPACE);
197 } 203 }
198 if (new_ws != old_ws) { 204 if (new_ws != old_ws) {
199 sway_seat_set_focus(cursor->seat, cont); 205 seat_set_focus(cursor->seat, cont);
200 } 206 }
201 } else { 207 } else {
202 sway_seat_set_focus(cursor->seat, cont); 208 seat_set_focus(cursor->seat, cont);
203 } 209 }
204 210
205 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, event->time_msec, 211 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, event->time_msec,
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index d421a03f..c3507f65 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -26,7 +26,7 @@ struct seat_config *current_seat_config = NULL;
26struct sway_seat *input_manager_current_seat(struct sway_input_manager *input) { 26struct sway_seat *input_manager_current_seat(struct sway_input_manager *input) {
27 struct sway_seat *seat = config->handler_context.seat; 27 struct sway_seat *seat = config->handler_context.seat;
28 if (!seat) { 28 if (!seat) {
29 seat = sway_input_manager_get_default_seat(input_manager); 29 seat = input_manager_get_default_seat(input_manager);
30 } 30 }
31 return seat; 31 return seat;
32} 32}
@@ -40,7 +40,7 @@ struct sway_seat *input_manager_get_seat(
40 } 40 }
41 } 41 }
42 42
43 return sway_seat_create(input, seat_name); 43 return seat_create(input, seat_name);
44} 44}
45 45
46static char *get_device_identifier(struct wlr_input_device *device) { 46static char *get_device_identifier(struct wlr_input_device *device) {
@@ -83,7 +83,8 @@ static struct sway_input_device *input_sway_device_from_wlr(
83static bool input_has_seat_configuration(struct sway_input_manager *input) { 83static bool input_has_seat_configuration(struct sway_input_manager *input) {
84 struct sway_seat *seat = NULL; 84 struct sway_seat *seat = NULL;
85 wl_list_for_each(seat, &input->seats, link) { 85 wl_list_for_each(seat, &input->seats, link) {
86 if (seat->config) { 86 struct seat_config *seat_config = seat_get_config(seat);
87 if (seat_config) {
87 return true; 88 return true;
88 } 89 }
89 } 90 }
@@ -91,9 +92,10 @@ static bool input_has_seat_configuration(struct sway_input_manager *input) {
91 return false; 92 return false;
92} 93}
93 94
94static void sway_input_manager_libinput_config_pointer(struct sway_input_device *input_device) { 95static void input_manager_libinput_config_pointer(
96 struct sway_input_device *input_device) {
95 struct wlr_input_device *wlr_device = input_device->wlr_device; 97 struct wlr_input_device *wlr_device = input_device->wlr_device;
96 struct input_config *ic = input_device->config; 98 struct input_config *ic = input_device_get_config(input_device);
97 struct libinput_device *libinput_device; 99 struct libinput_device *libinput_device;
98 100
99 if (!ic || !wlr_input_device_is_libinput(wlr_device)) { 101 if (!ic || !wlr_input_device_is_libinput(wlr_device)) {
@@ -101,22 +103,27 @@ static void sway_input_manager_libinput_config_pointer(struct sway_input_device
101 } 103 }
102 104
103 libinput_device = wlr_libinput_get_device_handle(wlr_device); 105 libinput_device = wlr_libinput_get_device_handle(wlr_device);
104 wlr_log(L_DEBUG, "sway_input_manager_libinput_config_pointer(%s)", ic->identifier); 106 wlr_log(L_DEBUG, "input_manager_libinput_config_pointer(%s)",
107 ic->identifier);
105 108
106 if (ic->accel_profile != INT_MIN) { 109 if (ic->accel_profile != INT_MIN) {
107 wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)", 110 wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)",
108 ic->identifier, ic->accel_profile); 111 ic->identifier, ic->accel_profile);
109 libinput_device_config_accel_set_profile(libinput_device, ic->accel_profile); 112 libinput_device_config_accel_set_profile(libinput_device,
113 ic->accel_profile);
110 } 114 }
111 if (ic->click_method != INT_MIN) { 115 if (ic->click_method != INT_MIN) {
112 wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)", 116 wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)",
113 ic->identifier, ic->click_method); 117 ic->identifier, ic->click_method);
114 libinput_device_config_click_set_method(libinput_device, ic->click_method); 118 libinput_device_config_click_set_method(libinput_device,
119 ic->click_method);
115 } 120 }
116 if (ic->drag_lock != INT_MIN) { 121 if (ic->drag_lock != INT_MIN) {
117 wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", 122 wlr_log(L_DEBUG,
123 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
118 ic->identifier, ic->click_method); 124 ic->identifier, ic->click_method);
119 libinput_device_config_tap_set_drag_lock_enabled(libinput_device, ic->drag_lock); 125 libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
126 ic->drag_lock);
120 } 127 }
121 if (ic->dwt != INT_MIN) { 128 if (ic->dwt != INT_MIN) {
122 wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)", 129 wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)",
@@ -124,34 +131,43 @@ static void sway_input_manager_libinput_config_pointer(struct sway_input_device
124 libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt); 131 libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt);
125 } 132 }
126 if (ic->left_handed != INT_MIN) { 133 if (ic->left_handed != INT_MIN) {
127 wlr_log(L_DEBUG, "libinput_config_pointer(%s) left_handed_set_enabled(%d)", 134 wlr_log(L_DEBUG,
135 "libinput_config_pointer(%s) left_handed_set_enabled(%d)",
128 ic->identifier, ic->left_handed); 136 ic->identifier, ic->left_handed);
129 libinput_device_config_left_handed_set(libinput_device, ic->left_handed); 137 libinput_device_config_left_handed_set(libinput_device,
138 ic->left_handed);
130 } 139 }
131 if (ic->middle_emulation != INT_MIN) { 140 if (ic->middle_emulation != INT_MIN) {
132 wlr_log(L_DEBUG, "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)", 141 wlr_log(L_DEBUG,
142 "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)",
133 ic->identifier, ic->middle_emulation); 143 ic->identifier, ic->middle_emulation);
134 libinput_device_config_middle_emulation_set_enabled(libinput_device, ic->middle_emulation); 144 libinput_device_config_middle_emulation_set_enabled(libinput_device,
145 ic->middle_emulation);
135 } 146 }
136 if (ic->natural_scroll != INT_MIN) { 147 if (ic->natural_scroll != INT_MIN) {
137 wlr_log(L_DEBUG, "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)", 148 wlr_log(L_DEBUG,
149 "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)",
138 ic->identifier, ic->natural_scroll); 150 ic->identifier, ic->natural_scroll);
139 libinput_device_config_scroll_set_natural_scroll_enabled(libinput_device, ic->natural_scroll); 151 libinput_device_config_scroll_set_natural_scroll_enabled(
152 libinput_device, ic->natural_scroll);
140 } 153 }
141 if (ic->pointer_accel != FLT_MIN) { 154 if (ic->pointer_accel != FLT_MIN) {
142 wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)", 155 wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)",
143 ic->identifier, ic->pointer_accel); 156 ic->identifier, ic->pointer_accel);
144 libinput_device_config_accel_set_speed(libinput_device, ic->pointer_accel); 157 libinput_device_config_accel_set_speed(libinput_device,
158 ic->pointer_accel);
145 } 159 }
146 if (ic->scroll_method != INT_MIN) { 160 if (ic->scroll_method != INT_MIN) {
147 wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)", 161 wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)",
148 ic->identifier, ic->scroll_method); 162 ic->identifier, ic->scroll_method);
149 libinput_device_config_scroll_set_method(libinput_device, ic->scroll_method); 163 libinput_device_config_scroll_set_method(libinput_device,
164 ic->scroll_method);
150 } 165 }
151 if (ic->send_events != INT_MIN) { 166 if (ic->send_events != INT_MIN) {
152 wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)", 167 wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)",
153 ic->identifier, ic->send_events); 168 ic->identifier, ic->send_events);
154 libinput_device_config_send_events_set_mode(libinput_device, ic->send_events); 169 libinput_device_config_send_events_set_mode(libinput_device,
170 ic->send_events);
155 } 171 }
156 if (ic->tap != INT_MIN) { 172 if (ic->tap != INT_MIN) {
157 wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)", 173 wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)",
@@ -175,12 +191,11 @@ static void handle_device_destroy(struct wl_listener *listener, void *data) {
175 191
176 struct sway_seat *seat = NULL; 192 struct sway_seat *seat = NULL;
177 wl_list_for_each(seat, &input_manager->seats, link) { 193 wl_list_for_each(seat, &input_manager->seats, link) {
178 sway_seat_remove_device(seat, input_device); 194 seat_remove_device(seat, input_device);
179 } 195 }
180 196
181 wl_list_remove(&input_device->link); 197 wl_list_remove(&input_device->link);
182 wl_list_remove(&input_device->device_destroy.link); 198 wl_list_remove(&input_device->device_destroy.link);
183 free_input_config(input_device->config);
184 free(input_device->identifier); 199 free(input_device->identifier);
185 free(input_device); 200 free(input_device);
186} 201}
@@ -203,44 +218,36 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
203 wlr_log(L_DEBUG, "adding device: '%s'", 218 wlr_log(L_DEBUG, "adding device: '%s'",
204 input_device->identifier); 219 input_device->identifier);
205 220
206 // find config
207 for (int i = 0; i < config->input_configs->length; ++i) {
208 struct input_config *input_config = config->input_configs->items[i];
209 if (strcmp(input_config->identifier, input_device->identifier) == 0) {
210 free_input_config(input_device->config);
211 input_device->config = copy_input_config(input_config);
212 break;
213 }
214 }
215
216 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 221 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
217 sway_input_manager_libinput_config_pointer(input_device); 222 input_manager_libinput_config_pointer(input_device);
218 } 223 }
219 224
220 struct sway_seat *seat = NULL; 225 struct sway_seat *seat = NULL;
221 if (!input_has_seat_configuration(input)) { 226 if (!input_has_seat_configuration(input)) {
222 wlr_log(L_DEBUG, "no seat configuration, using default seat"); 227 wlr_log(L_DEBUG, "no seat configuration, using default seat");
223 seat = input_manager_get_seat(input, default_seat); 228 seat = input_manager_get_seat(input, default_seat);
224 sway_seat_add_device(seat, input_device); 229 seat_add_device(seat, input_device);
225 return; 230 return;
226 } 231 }
227 232
228 bool added = false; 233 bool added = false;
229 wl_list_for_each(seat, &input->seats, link) { 234 wl_list_for_each(seat, &input->seats, link) {
230 bool has_attachment = seat->config && 235 struct seat_config *seat_config = seat_get_config(seat);
231 (seat_config_get_attachment(seat->config, input_device->identifier) || 236 bool has_attachment = seat_config &&
232 seat_config_get_attachment(seat->config, "*")); 237 (seat_config_get_attachment(seat_config, input_device->identifier) ||
238 seat_config_get_attachment(seat_config, "*"));
233 239
234 if (has_attachment) { 240 if (has_attachment) {
235 sway_seat_add_device(seat, input_device); 241 seat_add_device(seat, input_device);
236 added = true; 242 added = true;
237 } 243 }
238 } 244 }
239 245
240 if (!added) { 246 if (!added) {
241 wl_list_for_each(seat, &input->seats, link) { 247 wl_list_for_each(seat, &input->seats, link) {
242 if (seat->config && seat->config->fallback == 1) { 248 struct seat_config *seat_config = seat_get_config(seat);
243 sway_seat_add_device(seat, input_device); 249 if (seat_config && seat_config->fallback == 1) {
250 seat_add_device(seat, input_device);
244 added = true; 251 added = true;
245 } 252 }
246 } 253 }
@@ -256,7 +263,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
256 input_device->device_destroy.notify = handle_device_destroy; 263 input_device->device_destroy.notify = handle_device_destroy;
257} 264}
258 265
259struct sway_input_manager *sway_input_manager_create( 266struct sway_input_manager *input_manager_create(
260 struct sway_server *server) { 267 struct sway_server *server) {
261 struct sway_input_manager *input = 268 struct sway_input_manager *input =
262 calloc(1, sizeof(struct sway_input_manager)); 269 calloc(1, sizeof(struct sway_input_manager));
@@ -277,11 +284,11 @@ struct sway_input_manager *sway_input_manager_create(
277 return input; 284 return input;
278} 285}
279 286
280bool sway_input_manager_has_focus(struct sway_input_manager *input, 287bool input_manager_has_focus(struct sway_input_manager *input,
281 struct sway_container *container) { 288 struct sway_container *container) {
282 struct sway_seat *seat = NULL; 289 struct sway_seat *seat = NULL;
283 wl_list_for_each(seat, &input->seats, link) { 290 wl_list_for_each(seat, &input->seats, link) {
284 if (sway_seat_get_focus(seat) == container) { 291 if (seat_get_focus(seat) == container) {
285 return true; 292 return true;
286 } 293 }
287 } 294 }
@@ -289,35 +296,32 @@ bool sway_input_manager_has_focus(struct sway_input_manager *input,
289 return false; 296 return false;
290} 297}
291 298
292void sway_input_manager_set_focus(struct sway_input_manager *input, 299void input_manager_set_focus(struct sway_input_manager *input,
293 struct sway_container *container) { 300 struct sway_container *container) {
294 struct sway_seat *seat ; 301 struct sway_seat *seat ;
295 wl_list_for_each(seat, &input->seats, link) { 302 wl_list_for_each(seat, &input->seats, link) {
296 sway_seat_set_focus(seat, container); 303 seat_set_focus(seat, container);
297 } 304 }
298} 305}
299 306
300void sway_input_manager_apply_input_config(struct sway_input_manager *input, 307void input_manager_apply_input_config(struct sway_input_manager *input,
301 struct input_config *input_config) { 308 struct input_config *input_config) {
302 struct sway_input_device *input_device = NULL; 309 struct sway_input_device *input_device = NULL;
303 wl_list_for_each(input_device, &input->devices, link) { 310 wl_list_for_each(input_device, &input->devices, link) {
304 if (strcmp(input_device->identifier, input_config->identifier) == 0) { 311 if (strcmp(input_device->identifier, input_config->identifier) == 0) {
305 free_input_config(input_device->config);
306 input_device->config = copy_input_config(input_config);
307
308 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 312 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
309 sway_input_manager_libinput_config_pointer(input_device); 313 input_manager_libinput_config_pointer(input_device);
310 } 314 }
311 315
312 struct sway_seat *seat = NULL; 316 struct sway_seat *seat = NULL;
313 wl_list_for_each(seat, &input->seats, link) { 317 wl_list_for_each(seat, &input->seats, link) {
314 sway_seat_configure_device(seat, input_device); 318 seat_configure_device(seat, input_device);
315 } 319 }
316 } 320 }
317 } 321 }
318} 322}
319 323
320void sway_input_manager_apply_seat_config(struct sway_input_manager *input, 324void input_manager_apply_seat_config(struct sway_input_manager *input,
321 struct seat_config *seat_config) { 325 struct seat_config *seat_config) {
322 wlr_log(L_DEBUG, "applying new seat config for seat %s", 326 wlr_log(L_DEBUG, "applying new seat config for seat %s",
323 seat_config->name); 327 seat_config->name);
@@ -326,7 +330,7 @@ void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
326 return; 330 return;
327 } 331 }
328 332
329 sway_seat_set_config(seat, seat_config); 333 seat_apply_config(seat, seat_config);
330 334
331 // for every device, try to add it to a seat and if no seat has it 335 // for every device, try to add it to a seat and if no seat has it
332 // attached, add it to the fallback seats. 336 // attached, add it to the fallback seats.
@@ -335,11 +339,12 @@ void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
335 list_t *seat_list = create_list(); 339 list_t *seat_list = create_list();
336 struct sway_seat *seat = NULL; 340 struct sway_seat *seat = NULL;
337 wl_list_for_each(seat, &input->seats, link) { 341 wl_list_for_each(seat, &input->seats, link) {
338 if (!seat->config) { 342 struct seat_config *seat_config = seat_get_config(seat);
343 if (!seat_config) {
339 continue; 344 continue;
340 } 345 }
341 if (seat_config_get_attachment(seat->config, "*") || 346 if (seat_config_get_attachment(seat_config, "*") ||
342 seat_config_get_attachment(seat->config, 347 seat_config_get_attachment(seat_config,
343 input_device->identifier)) { 348 input_device->identifier)) {
344 list_add(seat_list, seat); 349 list_add(seat_list, seat);
345 } 350 }
@@ -355,17 +360,18 @@ void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
355 } 360 }
356 } 361 }
357 if (attached) { 362 if (attached) {
358 sway_seat_add_device(seat, input_device); 363 seat_add_device(seat, input_device);
359 } else { 364 } else {
360 sway_seat_remove_device(seat, input_device); 365 seat_remove_device(seat, input_device);
361 } 366 }
362 } 367 }
363 } else { 368 } else {
364 wl_list_for_each(seat, &input->seats, link) { 369 wl_list_for_each(seat, &input->seats, link) {
365 if (seat->config && seat->config->fallback == 1) { 370 struct seat_config *seat_config = seat_get_config(seat);
366 sway_seat_add_device(seat, input_device); 371 if (seat_config && seat_config->fallback == 1) {
372 seat_add_device(seat, input_device);
367 } else { 373 } else {
368 sway_seat_remove_device(seat, input_device); 374 seat_remove_device(seat, input_device);
369 } 375 }
370 } 376 }
371 } 377 }
@@ -373,14 +379,14 @@ void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
373 } 379 }
374} 380}
375 381
376void sway_input_manager_configure_xcursor(struct sway_input_manager *input) { 382void input_manager_configure_xcursor(struct sway_input_manager *input) {
377 struct sway_seat *seat = NULL; 383 struct sway_seat *seat = NULL;
378 wl_list_for_each(seat, &input->seats, link) { 384 wl_list_for_each(seat, &input->seats, link) {
379 sway_seat_configure_xcursor(seat); 385 seat_configure_xcursor(seat);
380 } 386 }
381} 387}
382 388
383struct sway_seat *sway_input_manager_get_default_seat( 389struct sway_seat *input_manager_get_default_seat(
384 struct sway_input_manager *input) { 390 struct sway_input_manager *input) {
385 struct sway_seat *seat = NULL; 391 struct sway_seat *seat = NULL;
386 wl_list_for_each(seat, &input->seats, link) { 392 wl_list_for_each(seat, &input->seats, link) {
@@ -390,3 +396,15 @@ struct sway_seat *sway_input_manager_get_default_seat(
390 } 396 }
391 return seat; 397 return seat;
392} 398}
399
400struct input_config *input_device_get_config(struct sway_input_device *device) {
401 struct input_config *input_config = NULL;
402 for (int i = 0; i < config->input_configs->length; ++i) {
403 input_config = config->input_configs->items[i];
404 if (strcmp(input_config->identifier, device->identifier) == 0) {
405 return input_config;
406 }
407 }
408
409 return NULL;
410}
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 99685052..dbb0c359 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -97,8 +97,8 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard,
97 config->handler_context.seat = keyboard->seat_device->sway_seat; 97 config->handler_context.seat = keyboard->seat_device->sway_seat;
98 struct cmd_results *results = execute_command(binding->command, NULL); 98 struct cmd_results *results = execute_command(binding->command, NULL);
99 if (results->status != CMD_SUCCESS) { 99 if (results->status != CMD_SUCCESS) {
100 wlr_log(L_DEBUG, "could not run command for binding: %s", 100 wlr_log(L_DEBUG, "could not run command for binding: %s (%s)",
101 binding->command); 101 binding->command, results->error);
102 } 102 }
103 free_cmd_results(results); 103 free_cmd_results(results);
104} 104}
@@ -428,7 +428,7 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
428 struct xkb_rule_names rules; 428 struct xkb_rule_names rules;
429 memset(&rules, 0, sizeof(rules)); 429 memset(&rules, 0, sizeof(rules));
430 struct input_config *input_config = 430 struct input_config *input_config =
431 keyboard->seat_device->input_device->config; 431 input_device_get_config(keyboard->seat_device->input_device);
432 struct wlr_input_device *wlr_device = 432 struct wlr_input_device *wlr_device =
433 keyboard->seat_device->input_device->wlr_device; 433 keyboard->seat_device->input_device->wlr_device;
434 434
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 9aa34aca..cf519a82 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,4 +1,5 @@
1#define _XOPEN_SOURCE 700 1#define _XOPEN_SOURCE 700
2#include <assert.h>
2#include <wlr/types/wlr_cursor.h> 3#include <wlr/types/wlr_cursor.h>
3#include <wlr/types/wlr_output_layout.h> 4#include <wlr/types/wlr_output_layout.h>
4#include <wlr/types/wlr_xcursor_manager.h> 5#include <wlr/types/wlr_xcursor_manager.h>
@@ -25,7 +26,7 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
25 free(seat_device); 26 free(seat_device);
26} 27}
27 28
28void sway_seat_destroy(struct sway_seat *seat) { 29void seat_destroy(struct sway_seat *seat) {
29 struct sway_seat_device *seat_device, *next; 30 struct sway_seat_device *seat_device, *next;
30 wl_list_for_each_safe(seat_device, next, &seat->devices, link) { 31 wl_list_for_each_safe(seat_device, next, &seat->devices, link) {
31 seat_device_destroy(seat_device); 32 seat_device_destroy(seat_device);
@@ -35,30 +36,89 @@ void sway_seat_destroy(struct sway_seat *seat) {
35 wlr_seat_destroy(seat->wlr_seat); 36 wlr_seat_destroy(seat->wlr_seat);
36} 37}
37 38
39static struct sway_seat_container *seat_container_from_container(
40 struct sway_seat *seat, struct sway_container *con);
41
42static void seat_container_destroy(struct sway_seat_container *seat_con) {
43 struct sway_container *con = seat_con->container;
44 struct sway_container *child = NULL;
45
46 if (con->children != NULL) {
47 for (int i = 0; i < con->children->length; ++i) {
48 child = con->children->items[i];
49 struct sway_seat_container *seat_child =
50 seat_container_from_container(seat_con->seat, child);
51 seat_container_destroy(seat_child);
52 }
53 }
54
55 wl_list_remove(&seat_con->destroy.link);
56 wl_list_remove(&seat_con->link);
57 free(seat_con);
58}
59
60static void seat_send_focus(struct sway_seat *seat,
61 struct sway_container *con) {
62 if (con->type != C_VIEW) {
63 return;
64 }
65 struct sway_view *view = con->sway_view;
66 if (view->type == SWAY_XWAYLAND_VIEW) {
67 struct wlr_xwayland *xwayland =
68 seat->input->server->xwayland;
69 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
70 }
71 view_set_activated(view, true);
72 struct wlr_keyboard *keyboard =
73 wlr_seat_get_keyboard(seat->wlr_seat);
74 if (keyboard) {
75 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
76 view->surface, keyboard->keycodes,
77 keyboard->num_keycodes, &keyboard->modifiers);
78 } else {
79 wlr_seat_keyboard_notify_enter(
80 seat->wlr_seat, view->surface, NULL, 0, NULL);
81 }
82
83}
84
38static void handle_seat_container_destroy(struct wl_listener *listener, 85static void handle_seat_container_destroy(struct wl_listener *listener,
39 void *data) { 86 void *data) {
40 struct sway_seat_container *seat_con = 87 struct sway_seat_container *seat_con =
41 wl_container_of(listener, seat_con, destroy); 88 wl_container_of(listener, seat_con, destroy);
42 struct sway_seat *seat = seat_con->seat; 89 struct sway_seat *seat = seat_con->seat;
43 struct sway_container *con = seat_con->container; 90 struct sway_container *con = seat_con->container;
91 struct sway_container *parent = con->parent;
92 struct sway_container *focus = seat_get_focus(seat);
44 93
45 bool is_focus = (sway_seat_get_focus(seat) == con); 94 bool set_focus =
95 focus != NULL &&
96 (focus == con || container_has_child(con, focus)) &&
97 con->type != C_WORKSPACE;
46 98
47 wl_list_remove(&seat_con->link); 99 seat_container_destroy(seat_con);
48 100
49 if (is_focus) { 101 if (set_focus) {
50 // pick next focus 102 struct sway_container *next_focus = NULL;
51 sway_seat_set_focus(seat, NULL); 103 while (next_focus == NULL) {
52 struct sway_container *next = sway_seat_get_focus_inactive(seat, con->parent); 104 next_focus = seat_get_focus_by_type(seat, parent, C_VIEW);
53 if (next == NULL) {
54 next = con->parent;
55 }
56 sway_seat_set_focus(seat, next);
57 }
58 105
59 wl_list_remove(&seat_con->destroy.link); 106 if (next_focus == NULL && parent->type == C_WORKSPACE) {
107 next_focus = parent;
108 break;
109 }
60 110
61 free(seat_con); 111 parent = parent->parent;
112 }
113
114 // the structure change might have caused it to move up to the top of
115 // the focus stack without sending focus notifications to the view
116 if (seat_get_focus(seat) == next_focus) {
117 seat_send_focus(seat, next_focus);
118 } else {
119 seat_set_focus(seat, next_focus);
120 }
121 }
62} 122}
63 123
64static struct sway_seat_container *seat_container_from_container( 124static struct sway_seat_container *seat_container_from_container(
@@ -110,7 +170,7 @@ static void collect_focus_iter(struct sway_container *con, void *data) {
110 wl_list_insert(&seat->focus_stack, &seat_con->link); 170 wl_list_insert(&seat->focus_stack, &seat_con->link);
111} 171}
112 172
113struct sway_seat *sway_seat_create(struct sway_input_manager *input, 173struct sway_seat *seat_create(struct sway_input_manager *input,
114 const char *seat_name) { 174 const char *seat_name) {
115 struct sway_seat *seat = calloc(1, sizeof(struct sway_seat)); 175 struct sway_seat *seat = calloc(1, sizeof(struct sway_seat));
116 if (!seat) { 176 if (!seat) {
@@ -133,7 +193,8 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
133 // init the focus stack 193 // init the focus stack
134 wl_list_init(&seat->focus_stack); 194 wl_list_init(&seat->focus_stack);
135 195
136 container_for_each_descendant_dfs(&root_container, collect_focus_iter, seat); 196 container_for_each_descendant_dfs(&root_container,
197 collect_focus_iter, seat);
137 198
138 wl_signal_add(&root_container.sway_root->events.new_container, 199 wl_signal_add(&root_container.sway_root->events.new_container,
139 &seat->new_container); 200 &seat->new_container);
@@ -147,7 +208,7 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
147 WL_SEAT_CAPABILITY_POINTER | 208 WL_SEAT_CAPABILITY_POINTER |
148 WL_SEAT_CAPABILITY_TOUCH); 209 WL_SEAT_CAPABILITY_TOUCH);
149 210
150 sway_seat_configure_xcursor(seat); 211 seat_configure_xcursor(seat);
151 212
152 wl_list_insert(&input->seats, &seat->link); 213 wl_list_insert(&input->seats, &seat->link);
153 214
@@ -165,11 +226,12 @@ static void seat_configure_keyboard(struct sway_seat *seat,
165 if (!seat_device->keyboard) { 226 if (!seat_device->keyboard) {
166 sway_keyboard_create(seat, seat_device); 227 sway_keyboard_create(seat, seat_device);
167 } 228 }
168 struct wlr_keyboard *wlr_keyboard = seat_device->input_device->wlr_device->keyboard; 229 struct wlr_keyboard *wlr_keyboard =
230 seat_device->input_device->wlr_device->keyboard;
169 sway_keyboard_configure(seat_device->keyboard); 231 sway_keyboard_configure(seat_device->keyboard);
170 wlr_seat_set_keyboard(seat->wlr_seat, 232 wlr_seat_set_keyboard(seat->wlr_seat,
171 seat_device->input_device->wlr_device); 233 seat_device->input_device->wlr_device);
172 struct sway_container *focus = sway_seat_get_focus(seat); 234 struct sway_container *focus = seat_get_focus(seat);
173 if (focus && focus->type == C_VIEW) { 235 if (focus && focus->type == C_VIEW) {
174 // force notify reenter to pick up the new configuration 236 // force notify reenter to pick up the new configuration
175 wlr_seat_keyboard_clear_focus(seat->wlr_seat); 237 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
@@ -179,7 +241,7 @@ static void seat_configure_keyboard(struct sway_seat *seat,
179 } 241 }
180} 242}
181 243
182static struct sway_seat_device *sway_seat_get_device(struct sway_seat *seat, 244static struct sway_seat_device *seat_get_device(struct sway_seat *seat,
183 struct sway_input_device *input_device) { 245 struct sway_input_device *input_device) {
184 struct sway_seat_device *seat_device = NULL; 246 struct sway_seat_device *seat_device = NULL;
185 wl_list_for_each(seat_device, &seat->devices, link) { 247 wl_list_for_each(seat_device, &seat->devices, link) {
@@ -191,19 +253,14 @@ static struct sway_seat_device *sway_seat_get_device(struct sway_seat *seat,
191 return NULL; 253 return NULL;
192} 254}
193 255
194void sway_seat_configure_device(struct sway_seat *seat, 256void seat_configure_device(struct sway_seat *seat,
195 struct sway_input_device *input_device) { 257 struct sway_input_device *input_device) {
196 struct sway_seat_device *seat_device = 258 struct sway_seat_device *seat_device =
197 sway_seat_get_device(seat, input_device); 259 seat_get_device(seat, input_device);
198 if (!seat_device) { 260 if (!seat_device) {
199 return; 261 return;
200 } 262 }
201 263
202 if (seat->config) {
203 seat_device->attachment_config =
204 seat_config_get_attachment(seat->config, input_device->identifier);
205 }
206
207 switch (input_device->wlr_device->type) { 264 switch (input_device->wlr_device->type) {
208 case WLR_INPUT_DEVICE_POINTER: 265 case WLR_INPUT_DEVICE_POINTER:
209 seat_configure_pointer(seat, seat_device); 266 seat_configure_pointer(seat, seat_device);
@@ -219,10 +276,10 @@ void sway_seat_configure_device(struct sway_seat *seat,
219 } 276 }
220} 277}
221 278
222void sway_seat_add_device(struct sway_seat *seat, 279void seat_add_device(struct sway_seat *seat,
223 struct sway_input_device *input_device) { 280 struct sway_input_device *input_device) {
224 if (sway_seat_get_device(seat, input_device)) { 281 if (seat_get_device(seat, input_device)) {
225 sway_seat_configure_device(seat, input_device); 282 seat_configure_device(seat, input_device);
226 return; 283 return;
227 } 284 }
228 285
@@ -240,13 +297,13 @@ void sway_seat_add_device(struct sway_seat *seat,
240 seat_device->input_device = input_device; 297 seat_device->input_device = input_device;
241 wl_list_insert(&seat->devices, &seat_device->link); 298 wl_list_insert(&seat->devices, &seat_device->link);
242 299
243 sway_seat_configure_device(seat, input_device); 300 seat_configure_device(seat, input_device);
244} 301}
245 302
246void sway_seat_remove_device(struct sway_seat *seat, 303void seat_remove_device(struct sway_seat *seat,
247 struct sway_input_device *input_device) { 304 struct sway_input_device *input_device) {
248 struct sway_seat_device *seat_device = 305 struct sway_seat_device *seat_device =
249 sway_seat_get_device(seat, input_device); 306 seat_get_device(seat, input_device);
250 307
251 if (!seat_device) { 308 if (!seat_device) {
252 return; 309 return;
@@ -258,7 +315,7 @@ void sway_seat_remove_device(struct sway_seat *seat,
258 seat_device_destroy(seat_device); 315 seat_device_destroy(seat_device);
259} 316}
260 317
261void sway_seat_configure_xcursor(struct sway_seat *seat) { 318void seat_configure_xcursor(struct sway_seat *seat) {
262 // TODO configure theme and size 319 // TODO configure theme and size
263 const char *cursor_theme = NULL; 320 const char *cursor_theme = NULL;
264 321
@@ -273,7 +330,8 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
273 } 330 }
274 331
275 for (int i = 0; i < root_container.children->length; ++i) { 332 for (int i = 0; i < root_container.children->length; ++i) {
276 struct sway_container *output_container = root_container.children->items[i]; 333 struct sway_container *output_container =
334 root_container.children->items[i];
277 struct wlr_output *output = 335 struct wlr_output *output =
278 output_container->sway_output->wlr_output; 336 output_container->sway_output->wlr_output;
279 bool result = 337 bool result =
@@ -292,10 +350,13 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
292 seat->cursor->cursor->y); 350 seat->cursor->cursor->y);
293} 351}
294 352
295void sway_seat_set_focus_warp(struct sway_seat *seat, 353void seat_set_focus_warp(struct sway_seat *seat,
296 struct sway_container *container, bool warp) { 354 struct sway_container *container, bool warp) {
297 struct sway_container *last_focus = sway_seat_get_focus(seat); 355 if (seat->focused_layer) {
356 return;
357 }
298 358
359 struct sway_container *last_focus = seat_get_focus(seat);
299 if (container && last_focus == container) { 360 if (container && last_focus == container) {
300 return; 361 return;
301 } 362 }
@@ -311,23 +372,7 @@ void sway_seat_set_focus_warp(struct sway_seat *seat,
311 wl_list_insert(&seat->focus_stack, &seat_con->link); 372 wl_list_insert(&seat->focus_stack, &seat_con->link);
312 373
313 if (container->type == C_VIEW) { 374 if (container->type == C_VIEW) {
314 struct sway_view *view = container->sway_view; 375 seat_send_focus(seat, container);
315 view_set_activated(view, true);
316 if (view->type == SWAY_XWAYLAND_VIEW) {
317 struct wlr_xwayland *xwayland =
318 seat->input->server->xwayland;
319 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
320 }
321 struct wlr_keyboard *keyboard =
322 wlr_seat_get_keyboard(seat->wlr_seat);
323 if (keyboard) {
324 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
325 view->surface, keyboard->keycodes,
326 keyboard->num_keycodes, &keyboard->modifiers);
327 } else {
328 wlr_seat_keyboard_notify_enter(
329 seat->wlr_seat, view->surface, NULL, 0, NULL);
330 }
331 } 376 }
332 } 377 }
333 378
@@ -364,7 +409,7 @@ void sway_seat_set_focus_warp(struct sway_seat *seat,
364 } 409 }
365 410
366 if (last_focus && last_focus->type == C_VIEW && 411 if (last_focus && last_focus->type == C_VIEW &&
367 !sway_input_manager_has_focus(seat->input, last_focus)) { 412 !input_manager_has_focus(seat->input, last_focus)) {
368 struct sway_view *view = last_focus->sway_view; 413 struct sway_view *view = last_focus->sway_view;
369 view_set_activated(view, false); 414 view_set_activated(view, false);
370 } 415 }
@@ -372,23 +417,69 @@ void sway_seat_set_focus_warp(struct sway_seat *seat,
372 seat->has_focus = (container != NULL); 417 seat->has_focus = (container != NULL);
373} 418}
374 419
375void sway_seat_set_focus(struct sway_seat *seat, 420void seat_set_focus(struct sway_seat *seat,
376 struct sway_container *container) { 421 struct sway_container *container) {
377 sway_seat_set_focus_warp(seat, container, true); 422 seat_set_focus_warp(seat, container, true);
378} 423}
379 424
380struct sway_container *sway_seat_get_focus_inactive(struct sway_seat *seat, struct sway_container *container) { 425void seat_set_focus_layer(struct sway_seat *seat,
426 struct wlr_layer_surface *layer) {
427 if (!layer) {
428 seat->focused_layer = NULL;
429 return;
430 }
431 if (seat->focused_layer == layer) {
432 return;
433 }
434 if (seat->has_focus) {
435 struct sway_container *focus = seat_get_focus(seat);
436 if (focus->type == C_VIEW) {
437 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
438 view_set_activated(focus->sway_view, false);
439 }
440 }
441 if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
442 seat->focused_layer = layer;
443 }
444 struct wlr_keyboard *keyboard =
445 wlr_seat_get_keyboard(seat->wlr_seat);
446 if (keyboard) {
447 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
448 layer->surface, keyboard->keycodes,
449 keyboard->num_keycodes, &keyboard->modifiers);
450 } else {
451 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
452 layer->surface, NULL, 0, NULL);
453 }
454}
455
456struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
457 struct sway_container *container) {
458 return seat_get_focus_by_type(seat, container, C_TYPES);
459}
460
461struct sway_container *sway_seat_get_focus(struct sway_seat *seat) {
462 if (!seat->has_focus) {
463 return NULL;
464 }
465 return seat_get_focus_inactive(seat, &root_container);
466}
467
468struct sway_container *seat_get_focus_by_type(struct sway_seat *seat,
469 struct sway_container *container, enum sway_container_type type) {
381 struct sway_seat_container *current = NULL; 470 struct sway_seat_container *current = NULL;
382 struct sway_container *parent = NULL; 471 struct sway_container *parent = NULL;
383 wl_list_for_each(current, &seat->focus_stack, link) { 472 wl_list_for_each(current, &seat->focus_stack, link) {
384 parent = current->container->parent; 473 parent = current->container->parent;
385 474
386 if (current->container == container) { 475 if (current->container == container &&
476 (type == C_TYPES || container->type == type)) {
387 return current->container; 477 return current->container;
388 } 478 }
389 479
390 while (parent) { 480 while (parent) {
391 if (parent == container) { 481 if (parent == container && (type == C_TYPES ||
482 current->container->type == type)) {
392 return current->container; 483 return current->container;
393 } 484 }
394 parent = parent->parent; 485 parent = parent->parent;
@@ -398,42 +489,34 @@ struct sway_container *sway_seat_get_focus_inactive(struct sway_seat *seat, stru
398 return NULL; 489 return NULL;
399} 490}
400 491
401struct sway_container *sway_seat_get_focus(struct sway_seat *seat) { 492struct sway_container *seat_get_focus(struct sway_seat *seat) {
402 if (!seat->has_focus) { 493 if (!seat->has_focus) {
403 return NULL; 494 return NULL;
404 } 495 }
405 return sway_seat_get_focus_inactive(seat, &root_container); 496 return seat_get_focus_inactive(seat, &root_container);
406} 497}
407 498
408struct sway_container *sway_seat_get_focus_by_type(struct sway_seat *seat, 499void seat_apply_config(struct sway_seat *seat,
409 enum sway_container_type type) {
410 struct sway_container *focus = sway_seat_get_focus_inactive(seat, &root_container);
411 if (focus->type == type) {
412 return focus;
413 }
414
415 return container_parent(focus, type);
416}
417
418void sway_seat_set_config(struct sway_seat *seat,
419 struct seat_config *seat_config) { 500 struct seat_config *seat_config) {
420 // clear configs
421 free_seat_config(seat->config);
422 seat->config = NULL;
423
424 struct sway_seat_device *seat_device = NULL; 501 struct sway_seat_device *seat_device = NULL;
425 wl_list_for_each(seat_device, &seat->devices, link) {
426 seat_device->attachment_config = NULL;
427 }
428 502
429 if (!seat_config) { 503 if (!seat_config) {
430 return; 504 return;
431 } 505 }
432 506
433 // add configs
434 seat->config = copy_seat_config(seat_config);
435
436 wl_list_for_each(seat_device, &seat->devices, link) { 507 wl_list_for_each(seat_device, &seat->devices, link) {
437 sway_seat_configure_device(seat, seat_device->input_device); 508 seat_configure_device(seat, seat_device->input_device);
438 } 509 }
439} 510}
511
512struct seat_config *seat_get_config(struct sway_seat *seat) {
513 struct seat_config *seat_config = NULL;
514 for (int i = 0; i < config->seat_configs->length; ++i ) {
515 seat_config = config->seat_configs->items[i];
516 if (strcmp(seat->wlr_seat->name, seat_config->name) == 0) {
517 return seat_config;
518 }
519 }
520
521 return NULL;
522}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 7c5f7304..3427c8ec 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -88,11 +88,11 @@ static void ipc_json_describe_output(struct sway_container *container, json_obje
88 json_object_new_string( 88 json_object_new_string(
89 ipc_json_get_output_transform(wlr_output->transform))); 89 ipc_json_get_output_transform(wlr_output->transform)));
90 90
91 struct sway_seat *seat = sway_input_manager_get_default_seat(input_manager); 91 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
92 const char *ws = NULL; 92 const char *ws = NULL;
93 if (seat) { 93 if (seat) {
94 struct sway_container *focus = 94 struct sway_container *focus =
95 sway_seat_get_focus_inactive(seat, container); 95 seat_get_focus_inactive(seat, container);
96 if (focus && focus->type != C_WORKSPACE) { 96 if (focus && focus->type != C_WORKSPACE) {
97 focus = container_parent(focus, C_WORKSPACE); 97 focus = container_parent(focus, C_WORKSPACE);
98 } 98 }
@@ -139,8 +139,8 @@ json_object *ipc_json_describe_container(struct sway_container *c) {
139 return NULL; 139 return NULL;
140 } 140 }
141 141
142 struct sway_seat *seat = sway_input_manager_get_default_seat(input_manager); 142 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
143 bool focused = sway_seat_get_focus(seat) == c; 143 bool focused = seat_get_focus(seat) == c;
144 144
145 json_object *object = json_object_new_object(); 145 json_object *object = json_object_new_object();
146 146
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 869f1ed0..df5fb699 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -388,8 +388,8 @@ static void ipc_get_workspaces_callback(struct sway_container *workspace,
388 // override the default focused indicator because 388 // override the default focused indicator because
389 // it's set differently for the get_workspaces reply 389 // it's set differently for the get_workspaces reply
390 struct sway_seat *seat = 390 struct sway_seat *seat =
391 sway_input_manager_get_default_seat(input_manager); 391 input_manager_get_default_seat(input_manager);
392 struct sway_container *focused_ws = sway_seat_get_focus(seat); 392 struct sway_container *focused_ws = seat_get_focus(seat);
393 if (focused_ws != NULL && focused_ws->type != C_WORKSPACE) { 393 if (focused_ws != NULL && focused_ws->type != C_WORKSPACE) {
394 focused_ws = container_parent(focused_ws, C_WORKSPACE); 394 focused_ws = container_parent(focused_ws, C_WORKSPACE);
395 } 395 }
@@ -399,7 +399,7 @@ static void ipc_get_workspaces_callback(struct sway_container *workspace,
399 json_object_new_boolean(focused)); 399 json_object_new_boolean(focused));
400 json_object_array_add((json_object *)data, workspace_json); 400 json_object_array_add((json_object *)data, workspace_json);
401 401
402 focused_ws = sway_seat_get_focus_inactive(seat, workspace->parent); 402 focused_ws = seat_get_focus_inactive(seat, workspace->parent);
403 if (focused_ws->type != C_WORKSPACE) { 403 if (focused_ws->type != C_WORKSPACE) {
404 focused_ws = container_parent(focused_ws, C_WORKSPACE); 404 focused_ws = container_parent(focused_ws, C_WORKSPACE);
405 } 405 }
diff --git a/sway/main.c b/sway/main.c
index ded922ee..e7f8ddd3 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -118,7 +118,7 @@ void run_as_ipc_client(char *command, char *socket_path) {
118static void log_env() { 118static void log_env() {
119 const char *log_vars[] = { 119 const char *log_vars[] = {
120 "PATH", 120 "PATH",
121 "LD_LOAD_PATH", 121 "LD_LIBRARY_PATH",
122 "LD_PRELOAD_PATH", 122 "LD_PRELOAD_PATH",
123 "LD_LIBRARY_PATH", 123 "LD_LIBRARY_PATH",
124 "SWAY_CURSOR_THEME", 124 "SWAY_CURSOR_THEME",
diff --git a/sway/meson.build b/sway/meson.build
index 0cc620ea..a6a633a0 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -19,6 +19,8 @@ sway_sources = files(
19 'commands/input.c', 19 'commands/input.c',
20 'commands/layout.c', 20 'commands/layout.c',
21 'commands/mode.c', 21 'commands/mode.c',
22 'commands/split.c',
23 'commands/move.c',
22 'commands/seat.c', 24 'commands/seat.c',
23 'commands/seat/attach.c', 25 'commands/seat/attach.c',
24 'commands/seat/fallback.c', 26 'commands/seat/fallback.c',
diff --git a/sway/server.c b/sway/server.c
index f5cc199c..54945312 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -109,7 +109,7 @@ bool server_init(struct sway_server *server) {
109 return false; 109 return false;
110 } 110 }
111 111
112 input_manager = sway_input_manager_create(server); 112 input_manager = input_manager_create(server);
113 return true; 113 return true;
114} 114}
115 115
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 746dbf1f..4db93ce8 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -33,12 +33,29 @@ static list_t *get_bfs_queue() {
33 return bfs_queue; 33 return bfs_queue;
34} 34}
35 35
36const char *container_type_to_str(enum sway_container_type type) {
37 switch (type) {
38 case C_ROOT:
39 return "C_ROOT";
40 case C_OUTPUT:
41 return "C_OUTPUT";
42 case C_WORKSPACE:
43 return "C_WORKSPACE";
44 case C_CONTAINER:
45 return "C_CONTAINER";
46 case C_VIEW:
47 return "C_VIEW";
48 default:
49 return "C_UNKNOWN";
50 }
51}
52
36static void notify_new_container(struct sway_container *container) { 53static void notify_new_container(struct sway_container *container) {
37 wl_signal_emit(&root_container.sway_root->events.new_container, container); 54 wl_signal_emit(&root_container.sway_root->events.new_container, container);
38 ipc_event_window(container, "new"); 55 ipc_event_window(container, "new");
39} 56}
40 57
41static struct sway_container *container_create(enum sway_container_type type) { 58struct sway_container *container_create(enum sway_container_type type) {
42 // next id starts at 1 because 0 is assigned to root_container in layout.c 59 // next id starts at 1 because 0 is assigned to root_container in layout.c
43 static size_t next_id = 1; 60 static size_t next_id = 1;
44 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 61 struct sway_container *c = calloc(1, sizeof(struct sway_container));
@@ -54,11 +71,12 @@ static struct sway_container *container_create(enum sway_container_type type) {
54 } 71 }
55 72
56 wl_signal_init(&c->events.destroy); 73 wl_signal_init(&c->events.destroy);
74 wl_signal_init(&c->events.reparent);
57 75
58 return c; 76 return c;
59} 77}
60 78
61struct sway_container *container_destroy(struct sway_container *cont) { 79static struct sway_container *_container_destroy(struct sway_container *cont) {
62 if (cont == NULL) { 80 if (cont == NULL) {
63 return NULL; 81 return NULL;
64 } 82 }
@@ -66,13 +84,14 @@ struct sway_container *container_destroy(struct sway_container *cont) {
66 wl_signal_emit(&cont->events.destroy, cont); 84 wl_signal_emit(&cont->events.destroy, cont);
67 85
68 struct sway_container *parent = cont->parent; 86 struct sway_container *parent = cont->parent;
69 if (cont->children) { 87 if (cont->children != NULL) {
70 // remove children until there are no more, container_destroy calls 88 // remove children until there are no more, container_destroy calls
71 // container_remove_child, which removes child from this container 89 // container_remove_child, which removes child from this container
72 while (cont->children->length) { 90 while (cont->children != NULL && cont->children->length != 0) {
73 container_destroy(cont->children->items[0]); 91 struct sway_container *child = cont->children->items[0];
92 container_remove_child(child);
93 container_destroy(child);
74 } 94 }
75 list_free(cont->children);
76 } 95 }
77 if (cont->marks) { 96 if (cont->marks) {
78 list_foreach(cont->marks, free); 97 list_foreach(cont->marks, free);
@@ -84,10 +103,19 @@ struct sway_container *container_destroy(struct sway_container *cont) {
84 if (cont->name) { 103 if (cont->name) {
85 free(cont->name); 104 free(cont->name);
86 } 105 }
106 list_free(cont->children);
107 cont->children = NULL;
87 free(cont); 108 free(cont);
88 return parent; 109 return parent;
89} 110}
90 111
112struct sway_container *container_destroy(struct sway_container *cont) {
113 struct sway_container *parent = _container_destroy(cont);
114 parent = container_reap_empty(parent);
115 arrange_windows(&root_container, -1, -1);
116 return parent;
117}
118
91struct sway_container *container_output_create( 119struct sway_container *container_output_create(
92 struct sway_output *sway_output) { 120 struct sway_output *sway_output) {
93 struct wlr_box size; 121 struct wlr_box size;
@@ -144,7 +172,7 @@ struct sway_container *container_output_create(
144 struct sway_seat *seat = NULL; 172 struct sway_seat *seat = NULL;
145 wl_list_for_each(seat, &input_manager->seats, link) { 173 wl_list_for_each(seat, &input_manager->seats, link) {
146 if (!seat->has_focus) { 174 if (!seat->has_focus) {
147 sway_seat_set_focus(seat, ws); 175 seat_set_focus(seat, ws);
148 } 176 }
149 } 177 }
150 178
@@ -395,3 +423,17 @@ bool container_has_anscestor(struct sway_container *descendant,
395 } 423 }
396 return false; 424 return false;
397} 425}
426
427static bool find_child_func(struct sway_container *con, void *data) {
428 struct sway_container *child = data;
429 return con == child;
430}
431
432bool container_has_child(struct sway_container *con,
433 struct sway_container *child) {
434 if (child == NULL || child->type == C_VIEW ||
435 child->children->length == 0) {
436 return false;
437 }
438 return container_find(con, find_child_func, child);
439}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index ce0682dc..95a84d12 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -1,4 +1,5 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
2#include <ctype.h> 3#include <ctype.h>
3#include <math.h> 4#include <math.h>
4#include <stdbool.h> 5#include <stdbool.h>
@@ -57,7 +58,7 @@ void layout_init(void) {
57 58
58 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); 59 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
59 root_container.sway_root->output_layout = wlr_output_layout_create(); 60 root_container.sway_root->output_layout = wlr_output_layout_create();
60 wl_list_init(&root_container.sway_root->unmanaged_views); 61 wl_list_init(&root_container.sway_root->xwayland_unmanaged);
61 wl_signal_init(&root_container.sway_root->events.new_container); 62 wl_signal_init(&root_container.sway_root->events.new_container);
62 63
63 root_container.sway_root->output_layout_change.notify = 64 root_container.sway_root->output_layout_change.notify =
@@ -103,11 +104,13 @@ void container_add_child(struct sway_container *parent,
103} 104}
104 105
105struct sway_container *container_reap_empty(struct sway_container *container) { 106struct sway_container *container_reap_empty(struct sway_container *container) {
106 if (!sway_assert(container, "reaping null container")) { 107 if (container == NULL) {
107 return NULL; 108 return NULL;
108 } 109 }
109 wlr_log(L_DEBUG, "reaping %p %s", container, container->name); 110 wlr_log(L_DEBUG, "Reaping %p %s '%s'", container,
110 while (container != &root_container && container->children->length == 0) { 111 container_type_to_str(container->type), container->name);
112 while (container->type != C_ROOT && container->type != C_OUTPUT
113 && container->children->length == 0) {
111 if (container->type == C_WORKSPACE) { 114 if (container->type == C_WORKSPACE) {
112 if (!workspace_is_visible(container)) { 115 if (!workspace_is_visible(container)) {
113 struct sway_container *parent = container->parent; 116 struct sway_container *parent = container->parent;
@@ -135,25 +138,49 @@ struct sway_container *container_remove_child(struct sway_container *child) {
135 } 138 }
136 } 139 }
137 child->parent = NULL; 140 child->parent = NULL;
138 return container_reap_empty(parent); 141 return parent;
139} 142}
140 143
141void container_move_to(struct sway_container* container, 144void container_move_to(struct sway_container *container,
142 struct sway_container* destination) { 145 struct sway_container *destination) {
143 if (container == destination 146 if (container == destination
144 || container_has_anscestor(container, destination)) { 147 || container_has_anscestor(container, destination)) {
145 return; 148 return;
146 } 149 }
147 struct sway_container *old_parent = container_remove_child(container); 150 struct sway_container *old_parent = container_remove_child(container);
148 container->width = container->height = 0; 151 container->width = container->height = 0;
149 struct sway_container *new_parent = 152 struct sway_container *new_parent;
150 container_add_sibling(destination, container); 153 if (destination->type == C_VIEW) {
154 new_parent = container_add_sibling(destination, container);
155 } else {
156 new_parent = destination;
157 container_add_child(destination, container);
158 }
159 wl_signal_emit(&container->events.reparent, old_parent);
160 if (container->type == C_WORKSPACE) {
161 struct sway_seat *seat = input_manager_get_default_seat(
162 input_manager);
163 if (old_parent->children->length == 0) {
164 char *ws_name = workspace_next_name(old_parent->name);
165 struct sway_container *ws =
166 container_workspace_create(old_parent, ws_name);
167 free(ws_name);
168 seat_set_focus(seat, ws);
169 }
170 container_sort_workspaces(new_parent);
171 seat_set_focus(seat, new_parent);
172 }
151 if (old_parent) { 173 if (old_parent) {
152 arrange_windows(old_parent, -1, -1); 174 arrange_windows(old_parent, -1, -1);
153 } 175 }
154 arrange_windows(new_parent, -1, -1); 176 arrange_windows(new_parent, -1, -1);
155} 177}
156 178
179void container_move(struct sway_container *container,
180 enum movement_direction dir, int move_amt) {
181 // TODO
182}
183
157enum sway_container_layout container_get_default_layout( 184enum sway_container_layout container_get_default_layout(
158 struct sway_container *output) { 185 struct sway_container *output) {
159 if (config->default_layout != L_NONE) { 186 if (config->default_layout != L_NONE) {
@@ -248,8 +275,8 @@ void arrange_windows(struct sway_container *container,
248 struct wlr_box *area = &output->sway_output->usable_area; 275 struct wlr_box *area = &output->sway_output->usable_area;
249 wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", 276 wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d",
250 area->width, area->height, area->x, area->y); 277 area->width, area->height, area->x, area->y);
251 container->width = area->width; 278 container->width = width = area->width;
252 container->height = area->height; 279 container->height = height = area->height;
253 container->x = x = area->x; 280 container->x = x = area->x;
254 container->y = y = area->y; 281 container->y = y = area->y;
255 wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", 282 wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f",
@@ -261,7 +288,7 @@ void arrange_windows(struct sway_container *container,
261 { 288 {
262 container->width = width; 289 container->width = width;
263 container->height = height; 290 container->height = height;
264 view_set_size(container->sway_view, 291 view_configure(container->sway_view, container->x, container->y,
265 container->width, container->height); 292 container->width, container->height);
266 wlr_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f", 293 wlr_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f",
267 container->width, container->height, 294 container->width, container->height,
@@ -322,7 +349,14 @@ static void apply_horiz_layout(struct sway_container *container,
322 wlr_log(L_DEBUG, 349 wlr_log(L_DEBUG,
323 "Calculating arrangement for %p:%d (will scale %f by %f)", 350 "Calculating arrangement for %p:%d (will scale %f by %f)",
324 child, child->type, width, scale); 351 child, child->type, width, scale);
325 view_set_position(child->sway_view, child_x, y); 352
353 if (child->type == C_VIEW) {
354 view_configure(child->sway_view, child_x, y, child->width,
355 child->height);
356 } else {
357 child->x = child_x;
358 child->y = y;
359 }
326 360
327 if (i == end - 1) { 361 if (i == end - 1) {
328 double remaining_width = x + width - child_x; 362 double remaining_width = x + width - child_x;
@@ -373,7 +407,13 @@ void apply_vert_layout(struct sway_container *container,
373 wlr_log(L_DEBUG, 407 wlr_log(L_DEBUG,
374 "Calculating arrangement for %p:%d (will scale %f by %f)", 408 "Calculating arrangement for %p:%d (will scale %f by %f)",
375 child, child->type, height, scale); 409 child, child->type, height, scale);
376 view_set_position(child->sway_view, x, child_y); 410 if (child->type == C_VIEW) {
411 view_configure(child->sway_view, x, child_y, child->width,
412 child->height);
413 } else {
414 child->x = x;
415 child->y = child_y;
416 }
377 417
378 if (i == end - 1) { 418 if (i == end - 1) {
379 double remaining_height = y + height - child_y; 419 double remaining_height = y + height - child_y;
@@ -404,7 +444,7 @@ static struct sway_container *get_swayc_in_output_direction(
404 return NULL; 444 return NULL;
405 } 445 }
406 446
407 struct sway_container *ws = sway_seat_get_focus_inactive(seat, output); 447 struct sway_container *ws = seat_get_focus_inactive(seat, output);
408 if (ws->type != C_WORKSPACE) { 448 if (ws->type != C_WORKSPACE) {
409 ws = container_parent(ws, C_WORKSPACE); 449 ws = container_parent(ws, C_WORKSPACE);
410 } 450 }
@@ -425,7 +465,7 @@ static struct sway_container *get_swayc_in_output_direction(
425 case MOVE_UP: 465 case MOVE_UP:
426 case MOVE_DOWN: { 466 case MOVE_DOWN: {
427 struct sway_container *focused = 467 struct sway_container *focused =
428 sway_seat_get_focus_inactive(seat, ws); 468 seat_get_focus_inactive(seat, ws);
429 if (focused && focused->parent) { 469 if (focused && focused->parent) {
430 struct sway_container *parent = focused->parent; 470 struct sway_container *parent = focused->parent;
431 if (parent->layout == L_VERT) { 471 if (parent->layout == L_VERT) {
@@ -505,11 +545,11 @@ static struct sway_container *sway_output_from_wlr(struct wlr_output *output) {
505 return NULL; 545 return NULL;
506} 546}
507 547
508static struct sway_container *get_swayc_in_direction_under( 548struct sway_container *container_get_in_direction(
509 struct sway_container *container, enum movement_direction dir, 549 struct sway_container *container, struct sway_seat *seat,
510 struct sway_seat *seat, struct sway_container *limit) { 550 enum movement_direction dir) {
511 if (dir == MOVE_CHILD) { 551 if (dir == MOVE_CHILD) {
512 return sway_seat_get_focus_inactive(seat, container); 552 return seat_get_focus_inactive(seat, container);
513 } 553 }
514 554
515 struct sway_container *parent = container->parent; 555 struct sway_container *parent = container->parent;
@@ -521,26 +561,6 @@ static struct sway_container *get_swayc_in_direction_under(
521 } 561 }
522 } 562 }
523 563
524 if (dir == MOVE_PREV || dir == MOVE_NEXT) {
525 int focused_idx = index_child(container);
526 if (focused_idx == -1) {
527 return NULL;
528 } else {
529 int desired = (focused_idx + (dir == MOVE_NEXT ? 1 : -1)) %
530 parent->children->length;
531 if (desired < 0) {
532 desired += parent->children->length;
533 }
534 return parent->children->items[desired];
535 }
536 }
537
538 // If moving to an adjacent output we need a starting position (since this
539 // output might border to multiple outputs).
540 //struct wlc_point abs_pos;
541 //get_layout_center_position(container, &abs_pos);
542
543
544 // TODO WLR fullscreen 564 // TODO WLR fullscreen
545 /* 565 /*
546 if (container->type == C_VIEW && swayc_is_fullscreen(container)) { 566 if (container->type == C_VIEW && swayc_is_fullscreen(container)) {
@@ -559,7 +579,6 @@ static struct sway_container *get_swayc_in_direction_under(
559 579
560 struct sway_container *wrap_candidate = NULL; 580 struct sway_container *wrap_candidate = NULL;
561 while (true) { 581 while (true) {
562 // Test if we can even make a difference here
563 bool can_move = false; 582 bool can_move = false;
564 int desired; 583 int desired;
565 int idx = index_child(container); 584 int idx = index_child(container);
@@ -589,7 +608,7 @@ static struct sway_container *get_swayc_in_direction_under(
589 } 608 }
590 if (next->children && next->children->length) { 609 if (next->children && next->children->length) {
591 // TODO consider floating children as well 610 // TODO consider floating children as well
592 return sway_seat_get_focus_inactive(seat, next); 611 return seat_get_focus_by_type(seat, next, C_VIEW);
593 } else { 612 } else {
594 return next; 613 return next;
595 } 614 }
@@ -619,21 +638,23 @@ static struct sway_container *get_swayc_in_direction_under(
619 wrap_candidate = parent->children->items[0]; 638 wrap_candidate = parent->children->items[0];
620 } 639 }
621 if (config->force_focus_wrapping) { 640 if (config->force_focus_wrapping) {
622 return wrap_candidate; 641 return seat_get_focus_by_type(seat,
642 wrap_candidate, C_VIEW);
623 } 643 }
624 } 644 }
625 } else { 645 } else {
626 wlr_log(L_DEBUG, 646 wlr_log(L_DEBUG,
627 "cont %d-%p dir %i sibling %d: %p", idx, 647 "cont %d-%p dir %i sibling %d: %p", idx,
628 container, dir, desired, parent->children->items[desired]); 648 container, dir, desired, parent->children->items[desired]);
629 return parent->children->items[desired]; 649 return seat_get_focus_by_type(seat,
650 parent->children->items[desired], C_VIEW);
630 } 651 }
631 } 652 }
632 653
633 if (!can_move) { 654 if (!can_move) {
634 container = parent; 655 container = parent;
635 parent = parent->parent; 656 parent = parent->parent;
636 if (!parent || container == limit) { 657 if (!parent) {
637 // wrapping is the last chance 658 // wrapping is the last chance
638 return wrap_candidate; 659 return wrap_candidate;
639 } 660 }
@@ -641,8 +662,70 @@ static struct sway_container *get_swayc_in_direction_under(
641 } 662 }
642} 663}
643 664
644struct sway_container *container_get_in_direction( 665struct sway_container *container_replace_child(struct sway_container *child,
645 struct sway_container *container, struct sway_seat *seat, 666 struct sway_container *new_child) {
646 enum movement_direction dir) { 667 struct sway_container *parent = child->parent;
647 return get_swayc_in_direction_under(container, dir, seat, NULL); 668 if (parent == NULL) {
669 return NULL;
670 }
671 int i = index_child(child);
672
673 // TODO floating
674 parent->children->items[i] = new_child;
675 new_child->parent = parent;
676 child->parent = NULL;
677
678 // Set geometry for new child
679 new_child->x = child->x;
680 new_child->y = child->y;
681 new_child->width = child->width;
682 new_child->height = child->height;
683
684 // reset geometry for child
685 child->width = 0;
686 child->height = 0;
687
688 return parent;
689}
690
691struct sway_container *container_split(struct sway_container *child,
692 enum sway_container_layout layout) {
693 // TODO floating: cannot split a floating container
694 if (!sway_assert(child, "child cannot be null")) {
695 return NULL;
696 }
697 struct sway_container *cont = container_create(C_CONTAINER);
698
699 wlr_log(L_DEBUG, "creating container %p around %p", cont, child);
700
701 cont->prev_layout = L_NONE;
702 cont->width = child->width;
703 cont->height = child->height;
704 cont->x = child->x;
705 cont->y = child->y;
706
707 if (child->type == C_WORKSPACE) {
708 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
709 struct sway_container *workspace = child;
710 bool set_focus = (seat_get_focus(seat) == workspace);
711
712 while (workspace->children->length) {
713 struct sway_container *ws_child = workspace->children->items[0];
714 container_remove_child(ws_child);
715 container_add_child(cont, ws_child);
716 }
717
718 container_add_child(workspace, cont);
719 container_set_layout(workspace, layout);
720
721 if (set_focus) {
722 seat_set_focus(seat, cont);
723 }
724 } else {
725 cont->layout = layout;
726 container_replace_child(child, cont);
727 container_add_child(cont, child);
728 }
729
730 return cont;
648} 731}
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 7248fd00..0509db23 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -1,3 +1,4 @@
1#include <strings.h>
1#include "sway/tree/container.h" 2#include "sway/tree/container.h"
2#include "sway/tree/layout.h" 3#include "sway/tree/layout.h"
3#include "sway/output.h" 4#include "sway/output.h"
@@ -29,6 +30,7 @@ struct sway_container *container_output_destroy(struct sway_container *output) {
29 wl_list_remove(&output->sway_output->destroy.link); 30 wl_list_remove(&output->sway_output->destroy.link);
30 wl_list_remove(&output->sway_output->mode.link); 31 wl_list_remove(&output->sway_output->mode.link);
31 wl_list_remove(&output->sway_output->transform.link); 32 wl_list_remove(&output->sway_output->transform.link);
33 wl_list_remove(&output->sway_output->scale.link);
32 34
33 wl_list_remove(&output->sway_output->damage_destroy.link); 35 wl_list_remove(&output->sway_output->damage_destroy.link);
34 wl_list_remove(&output->sway_output->damage_frame.link); 36 wl_list_remove(&output->sway_output->damage_frame.link);
@@ -37,3 +39,13 @@ struct sway_container *container_output_destroy(struct sway_container *output) {
37 container_destroy(output); 39 container_destroy(output);
38 return &root_container; 40 return &root_container;
39} 41}
42
43struct sway_container *output_by_name(const char *name) {
44 for (int i = 0; i < root_container.children->length; ++i) {
45 struct sway_container *output = root_container.children->items[i];
46 if (strcasecmp(output->name, name) == 0){
47 return output;
48 }
49 }
50 return NULL;
51}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index b7d1a41b..09c804e4 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1,3 +1,4 @@
1#include <stdlib.h>
1#include <wayland-server.h> 2#include <wayland-server.h>
2#include <wlr/types/wlr_output_layout.h> 3#include <wlr/types/wlr_output_layout.h>
3#include "log.h" 4#include "log.h"
@@ -6,82 +7,117 @@
6#include "sway/tree/layout.h" 7#include "sway/tree/layout.h"
7#include "sway/tree/view.h" 8#include "sway/tree/view.h"
8 9
10struct sway_view *view_create(enum sway_view_type type,
11 const struct sway_view_impl *impl) {
12 struct sway_view *view = calloc(1, sizeof(struct sway_view));
13 if (view == NULL) {
14 return NULL;
15 }
16 view->type = type;
17 view->impl = impl;
18 return view;
19}
20
21void view_destroy(struct sway_view *view) {
22 if (view == NULL) {
23 return;
24 }
25
26 if (view->surface != NULL) {
27 view_unmap(view);
28 }
29
30 container_view_destroy(view->swayc);
31 free(view);
32}
33
9const char *view_get_title(struct sway_view *view) { 34const char *view_get_title(struct sway_view *view) {
10 if (view->iface.get_prop) { 35 if (view->impl->get_prop) {
11 return view->iface.get_prop(view, VIEW_PROP_TITLE); 36 return view->impl->get_prop(view, VIEW_PROP_TITLE);
12 } 37 }
13 return NULL; 38 return NULL;
14} 39}
15 40
16const char *view_get_app_id(struct sway_view *view) { 41const char *view_get_app_id(struct sway_view *view) {
17 if (view->iface.get_prop) { 42 if (view->impl->get_prop) {
18 return view->iface.get_prop(view, VIEW_PROP_APP_ID); 43 return view->impl->get_prop(view, VIEW_PROP_APP_ID);
19 } 44 }
20 return NULL; 45 return NULL;
21} 46}
22 47
23const char *view_get_class(struct sway_view *view) { 48const char *view_get_class(struct sway_view *view) {
24 if (view->iface.get_prop) { 49 if (view->impl->get_prop) {
25 return view->iface.get_prop(view, VIEW_PROP_CLASS); 50 return view->impl->get_prop(view, VIEW_PROP_CLASS);
26 } 51 }
27 return NULL; 52 return NULL;
28} 53}
29 54
30const char *view_get_instance(struct sway_view *view) { 55const char *view_get_instance(struct sway_view *view) {
31 if (view->iface.get_prop) { 56 if (view->impl->get_prop) {
32 return view->iface.get_prop(view, VIEW_PROP_INSTANCE); 57 return view->impl->get_prop(view, VIEW_PROP_INSTANCE);
33 } 58 }
34 return NULL; 59 return NULL;
35} 60}
36 61
37void view_set_size(struct sway_view *view, int width, int height) { 62void view_configure(struct sway_view *view, double ox, double oy, int width,
38 if (view->iface.set_size) { 63 int height) {
39 struct wlr_box box = { 64 if (view->impl->configure) {
40 .x = view->swayc->x, 65 view->impl->configure(view, ox, oy, width, height);
41 .y = view->swayc->y,
42 .width = view->width,
43 .height = view->height,
44 };
45 view->iface.set_size(view, width, height);
46 view_update_outputs(view, &box);
47 } 66 }
48} 67}
49 68
50// TODO make view coordinates in layout coordinates 69void view_set_activated(struct sway_view *view, bool activated) {
51void view_set_position(struct sway_view *view, double ox, double oy) { 70 if (view->impl->set_activated) {
52 if (view->iface.set_position) { 71 view->impl->set_activated(view, activated);
53 struct wlr_box box = {
54 .x = view->swayc->x,
55 .y = view->swayc->y,
56 .width = view->width,
57 .height = view->height,
58 };
59 view->iface.set_position(view, ox, oy);
60 view_update_outputs(view, &box);
61 } 72 }
62} 73}
63 74
64void view_set_activated(struct sway_view *view, bool activated) { 75void view_close(struct sway_view *view) {
65 if (view->iface.set_activated) { 76 if (view->impl->close) {
66 view->iface.set_activated(view, activated); 77 view->impl->close(view);
67 } 78 }
68} 79}
69 80
70void view_close(struct sway_view *view) { 81struct sway_container *container_view_destroy(struct sway_container *view) {
71 if (view->iface.close) { 82 if (!view) {
72 view->iface.close(view); 83 return NULL;
73 } 84 }
85 wlr_log(L_DEBUG, "Destroying view '%s'", view->name);
86 struct sway_container *parent = container_destroy(view);
87 arrange_windows(parent, -1, -1);
88 return parent;
89}
90
91void view_damage_whole(struct sway_view *view) {
92 for (int i = 0; i < root_container.children->length; ++i) {
93 struct sway_container *cont = root_container.children->items[i];
94 if (cont->type == C_OUTPUT) {
95 output_damage_whole_view(cont->sway_output, view);
96 }
97 }
98}
99
100void view_damage_from(struct sway_view *view) {
101 // TODO
102 view_damage_whole(view);
103}
104
105static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
106 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
107
108 box->x = output->x + view->swayc->x;
109 box->y = output->y + view->swayc->y;
110 box->width = view->width;
111 box->height = view->height;
74} 112}
75 113
76void view_update_outputs(struct sway_view *view, const struct wlr_box *before) { 114static void view_update_outputs(struct sway_view *view,
115 const struct wlr_box *before) {
116 struct wlr_box box;
117 view_get_layout_box(view, &box);
118
77 struct wlr_output_layout *output_layout = 119 struct wlr_output_layout *output_layout =
78 root_container.sway_root->output_layout; 120 root_container.sway_root->output_layout;
79 struct wlr_box box = {
80 .x = view->swayc->x,
81 .y = view->swayc->y,
82 .width = view->width,
83 .height = view->height,
84 };
85 struct wlr_output_layout_output *layout_output; 121 struct wlr_output_layout_output *layout_output;
86 wl_list_for_each(layout_output, &output_layout->outputs, link) { 122 wl_list_for_each(layout_output, &output_layout->outputs, link) {
87 bool intersected = before != NULL && wlr_output_layout_intersects( 123 bool intersected = before != NULL && wlr_output_layout_intersects(
@@ -97,27 +133,63 @@ void view_update_outputs(struct sway_view *view, const struct wlr_box *before) {
97 } 133 }
98} 134}
99 135
100struct sway_container *container_view_destroy(struct sway_container *view) { 136void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
101 if (!view) { 137 if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {
102 return NULL; 138 return;
103 } 139 }
104 wlr_log(L_DEBUG, "Destroying view '%s'", view->name); 140
105 struct sway_container *parent = container_destroy(view); 141 struct sway_seat *seat = input_manager_current_seat(input_manager);
106 arrange_windows(parent, -1, -1); 142 struct sway_container *focus = seat_get_focus_inactive(seat,
107 return parent; 143 &root_container);
144 struct sway_container *cont = container_view_create(focus, view);
145
146 view->surface = wlr_surface;
147 view->swayc = cont;
148
149 arrange_windows(cont->parent, -1, -1);
150 input_manager_set_focus(input_manager, cont);
151
152 view_damage_whole(view);
153 view_update_outputs(view, NULL);
108} 154}
109 155
110void view_damage_whole(struct sway_view *view) { 156void view_unmap(struct sway_view *view) {
111 struct sway_container *cont = NULL; 157 if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) {
112 for (int i = 0; i < root_container.children->length; ++i) { 158 return;
113 cont = root_container.children->items[i];
114 if (cont->type == C_OUTPUT) {
115 output_damage_whole_view(cont->sway_output, view);
116 }
117 } 159 }
160
161 view_damage_whole(view);
162
163 container_view_destroy(view->swayc);
164
165 view->swayc = NULL;
166 view->surface = NULL;
118} 167}
119 168
120void view_damage_from(struct sway_view *view) { 169void view_update_position(struct sway_view *view, double ox, double oy) {
121 // TODO 170 if (view->swayc->x == ox && view->swayc->y == oy) {
171 return;
172 }
173
174 struct wlr_box box;
175 view_get_layout_box(view, &box);
176 view_damage_whole(view);
177 view->swayc->x = ox;
178 view->swayc->y = oy;
179 view_update_outputs(view, &box);
180 view_damage_whole(view);
181}
182
183void view_update_size(struct sway_view *view, int width, int height) {
184 if (view->width == width && view->height == height) {
185 return;
186 }
187
188 struct wlr_box box;
189 view_get_layout_box(view, &box);
190 view_damage_whole(view);
191 view->width = width;
192 view->height = height;
193 view_update_outputs(view, &box);
122 view_damage_whole(view); 194 view_damage_whole(view);
123} 195}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index d6fd7c70..8077de2e 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -176,7 +176,7 @@ static bool _workspace_by_name(struct sway_container *view, void *data) {
176struct sway_container *workspace_by_name(const char *name) { 176struct sway_container *workspace_by_name(const char *name) {
177 struct sway_seat *seat = input_manager_current_seat(input_manager); 177 struct sway_seat *seat = input_manager_current_seat(input_manager);
178 struct sway_container *current_workspace = NULL, *current_output = NULL; 178 struct sway_container *current_workspace = NULL, *current_output = NULL;
179 struct sway_container *focus = sway_seat_get_focus(seat); 179 struct sway_container *focus = seat_get_focus(seat);
180 if (focus) { 180 if (focus) {
181 current_workspace = container_parent(focus, C_WORKSPACE); 181 current_workspace = container_parent(focus, C_WORKSPACE);
182 current_output = container_parent(focus, C_OUTPUT); 182 current_output = container_parent(focus, C_OUTPUT);
@@ -218,7 +218,7 @@ struct sway_container *workspace_create(const char *name) {
218 // Otherwise create a new one 218 // Otherwise create a new one
219 struct sway_seat *seat = input_manager_current_seat(input_manager); 219 struct sway_seat *seat = input_manager_current_seat(input_manager);
220 struct sway_container *focus = 220 struct sway_container *focus =
221 sway_seat_get_focus_inactive(seat, &root_container); 221 seat_get_focus_inactive(seat, &root_container);
222 parent = focus; 222 parent = focus;
223 parent = container_parent(parent, C_OUTPUT); 223 parent = container_parent(parent, C_OUTPUT);
224 struct sway_container *new_ws = container_workspace_create(parent, name); 224 struct sway_container *new_ws = container_workspace_create(parent, name);
@@ -278,7 +278,7 @@ struct sway_container *workspace_output_prev_next_impl(
278 } 278 }
279 279
280 struct sway_seat *seat = input_manager_current_seat(input_manager); 280 struct sway_seat *seat = input_manager_current_seat(input_manager);
281 struct sway_container *focus = sway_seat_get_focus_inactive(seat, output); 281 struct sway_container *focus = seat_get_focus_inactive(seat, output);
282 struct sway_container *workspace = (focus->type == C_WORKSPACE ? 282 struct sway_container *workspace = (focus->type == C_WORKSPACE ?
283 focus : 283 focus :
284 container_parent(focus, C_WORKSPACE)); 284 container_parent(focus, C_WORKSPACE));
@@ -363,13 +363,13 @@ bool workspace_switch(struct sway_container *workspace) {
363 } 363 }
364 struct sway_seat *seat = input_manager_current_seat(input_manager); 364 struct sway_seat *seat = input_manager_current_seat(input_manager);
365 struct sway_container *focus = 365 struct sway_container *focus =
366 sway_seat_get_focus_inactive(seat, &root_container); 366 seat_get_focus_inactive(seat, &root_container);
367 if (!seat || !focus) { 367 if (!seat || !focus) {
368 return false; 368 return false;
369 } 369 }
370 struct sway_container *active_ws = focus; 370 struct sway_container *active_ws = focus;
371 if (active_ws->type != C_WORKSPACE) { 371 if (active_ws->type != C_WORKSPACE) {
372 container_parent(focus, C_WORKSPACE); 372 active_ws = container_parent(focus, C_WORKSPACE);
373 } 373 }
374 374
375 if (config->auto_back_and_forth 375 if (config->auto_back_and_forth
@@ -394,11 +394,11 @@ bool workspace_switch(struct sway_container *workspace) {
394 394
395 wlr_log(L_DEBUG, "Switching to workspace %p:%s", 395 wlr_log(L_DEBUG, "Switching to workspace %p:%s",
396 workspace, workspace->name); 396 workspace, workspace->name);
397 struct sway_container *next = sway_seat_get_focus_inactive(seat, workspace); 397 struct sway_container *next = seat_get_focus_inactive(seat, workspace);
398 if (next == NULL) { 398 if (next == NULL) {
399 next = workspace; 399 next = workspace;
400 } 400 }
401 sway_seat_set_focus(seat, next); 401 seat_set_focus(seat, next);
402 struct sway_container *output = container_parent(workspace, C_OUTPUT); 402 struct sway_container *output = container_parent(workspace, C_OUTPUT);
403 arrange_windows(output, -1, -1); 403 arrange_windows(output, -1, -1);
404 return true; 404 return true;
@@ -407,7 +407,7 @@ bool workspace_switch(struct sway_container *workspace) {
407bool workspace_is_visible(struct sway_container *ws) { 407bool workspace_is_visible(struct sway_container *ws) {
408 struct sway_container *output = container_parent(ws, C_OUTPUT); 408 struct sway_container *output = container_parent(ws, C_OUTPUT);
409 struct sway_seat *seat = input_manager_current_seat(input_manager); 409 struct sway_seat *seat = input_manager_current_seat(input_manager);
410 struct sway_container *focus = sway_seat_get_focus_inactive(seat, output); 410 struct sway_container *focus = seat_get_focus_inactive(seat, output);
411 if (focus->type != C_WORKSPACE) { 411 if (focus->type != C_WORKSPACE) {
412 focus = container_parent(focus, C_WORKSPACE); 412 focus = container_parent(focus, C_WORKSPACE);
413 } 413 }
diff --git a/swaybar/bar.c b/swaybar/bar.c
index f743236c..fb417095 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -308,14 +308,14 @@ static void display_in(int fd, short mask, void *_bar) {
308 308
309static void ipc_in(int fd, short mask, void *_bar) { 309static void ipc_in(int fd, short mask, void *_bar) {
310 struct swaybar *bar = (struct swaybar *)_bar; 310 struct swaybar *bar = (struct swaybar *)_bar;
311 if (handle_ipc_event(bar)) { 311 if (handle_ipc_readable(bar)) {
312 render_all_frames(bar); 312 render_all_frames(bar);
313 } 313 }
314} 314}
315 315
316static void status_in(int fd, short mask, void *_bar) { 316static void status_in(int fd, short mask, void *_bar) {
317 struct swaybar *bar = (struct swaybar *)_bar; 317 struct swaybar *bar = (struct swaybar *)_bar;
318 if (handle_status_readable(bar->status)) { 318 if (status_handle_readable(bar->status)) {
319 render_all_frames(bar); 319 render_all_frames(bar);
320 } 320 }
321} 321}
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
new file mode 100644
index 00000000..ed134a01
--- /dev/null
+++ b/swaybar/i3bar.c
@@ -0,0 +1,211 @@
1#define _POSIX_C_SOURCE 200809L
2#include <json-c/json.h>
3#include <stdlib.h>
4#include <string.h>
5#include <unistd.h>
6#include <wlr/util/log.h>
7#include "swaybar/config.h"
8#include "swaybar/status_line.h"
9
10static void i3bar_block_free(struct i3bar_block *block) {
11 if (!block) {
12 return;
13 }
14 wl_list_remove(&block->link);
15 free(block->full_text);
16 free(block->short_text);
17 free(block->align);
18 free(block->name);
19 free(block->instance);
20 free(block->color);
21}
22
23static bool i3bar_parse_json(struct status_line *status, const char *text) {
24 struct i3bar_block *block, *tmp;
25 wl_list_for_each_safe(block, tmp, &status->blocks, link) {
26 i3bar_block_free(block);
27 }
28 json_object *results = json_tokener_parse(text);
29 if (!results) {
30 status_error(status, "[failed to parse i3bar json]");
31 return false;
32 }
33 wlr_log(L_DEBUG, "Got i3bar json: '%s'", text);
34 for (size_t i = 0; i < json_object_array_length(results); ++i) {
35 json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
36 json_object *name, *instance, *separator, *separator_block_width;
37 json_object *background, *border, *border_top, *border_bottom;
38 json_object *border_left, *border_right, *markup;
39 json_object *json = json_object_array_get_idx(results, i);
40 if (!json) {
41 continue;
42 }
43 json_object_object_get_ex(json, "full_text", &full_text);
44 json_object_object_get_ex(json, "short_text", &short_text);
45 json_object_object_get_ex(json, "color", &color);
46 json_object_object_get_ex(json, "min_width", &min_width);
47 json_object_object_get_ex(json, "align", &align);
48 json_object_object_get_ex(json, "urgent", &urgent);
49 json_object_object_get_ex(json, "name", &name);
50 json_object_object_get_ex(json, "instance", &instance);
51 json_object_object_get_ex(json, "markup", &markup);
52 json_object_object_get_ex(json, "separator", &separator);
53 json_object_object_get_ex(json, "separator_block_width", &separator_block_width);
54 json_object_object_get_ex(json, "background", &background);
55 json_object_object_get_ex(json, "border", &border);
56 json_object_object_get_ex(json, "border_top", &border_top);
57 json_object_object_get_ex(json, "border_bottom", &border_bottom);
58 json_object_object_get_ex(json, "border_left", &border_left);
59 json_object_object_get_ex(json, "border_right", &border_right);
60
61 struct i3bar_block *block = calloc(1, sizeof(struct i3bar_block));
62 block->full_text = full_text ?
63 strdup(json_object_get_string(full_text)) : NULL;
64 block->short_text = short_text ?
65 strdup(json_object_get_string(short_text)) : NULL;
66 if (color) {
67 block->color = malloc(sizeof(uint32_t));
68 *block->color = parse_color(json_object_get_string(color));
69 }
70 if (min_width) {
71 json_type type = json_object_get_type(min_width);
72 if (type == json_type_int) {
73 block->min_width = json_object_get_int(min_width);
74 } else if (type == json_type_string) {
75 /* the width will be calculated when rendering */
76 block->min_width = 0;
77 }
78 }
79 block->align = strdup(align ? json_object_get_string(align) : "left");
80 block->urgent = urgent ? json_object_get_int(urgent) : false;
81 block->name = name ? strdup(json_object_get_string(name)) : NULL;
82 block->instance = instance ?
83 strdup(json_object_get_string(instance)) : NULL;
84 if (markup) {
85 block->markup = false;
86 const char *markup_str = json_object_get_string(markup);
87 if (strcmp(markup_str, "pango") == 0) {
88 block->markup = true;
89 }
90 }
91 block->separator = separator ? json_object_get_int(separator) : true;
92 block->separator_block_width = separator_block_width ?
93 json_object_get_int(separator_block_width) : 9;
94 // Airblader features
95 block->background = background ?
96 parse_color(json_object_get_string(background)) : 0;
97 block->border = border ?
98 parse_color(json_object_get_string(border)) : 0;
99 block->border_top = border_top ? json_object_get_int(border_top) : 1;
100 block->border_bottom = border_bottom ?
101 json_object_get_int(border_bottom) : 1;
102 block->border_left = border_left ? json_object_get_int(border_left) : 1;
103 block->border_right = border_right ?
104 json_object_get_int(border_right) : 1;
105 wl_list_insert(&status->blocks, &block->link);
106 }
107 return true;
108}
109
110bool i3bar_handle_readable(struct status_line *status) {
111 struct i3bar_protocol_state *state = &status->i3bar_state;
112
113 char *cur = &state->buffer[state->buffer_index];
114 ssize_t n = read(status->read_fd, cur,
115 state->buffer_size - state->buffer_index);
116 if (n == 0) {
117 return 0;
118 }
119
120 if (n == (ssize_t)(state->buffer_size - state->buffer_index)) {
121 state->buffer_size = state->buffer_size * 2;
122 char *new_buffer = realloc(state->buffer, state->buffer_size);
123 if (!new_buffer) {
124 free(state->buffer);
125 status_error(status, "[failed to allocate buffer]");
126 return -1;
127 }
128 state->buffer = new_buffer;
129 }
130
131 bool redraw = false;
132 while (*cur) {
133 if (state->nodes[state->depth] == JSON_NODE_STRING) {
134 if (!state->escape && *cur == '"') {
135 --state->depth;
136 }
137 state->escape = !state->escape && *cur == '\\';
138 } else {
139 switch (*cur) {
140 case '[':
141 ++state->depth;
142 if (state->depth >
143 sizeof(state->nodes) / sizeof(state->nodes[0])) {
144 status_error(status, "[i3bar json too deep]");
145 return false;
146 }
147 state->nodes[state->depth] = JSON_NODE_ARRAY;
148 if (state->depth == 1) {
149 state->current_node = cur;
150 }
151 break;
152 case ']':
153 if (state->nodes[state->depth] != JSON_NODE_ARRAY) {
154 status_error(status, "[failed to parse i3bar json]");
155 return false;
156 }
157 --state->depth;
158 if (state->depth == 0) {
159 // cur[1] is valid since cur[0] != '\0'
160 char p = cur[1];
161 cur[1] = '\0';
162 redraw = i3bar_parse_json(
163 status, state->current_node) || redraw;
164 cur[1] = p;
165 memmove(state->buffer, cur,
166 state->buffer_size - (cur - state->buffer));
167 cur = state->buffer;
168 state->current_node = cur + 1;
169 }
170 break;
171 case '"':
172 ++state->depth;
173 if (state->depth >
174 sizeof(state->nodes) / sizeof(state->nodes[0])) {
175 status_error(status, "[i3bar json too deep]");
176 return false;
177 }
178 state->nodes[state->depth] = JSON_NODE_STRING;
179 break;
180 }
181 }
182 ++cur;
183 }
184 state->buffer_index = cur - state->buffer;
185 return redraw;
186}
187
188void i3bar_block_send_click(struct status_line *status,
189 struct i3bar_block *block, int x, int y, uint32_t button) {
190 wlr_log(L_DEBUG, "block %s clicked", block->name ? block->name : "(nil)");
191 if (!block->name || !status->i3bar_state.click_events) {
192 return;
193 }
194
195 struct json_object *event_json = json_object_new_object();
196 json_object_object_add(event_json, "name",
197 json_object_new_string(block->name));
198 if (block->instance) {
199 json_object_object_add(event_json, "instance",
200 json_object_new_string(block->instance));
201 }
202
203 json_object_object_add(event_json, "button", json_object_new_int(button));
204 json_object_object_add(event_json, "x", json_object_new_int(x));
205 json_object_object_add(event_json, "y", json_object_new_int(y));
206 if (dprintf(status->write_fd, "%s\n",
207 json_object_to_json_string(event_json)) < 0) {
208 status_error(status, "[failed to write click event]");
209 }
210 json_object_put(event_json);
211}
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 64583df0..ed5d9a31 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -323,7 +323,7 @@ void ipc_initialize(struct swaybar *bar, const char *bar_id) {
323 IPC_SUBSCRIBE, subscribe, &len)); 323 IPC_SUBSCRIBE, subscribe, &len));
324} 324}
325 325
326bool handle_ipc_event(struct swaybar *bar) { 326bool handle_ipc_readable(struct swaybar *bar) {
327 struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd); 327 struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd);
328 if (!resp) { 328 if (!resp) {
329 return false; 329 return false;
diff --git a/swaybar/meson.build b/swaybar/meson.build
index bf6f6d7a..d65edb11 100644
--- a/swaybar/meson.build
+++ b/swaybar/meson.build
@@ -3,6 +3,7 @@ executable(
3 'bar.c', 3 'bar.c',
4 'config.c', 4 'config.c',
5 'event_loop.c', 5 'event_loop.c',
6 'i3bar.c',
6 'ipc.c', 7 'ipc.c',
7 'main.c', 8 'main.c',
8 'render.c', 9 'render.c',
diff --git a/swaybar/render.c b/swaybar/render.c
index c2358724..6f3b0788 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -18,10 +18,33 @@ static const int ws_horizontal_padding = 5;
18static const double ws_vertical_padding = 1.5; 18static const double ws_vertical_padding = 1.5;
19static const double border_width = 1; 19static const double border_width = 1;
20 20
21static uint32_t render_status_line_error(cairo_t *cairo,
22 struct swaybar_config *config, const char *error,
23 double *x, uint32_t width, uint32_t height) {
24 if (!error) {
25 return 0;
26 }
27 cairo_set_source_u32(cairo, 0xFF0000FF);
28 static const int margin = 3;
29 int text_width, text_height;
30 get_text_size(cairo, config->font,
31 &text_width, &text_height, 1, false, "%s", error);
32 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
33 if (height < ideal_height) {
34 return ideal_height;
35 }
36 *x -= text_width + margin;
37 double text_y = height / 2.0 - text_height / 2.0;
38 cairo_move_to(cairo, *x, (int)floor(text_y));
39 pango_printf(cairo, config->font, 1, false, "%s", error);
40 *x -= margin;
41 return ideal_height;
42}
43
21static uint32_t render_status_line_text(cairo_t *cairo, 44static uint32_t render_status_line_text(cairo_t *cairo,
22 struct swaybar_config *config, struct status_line *status, 45 struct swaybar_config *config, const char *text,
23 bool focused, uint32_t width, uint32_t height) { 46 bool focused, double *x, uint32_t width, uint32_t height) {
24 if (!status->text) { 47 if (!text) {
25 return 0; 48 return 0;
26 } 49 }
27 cairo_set_source_u32(cairo, focused ? 50 cairo_set_source_u32(cairo, focused ?
@@ -29,38 +52,211 @@ static uint32_t render_status_line_text(cairo_t *cairo,
29 static const int margin = 3; 52 static const int margin = 3;
30 int text_width, text_height; 53 int text_width, text_height;
31 get_text_size(cairo, config->font, &text_width, &text_height, 54 get_text_size(cairo, config->font, &text_width, &text_height,
32 1, config->pango_markup, "%s", status->text); 55 1, config->pango_markup, "%s", text);
33 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 56 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
34 if (height < ideal_height) { 57 if (height < ideal_height) {
35 return ideal_height; 58 return ideal_height;
36 } 59 }
60 *x -= text_width + margin;
37 double text_y = height / 2.0 - text_height / 2.0; 61 double text_y = height / 2.0 - text_height / 2.0;
38 cairo_move_to(cairo, width - text_width - margin, (int)floor(text_y)); 62 cairo_move_to(cairo, *x, (int)floor(text_y));
39 pango_printf(cairo, config->font, 1, config->pango_markup, 63 pango_printf(cairo, config->font, 1, config->pango_markup, "%s", text);
40 "%s", status->text); 64 *x -= margin;
65 return ideal_height;
66}
67
68static void render_sharp_line(cairo_t *cairo, uint32_t color,
69 double x, double y, double width, double height) {
70 cairo_set_source_u32(cairo, color);
71 if (width > 1 && height > 1) {
72 cairo_rectangle(cairo, x, y, width, height);
73 cairo_fill(cairo);
74 } else {
75 if (width == 1) {
76 x += 0.5;
77 height += y;
78 width = x;
79 }
80 if (height == 1) {
81 y += 0.5;
82 width += x;
83 height = y;
84 }
85 cairo_move_to(cairo, x, y);
86 cairo_set_line_width(cairo, 1.0);
87 cairo_line_to(cairo, width, height);
88 cairo_stroke(cairo);
89 }
90}
91
92static void block_hotspot_callback(struct swaybar_output *output,
93 int x, int y, uint32_t button, void *data) {
94 struct i3bar_block *block = data;
95 struct status_line *status = output->bar->status;
96 i3bar_block_send_click(status, block, x, y, button);
97}
98
99static uint32_t render_status_block(cairo_t *cairo,
100 struct swaybar_config *config, struct swaybar_output *output,
101 struct i3bar_block *block, double *x,
102 uint32_t height, bool focused, bool edge) {
103 static const int margin = 3;
104 if (!block->full_text || !*block->full_text) {
105 return 0;
106 }
107
108 int text_width, text_height;
109 get_text_size(cairo, config->font, &text_width, &text_height,
110 1, block->markup, "%s", block->full_text);
111 int width = text_width;
112 if (width < block->min_width) {
113 width = block->min_width;
114 }
115 double block_width = width;
116 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
117 if (height < ideal_height) {
118 return ideal_height;
119 }
120
121 *x -= width;
122 if (block->border && block->border_left > 0) {
123 *x -= (block->border_left + margin);
124 block_width += block->border_left + margin;
125 }
126 if (block->border && block->border_right > 0) {
127 *x -= (block->border_right + margin);
128 block_width += block->border_right + margin;
129 }
130
131 int sep_width;
132 if (!edge) {
133 if (config->sep_symbol) {
134 int _height;
135 get_text_size(cairo, config->font, &sep_width, &_height,
136 1, false, "%s", config->sep_symbol);
137 uint32_t _ideal_height = _height + ws_vertical_padding * 2;
138 if (height < _ideal_height) {
139 return _height;
140 }
141 if (sep_width > block->separator_block_width) {
142 block->separator_block_width = sep_width + margin * 2;
143 }
144 }
145 *x -= block->separator_block_width;
146 } else {
147 *x -= margin;
148 }
149
150 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
151 hotspot->x = *x;
152 hotspot->y = 0;
153 hotspot->width = width;
154 hotspot->height = height;
155 hotspot->callback = block_hotspot_callback;
156 hotspot->destroy = NULL;
157 hotspot->data = block;
158 wl_list_insert(&output->hotspots, &hotspot->link);
159
160 double pos = *x;
161 if (block->background) {
162 cairo_set_source_u32(cairo, block->background);
163 cairo_rectangle(cairo, pos - 0.5, 1, block_width, height);
164 cairo_fill(cairo);
165 }
166
167 if (block->border && block->border_top > 0) {
168 render_sharp_line(cairo, block->border,
169 pos - 0.5, 1, block_width, block->border_top);
170 }
171 if (block->border && block->border_bottom > 0) {
172 render_sharp_line(cairo, block->border,
173 pos - 0.5, height - 1 - block->border_bottom,
174 block_width, block->border_bottom);
175 }
176 if (block->border != 0 && block->border_left > 0) {
177 render_sharp_line(cairo, block->border,
178 pos - 0.5, 1, block->border_left, height);
179 pos += block->border_left + margin;
180 }
181
182 double offset = 0;
183 if (strncmp(block->align, "left", 5) == 0) {
184 offset = pos;
185 } else if (strncmp(block->align, "right", 5) == 0) {
186 offset = pos + width - text_width;
187 } else if (strncmp(block->align, "center", 6) == 0) {
188 offset = pos + (width - text_width) / 2;
189 }
190 cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0);
191 uint32_t color = block->color ? *block->color : config->colors.statusline;
192 cairo_set_source_u32(cairo, color);
193 pango_printf(cairo, config->font, 1, block->markup, "%s", block->full_text);
194 pos += width;
195
196 if (block->border && block->border_right > 0) {
197 pos += margin;
198 render_sharp_line(cairo, block->border,
199 pos - 0.5, 1, block->border_right, height);
200 pos += block->border_right;
201 }
202
203 if (!edge && block->separator) {
204 if (focused) {
205 cairo_set_source_u32(cairo, config->colors.focused_separator);
206 } else {
207 cairo_set_source_u32(cairo, config->colors.separator);
208 }
209 if (config->sep_symbol) {
210 offset = pos + (block->separator_block_width - sep_width) / 2;
211 cairo_move_to(cairo, offset, margin);
212 pango_printf(cairo, config->font, 1, false,
213 "%s", config->sep_symbol);
214 } else {
215 cairo_set_line_width(cairo, 1);
216 cairo_move_to(cairo,
217 pos + block->separator_block_width / 2, margin);
218 cairo_line_to(cairo,
219 pos + block->separator_block_width / 2, height - margin);
220 cairo_stroke(cairo);
221 }
222 }
41 return ideal_height; 223 return ideal_height;
42} 224}
43 225
44static uint32_t render_status_line_i3bar(cairo_t *cairo, 226static uint32_t render_status_line_i3bar(cairo_t *cairo,
45 struct swaybar_config *config, struct status_line *status, 227 struct swaybar_config *config, struct swaybar_output *output,
46 bool focused, uint32_t width, uint32_t height) { 228 struct status_line *status, bool focused,
47 // TODO 229 double *x, uint32_t width, uint32_t height) {
48 return 0; 230 uint32_t max_height = 0;
231 bool edge = true;
232 struct i3bar_block *block;
233 wl_list_for_each(block, &status->blocks, link) {
234 uint32_t h = render_status_block(cairo, config, output,
235 block, x, height, focused, edge);
236 max_height = h > max_height ? h : max_height;
237 edge = false;
238 }
239 return max_height;
49} 240}
50 241
51static uint32_t render_status_line(cairo_t *cairo, 242static uint32_t render_status_line(cairo_t *cairo,
52 struct swaybar_config *config, struct status_line *status, 243 struct swaybar_config *config, struct swaybar_output *output,
53 bool focused, uint32_t width, uint32_t height) { 244 struct status_line *status, bool focused,
245 double *x, uint32_t width, uint32_t height) {
54 switch (status->protocol) { 246 switch (status->protocol) {
247 case PROTOCOL_ERROR:
248 return render_status_line_error(cairo,
249 config, status->text, x, width, height);
55 case PROTOCOL_TEXT: 250 case PROTOCOL_TEXT:
56 return render_status_line_text(cairo, 251 return render_status_line_text(cairo,
57 config, status, focused, width, height); 252 config, status->text, focused, x, width, height);
58 case PROTOCOL_I3BAR: 253 case PROTOCOL_I3BAR:
59 return render_status_line_i3bar(cairo, 254 return render_status_line_i3bar(cairo, config, output, status,
60 config, status, focused, width, height); 255 focused, x, width, height);
61 default: 256 case PROTOCOL_UNDEF:
62 return 0; 257 return 0;
63 } 258 }
259 return 0;
64} 260}
65 261
66static uint32_t render_binding_mode_indicator(cairo_t *cairo, 262static uint32_t render_binding_mode_indicator(cairo_t *cairo,
@@ -166,8 +362,8 @@ static uint32_t render_workspace_button(cairo_t *cairo,
166 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 362 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
167 hotspot->x = *x; 363 hotspot->x = *x;
168 hotspot->y = 0; 364 hotspot->y = 0;
169 hotspot->height = height;
170 hotspot->width = width; 365 hotspot->width = width;
366 hotspot->height = height;
171 hotspot->callback = workspace_hotspot_callback; 367 hotspot->callback = workspace_hotspot_callback;
172 hotspot->destroy = free; 368 hotspot->destroy = free;
173 hotspot->data = strdup(name); 369 hotspot->data = strdup(name);
@@ -180,6 +376,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
180static uint32_t render_to_cairo(cairo_t *cairo, 376static uint32_t render_to_cairo(cairo_t *cairo,
181 struct swaybar *bar, struct swaybar_output *output) { 377 struct swaybar *bar, struct swaybar_output *output) {
182 struct swaybar_config *config = bar->config; 378 struct swaybar_config *config = bar->config;
379 wlr_log(L_DEBUG, "output %p", output);
183 380
184 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 381 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
185 if (output->focused) { 382 if (output->focused) {
@@ -211,9 +408,10 @@ static uint32_t render_to_cairo(cairo_t *cairo,
211 cairo, config, config->mode, x, output->height); 408 cairo, config, config->mode, x, output->height);
212 max_height = h > max_height ? h : max_height; 409 max_height = h > max_height ? h : max_height;
213 } 410 }
411 x = output->width;
214 if (bar->status) { 412 if (bar->status) {
215 uint32_t h = render_status_line(cairo, config, bar->status, 413 uint32_t h = render_status_line(cairo, config, output, bar->status,
216 output->focused, output->width, output->height); 414 output->focused, &x, output->width, output->height);
217 max_height = h > max_height ? h : max_height; 415 max_height = h > max_height ? h : max_height;
218 } 416 }
219 417
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 3454f207..8afe4707 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -1,5 +1,6 @@
1#define _POSIX_C_SOURCE 1#define _POSIX_C_SOURCE
2#include <fcntl.h> 2#include <fcntl.h>
3#include <json-c/json.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <string.h> 5#include <string.h>
5#include <stdio.h> 6#include <stdio.h>
@@ -9,35 +10,83 @@
9#include "swaybar/status_line.h" 10#include "swaybar/status_line.h"
10#include "readline.h" 11#include "readline.h"
11 12
12bool handle_status_readable(struct status_line *status) { 13void status_error(struct status_line *status, const char *text) {
13 char *line = read_line_buffer(status->read, 14 close(status->read_fd);
14 status->buffer, status->buffer_size); 15 close(status->write_fd);
16 status->protocol = PROTOCOL_ERROR;
17 status->text = text;
18}
19
20bool status_handle_readable(struct status_line *status) {
21 char *line;
15 switch (status->protocol) { 22 switch (status->protocol) {
23 case PROTOCOL_ERROR:
24 return false;
16 case PROTOCOL_I3BAR: 25 case PROTOCOL_I3BAR:
17 // TODO 26 if (i3bar_handle_readable(status) > 0) {
27 return true;
28 }
18 break; 29 break;
19 case PROTOCOL_TEXT: 30 case PROTOCOL_TEXT:
20 status->text = line; 31 line = read_line_buffer(status->read,
32 status->text_state.buffer, status->text_state.buffer_size);
33 if (!line) {
34 status_error(status, "[error reading from status command]");
35 } else {
36 status->text = line;
37 }
21 return true; 38 return true;
22 case PROTOCOL_UNDEF: 39 case PROTOCOL_UNDEF:
40 line = read_line_buffer(status->read,
41 status->text_state.buffer, status->text_state.buffer_size);
23 if (!line) { 42 if (!line) {
43 status_error(status, "[error reading from status command]");
24 return false; 44 return false;
25 } 45 }
26 if (line[0] == '{') { 46 if (line[0] == '{') {
27 // TODO: JSON 47 json_object *proto = json_tokener_parse(line);
48 if (proto) {
49 json_object *version;
50 if (json_object_object_get_ex(proto, "version", &version)
51 && json_object_get_int(version) == 1) {
52 wlr_log(L_DEBUG, "Switched to i3bar protocol.");
53 status->protocol = PROTOCOL_I3BAR;
54 }
55 json_object *click_events;
56 if (json_object_object_get_ex(
57 proto, "click_events", &click_events)
58 && json_object_get_boolean(click_events)) {
59 wlr_log(L_DEBUG, "Enabled click events.");
60 status->i3bar_state.click_events = true;
61 const char *events_array = "[\n";
62 ssize_t len = strlen(events_array);
63 if (write(status->write_fd, events_array, len) != len) {
64 status_error(status,
65 "[failed to write to status command]");
66 }
67 }
68 json_object_put(proto);
69 }
70
71 status->protocol = PROTOCOL_I3BAR;
72 free(status->text_state.buffer);
73 wl_list_init(&status->blocks);
74 status->i3bar_state.buffer_size = 4096;
75 status->i3bar_state.buffer =
76 malloc(status->i3bar_state.buffer_size);
28 } else { 77 } else {
29 status->text = line;
30 status->protocol = PROTOCOL_TEXT; 78 status->protocol = PROTOCOL_TEXT;
79 status->text = line;
31 } 80 }
32 return false; 81 return true;
33 } 82 }
34 return false; 83 return false;
35} 84}
36 85
37struct status_line *status_line_init(char *cmd) { 86struct status_line *status_line_init(char *cmd) {
38 struct status_line *status = calloc(1, sizeof(struct status_line)); 87 struct status_line *status = calloc(1, sizeof(struct status_line));
39 status->buffer_size = 4096; 88 status->text_state.buffer_size = 8192;
40 status->buffer = malloc(status->buffer_size); 89 status->text_state.buffer = malloc(status->text_state.buffer_size);
41 90
42 int pipe_read_fd[2]; 91 int pipe_read_fd[2];
43 int pipe_write_fd[2]; 92 int pipe_write_fd[2];