summaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/CMakeLists.txt4
-rw-r--r--sway/commands.c6
-rw-r--r--sway/commands/bind.c5
-rw-r--r--sway/commands/move.c24
-rw-r--r--sway/commands/output.c74
-rw-r--r--sway/commands/set.c2
-rw-r--r--sway/config.c7
-rw-r--r--sway/container.c12
-rw-r--r--sway/criteria.c58
-rw-r--r--sway/handlers.c104
-rw-r--r--sway/input_state.c33
-rw-r--r--sway/ipc-json.c94
-rw-r--r--sway/ipc-server.c108
-rw-r--r--sway/main.c41
-rw-r--r--sway/sway-security.7.txt2
-rw-r--r--sway/sway.1.txt2
-rw-r--r--sway/workspace.c2
17 files changed, 416 insertions, 162 deletions
diff --git a/sway/CMakeLists.txt b/sway/CMakeLists.txt
index 11bec4df..48f7a7a8 100644
--- a/sway/CMakeLists.txt
+++ b/sway/CMakeLists.txt
@@ -94,6 +94,10 @@ endfunction()
94add_config(config config sway) 94add_config(config config sway)
95add_config(00-defaults security.d/00-defaults sway/security.d) 95add_config(00-defaults security.d/00-defaults sway/security.d)
96 96
97if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
98 add_config(10-freebsd security.d/10-freebsd sway/security.d)
99endif (CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
100
97if (A2X_FOUND) 101if (A2X_FOUND)
98 add_manpage(sway 1) 102 add_manpage(sway 1)
99 add_manpage(sway 5) 103 add_manpage(sway 5)
diff --git a/sway/commands.c b/sway/commands.c
index d55d9a96..c7dbf731 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -458,7 +458,11 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
458 if (!containers) { 458 if (!containers) {
459 current_container = get_focused_container(&root_container); 459 current_container = get_focused_container(&root_container);
460 } else if (containers->length == 0) { 460 } else if (containers->length == 0) {
461 break; 461 if (results) {
462 free_cmd_results(results);
463 }
464 results = cmd_results_new(CMD_FAILURE, argv[0], "No matching container");
465 goto cleanup;
462 } else { 466 } else {
463 current_container = (swayc_t *)containers->items[i]; 467 current_container = (swayc_t *)containers->items[i];
464 } 468 }
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index af5a01e5..d9ea37b7 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -61,10 +61,11 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) {
61 sym = ((char *)split->items[i])[strlen("button")] - '1' + M_LEFT_CLICK; 61 sym = ((char *)split->items[i])[strlen("button")] - '1' + M_LEFT_CLICK;
62 } 62 }
63 if (!sym) { 63 if (!sym) {
64 struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym",
65 "Unknown key '%s'", (char *)split->items[i]);
64 free_sway_binding(binding); 66 free_sway_binding(binding);
65 free_flat_list(split); 67 free_flat_list(split);
66 return cmd_results_new(CMD_INVALID, "bindsym", "Unknown key '%s'", 68 return ret;
67 (char *)split->items[i]);
68 } 69 }
69 xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t)); 70 xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
70 if (!key) { 71 if (!key) {
diff --git a/sway/commands/move.c b/sway/commands/move.c
index a38687c1..8d89f2ef 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -7,6 +7,7 @@
7#include "sway/output.h" 7#include "sway/output.h"
8#include "sway/workspace.h" 8#include "sway/workspace.h"
9#include "list.h" 9#include "list.h"
10#include "stringop.h"
10 11
11struct cmd_results *cmd_move(int argc, char **argv) { 12struct cmd_results *cmd_move(int argc, char **argv) {
12 struct cmd_results *error = NULL; 13 struct cmd_results *error = NULL;
@@ -59,18 +60,23 @@ struct cmd_results *cmd_move(int argc, char **argv) {
59 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views."); 60 return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
60 } 61 }
61 62
62 const char *ws_name = argv[3];
63 swayc_t *ws; 63 swayc_t *ws;
64 if (argc == 5 && strcasecmp(ws_name, "number") == 0) { 64 const char *num_name = NULL;
65 char *ws_name = NULL;
66 if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
65 // move "container to workspace number x" 67 // move "container to workspace number x"
66 ws_name = argv[4]; 68 num_name = argv[4];
67 ws = workspace_by_number(ws_name); 69 ws = workspace_by_number(num_name);
68 } else { 70 } else {
71 ws_name = join_args(argv + 3, argc - 3);
69 ws = workspace_by_name(ws_name); 72 ws = workspace_by_name(ws_name);
70 } 73 }
71 74
72 if (ws == NULL) { 75 if (ws == NULL) {
73 ws = workspace_create(ws_name); 76 ws = workspace_create(ws_name ? ws_name : num_name);
77 }
78 if (ws_name) {
79 free(ws_name);
74 } 80 }
75 move_container_to(view, get_focused_container(ws)); 81 move_container_to(view, get_focused_container(ws));
76 } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) { 82 } else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) {
@@ -161,11 +167,11 @@ struct cmd_results *cmd_move(int argc, char **argv) {
161 wlc_view_get_visible_geometry(view->handle, &g); 167 wlc_view_get_visible_geometry(view->handle, &g);
162 const struct wlc_size *size = wlc_output_get_resolution(output->handle); 168 const struct wlc_size *size = wlc_output_get_resolution(output->handle);
163 169
164 struct wlc_point origin; 170 double x_pos, y_pos;
165 wlc_pointer_get_position(&origin); 171 wlc_pointer_get_position_v2(&x_pos, &y_pos);
166 172
167 int32_t x = origin.x - g.size.w / 2; 173 int32_t x = x_pos - g.size.w / 2;
168 int32_t y = origin.y - g.size.h / 2; 174 int32_t y = y_pos - g.size.h / 2;
169 175
170 uint32_t w = size->w - g.size.w; 176 uint32_t w = size->w - g.size.w;
171 uint32_t h = size->h - g.size.h; 177 uint32_t h = size->h - g.size.h;
diff --git a/sway/commands/output.c b/sway/commands/output.c
index e5d4b317..911391d2 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -46,7 +46,8 @@ struct cmd_results *cmd_output(int argc, char **argv) {
46 output->enabled = 0; 46 output->enabled = 0;
47 } else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) { 47 } else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) {
48 if (++i >= argc) { 48 if (++i >= argc) {
49 return cmd_results_new(CMD_INVALID, "output", "Missing resolution argument."); 49 error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument.");
50 goto fail;
50 } 51 }
51 char *res = argv[i]; 52 char *res = argv[i];
52 char *x = strchr(res, 'x'); 53 char *x = strchr(res, 'x');
@@ -61,7 +62,8 @@ struct cmd_results *cmd_output(int argc, char **argv) {
61 // Format is 1234 4321 62 // Format is 1234 4321
62 width = atoi(res); 63 width = atoi(res);
63 if (++i >= argc) { 64 if (++i >= argc) {
64 return cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height)."); 65 error = cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height).");
66 goto fail;
65 } 67 }
66 res = argv[i]; 68 res = argv[i];
67 height = atoi(res); 69 height = atoi(res);
@@ -70,7 +72,8 @@ struct cmd_results *cmd_output(int argc, char **argv) {
70 output->height = height; 72 output->height = height;
71 } else if (strcasecmp(command, "position") == 0 || strcasecmp(command, "pos") == 0) { 73 } else if (strcasecmp(command, "position") == 0 || strcasecmp(command, "pos") == 0) {
72 if (++i >= argc) { 74 if (++i >= argc) {
73 return cmd_results_new(CMD_INVALID, "output", "Missing position argument."); 75 error = cmd_results_new(CMD_INVALID, "output", "Missing position argument.");
76 goto fail;
74 } 77 }
75 char *res = argv[i]; 78 char *res = argv[i];
76 char *c = strchr(res, ','); 79 char *c = strchr(res, ',');
@@ -85,7 +88,8 @@ struct cmd_results *cmd_output(int argc, char **argv) {
85 // Format is 1234 4321 88 // Format is 1234 4321
86 x = atoi(res); 89 x = atoi(res);
87 if (++i >= argc) { 90 if (++i >= argc) {
88 return cmd_results_new(CMD_INVALID, "output", "Missing position argument (y)."); 91 error = cmd_results_new(CMD_INVALID, "output", "Missing position argument (y).");
92 goto fail;
89 } 93 }
90 res = argv[i]; 94 res = argv[i];
91 y = atoi(res); 95 y = atoi(res);
@@ -94,25 +98,49 @@ struct cmd_results *cmd_output(int argc, char **argv) {
94 output->y = y; 98 output->y = y;
95 } else if (strcasecmp(command, "scale") == 0) { 99 } else if (strcasecmp(command, "scale") == 0) {
96 if (++i >= argc) { 100 if (++i >= argc) {
97 return cmd_results_new(CMD_INVALID, "output", "Missing scale parameter."); 101 error = cmd_results_new(CMD_INVALID, "output", "Missing scale parameter.");
102 goto fail;
98 } 103 }
99 output->scale = atoi(argv[i]); 104 output->scale = atoi(argv[i]);
100 } else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) { 105 } else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) {
101 wordexp_t p; 106 wordexp_t p;
102 if (++i >= argc) { 107 if (++i >= argc) {
103 return cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification."); 108 error = cmd_results_new(CMD_INVALID, "output", "Missing background file or color specification.");
109 goto fail;
104 } 110 }
105 if (i + 1 >= argc) { 111 if (i + 1 >= argc) {
106 return cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`."); 112 error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode or `solid_color`.");
113 goto fail;
107 } 114 }
108 if (strcasecmp(argv[argc - 1], "solid_color") == 0) { 115 if (strcasecmp(argv[i + 1], "solid_color") == 0) {
109 output->background = strdup(argv[argc - 2]); 116 output->background = strdup(argv[argc - 2]);
110 output->background_option = strdup("solid_color"); 117 output->background_option = strdup("solid_color");
111 } else { 118 } else {
112 char *src = join_args(argv + i, argc - i - 1); 119 // argv[i+j]=bg_option
113 char *mode = argv[argc - 1]; 120 bool valid = false;
121 char *mode;
122 size_t j;
123 for (j = 0; j < (size_t) (argc - i); ++j) {
124 mode = argv[i + j];
125 for (size_t k = 0; k < sizeof(bg_options) / sizeof(char *); ++k) {
126 if (strcasecmp(mode, bg_options[k]) == 0) {
127 valid = true;
128 break;
129 }
130 }
131 if (valid) {
132 break;
133 }
134 }
135 if (!valid) {
136 error = cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode.");
137 goto fail;
138 }
139
140 char *src = join_args(argv + i, j);
114 if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { 141 if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) {
115 return cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src); 142 error = cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src);
143 goto fail;
116 } 144 }
117 free(src); 145 free(src);
118 src = p.we_wordv[0]; 146 src = p.we_wordv[0];
@@ -132,27 +160,19 @@ struct cmd_results *cmd_output(int argc, char **argv) {
132 } 160 }
133 } 161 }
134 if (!src || access(src, F_OK) == -1) { 162 if (!src || access(src, F_OK) == -1) {
135 return cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src); 163 error = cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src);
136 } 164 wordfree(&p);
137 for (char *m = mode; *m; ++m) *m = tolower(*m); 165 goto fail;
138 // Check mode
139 bool valid = false;
140 size_t j;
141 for (j = 0; j < sizeof(bg_options) / sizeof(char *); ++j) {
142 if (strcasecmp(mode, bg_options[j]) == 0) {
143 valid = true;
144 break;
145 }
146 }
147 if (!valid) {
148 return cmd_results_new(CMD_INVALID, "output", "Invalid background scaling mode.");
149 } 166 }
167
150 output->background = strdup(src); 168 output->background = strdup(src);
151 output->background_option = strdup(mode); 169 output->background_option = strdup(mode);
152 if (src != p.we_wordv[0]) { 170 if (src != p.we_wordv[0]) {
153 free(src); 171 free(src);
154 } 172 }
155 wordfree(&p); 173 wordfree(&p);
174
175 i += j;
156 } 176 }
157 } 177 }
158 } 178 }
@@ -192,4 +212,8 @@ struct cmd_results *cmd_output(int argc, char **argv) {
192 } 212 }
193 213
194 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 214 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
215
216fail:
217 free_output_config(output);
218 return error;
195} 219}
diff --git a/sway/commands/set.c b/sway/commands/set.c
index e3d08dd3..46fc6d38 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -30,7 +30,7 @@ struct cmd_results *cmd_set(int argc, char **argv) {
30 if (!tmp) { 30 if (!tmp) {
31 return cmd_results_new(CMD_FAILURE, "set", "Not possible to create variable $'%s'", argv[0]); 31 return cmd_results_new(CMD_FAILURE, "set", "Not possible to create variable $'%s'", argv[0]);
32 } 32 }
33 snprintf(tmp, size, "$%s", argv[0]); 33 snprintf(tmp, size+1, "$%s", argv[0]);
34 34
35 argv[0] = tmp; 35 argv[0] = tmp;
36 } 36 }
diff --git a/sway/config.c b/sway/config.c
index e0b65615..5b2b6569 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -128,6 +128,8 @@ void free_output_config(struct output_config *oc) {
128 return; 128 return;
129 } 129 }
130 free(oc->name); 130 free(oc->name);
131 free(oc->background);
132 free(oc->background_option);
131 free(oc); 133 free(oc);
132} 134}
133 135
@@ -548,9 +550,12 @@ bool load_main_config(const char *file, bool is_active) {
548 strcpy(_path, base); 550 strcpy(_path, base);
549 strcat(_path, ent->d_name); 551 strcat(_path, ent->d_name);
550 lstat(_path, &s); 552 lstat(_path, &s);
551 if (S_ISREG(s.st_mode)) { 553 if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') {
552 list_add(secconfigs, _path); 554 list_add(secconfigs, _path);
553 } 555 }
556 else {
557 free(_path);
558 }
554 ent = readdir(dir); 559 ent = readdir(dir);
555 } 560 }
556 closedir(dir); 561 closedir(dir);
diff --git a/sway/container.c b/sway/container.c
index 358ba767..718608ff 100644
--- a/sway/container.c
+++ b/sway/container.c
@@ -516,11 +516,11 @@ swayc_t *destroy_view(swayc_t *view) {
516 return NULL; 516 return NULL;
517 } 517 }
518 sway_log(L_DEBUG, "Destroying view '%p'", view); 518 sway_log(L_DEBUG, "Destroying view '%p'", view);
519 swayc_t *parent = view->parent;
520 free_swayc(view); 519 free_swayc(view);
521 520
522 // Destroy empty containers 521 // Destroy empty containers
523 if (parent->type == C_CONTAINER) { 522 swayc_t *parent = view->parent;
523 if (parent && parent->type == C_CONTAINER) {
524 return destroy_container(parent); 524 return destroy_container(parent);
525 } 525 }
526 return parent; 526 return parent;
@@ -707,8 +707,10 @@ swayc_t *container_under_pointer(void) {
707 if (lookup->children && !lookup->unmanaged) { 707 if (lookup->children && !lookup->unmanaged) {
708 return NULL; 708 return NULL;
709 } 709 }
710 struct wlc_point origin; 710 double x, y;
711 wlc_pointer_get_position(&origin); 711 wlc_pointer_get_position_v2(&x, &y);
712 struct wlc_point origin = { .x = x, .y = y };
713
712 while (lookup && lookup->type != C_VIEW) { 714 while (lookup && lookup->type != C_VIEW) {
713 int i; 715 int i;
714 int len; 716 int len;
@@ -847,7 +849,6 @@ int swayc_gap(swayc_t *container) {
847 849
848void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) { 850void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) {
849 if (container) { 851 if (container) {
850 f(container, data);
851 int i; 852 int i;
852 if (container->children) { 853 if (container->children) {
853 for (i = 0; i < container->children->length; ++i) { 854 for (i = 0; i < container->children->length; ++i) {
@@ -861,6 +862,7 @@ void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), voi
861 container_map(child, f, data); 862 container_map(child, f, data);
862 } 863 }
863 } 864 }
865 f(container, data);
864 } 866 }
865} 867}
866 868
diff --git a/sway/criteria.c b/sway/criteria.c
index 04683f66..f5fe40cb 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -12,9 +12,12 @@
12 12
13enum criteria_type { // *must* keep in sync with criteria_strings[] 13enum criteria_type { // *must* keep in sync with criteria_strings[]
14 CRIT_CLASS, 14 CRIT_CLASS,
15 CRIT_CON_ID,
15 CRIT_CON_MARK, 16 CRIT_CON_MARK,
17 CRIT_FLOATING,
16 CRIT_ID, 18 CRIT_ID,
17 CRIT_INSTANCE, 19 CRIT_INSTANCE,
20 CRIT_TILING,
18 CRIT_TITLE, 21 CRIT_TITLE,
19 CRIT_URGENT, 22 CRIT_URGENT,
20 CRIT_WINDOW_ROLE, 23 CRIT_WINDOW_ROLE,
@@ -25,9 +28,12 @@ enum criteria_type { // *must* keep in sync with criteria_strings[]
25 28
26static const char * const criteria_strings[CRIT_LAST] = { 29static const char * const criteria_strings[CRIT_LAST] = {
27 [CRIT_CLASS] = "class", 30 [CRIT_CLASS] = "class",
31 [CRIT_CON_ID] = "con_id",
28 [CRIT_CON_MARK] = "con_mark", 32 [CRIT_CON_MARK] = "con_mark",
33 [CRIT_FLOATING] = "floating",
29 [CRIT_ID] = "id", 34 [CRIT_ID] = "id",
30 [CRIT_INSTANCE] = "instance", 35 [CRIT_INSTANCE] = "instance",
36 [CRIT_TILING] = "tiling",
31 [CRIT_TITLE] = "title", 37 [CRIT_TITLE] = "title",
32 [CRIT_URGENT] = "urgent", // either "latest" or "oldest" ... 38 [CRIT_URGENT] = "urgent", // either "latest" or "oldest" ...
33 [CRIT_WINDOW_ROLE] = "window_role", 39 [CRIT_WINDOW_ROLE] = "window_role",
@@ -108,6 +114,7 @@ static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str
108 114
109 char **argv = *buf = calloc(max_tokens, sizeof(char*)); 115 char **argv = *buf = calloc(max_tokens, sizeof(char*));
110 argv[0] = base; // this needs to be freed by caller 116 argv[0] = base; // this needs to be freed by caller
117 bool quoted = true;
111 118
112 *argc = 1; // uneven = name, even = value 119 *argc = 1; // uneven = name, even = value
113 while (*head && *argc < max_tokens) { 120 while (*head && *argc < max_tokens) {
@@ -128,7 +135,8 @@ static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str
128 if (*(namep) == ' ') { 135 if (*(namep) == ' ') {
129 namep = strrchr(namep, ' ') + 1; 136 namep = strrchr(namep, ' ') + 1;
130 } 137 }
131 argv[(*argc)++] = namep; 138 argv[*argc] = namep;
139 *argc += 1;
132 } 140 }
133 } else if (*head == '"') { 141 } else if (*head == '"') {
134 if (*argc % 2 != 0) { 142 if (*argc % 2 != 0) {
@@ -137,21 +145,38 @@ static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str
137 "Found quoted value where it was not expected"); 145 "Found quoted value where it was not expected");
138 } else if (!valp) { // value starts here 146 } else if (!valp) { // value starts here
139 valp = head + 1; 147 valp = head + 1;
148 quoted = true;
140 } else { 149 } else {
141 // value ends here 150 // value ends here
142 argv[(*argc)++] = valp; 151 argv[*argc] = valp;
152 *argc += 1;
143 *head = '\0'; 153 *head = '\0';
144 valp = NULL; 154 valp = NULL;
145 namep = head + 1; 155 namep = head + 1;
146 } 156 }
147 } else if (*argc % 2 == 0 && !valp && *head != ' ') { 157 } else if (*argc % 2 == 0 && *head != ' ') {
148 // We're expecting a quoted value, haven't found one yet, and this 158 // parse unquoted values
149 // is not an empty space. 159 if (!valp) {
150 return strdup("Unable to parse criteria: " 160 quoted = false;
151 "Names must be unquoted, values must be quoted"); 161 valp = head; // value starts here
162 }
163 } else if (valp && !quoted && *head == ' ') {
164 // value ends here
165 argv[*argc] = valp;
166 *argc += 1;
167 *head = '\0';
168 valp = NULL;
169 namep = head + 1;
152 } 170 }
153 head++; 171 head++;
154 } 172 }
173
174 // catch last unquoted value if needed
175 if (valp && !quoted && !*head) {
176 argv[*argc] = valp;
177 *argc += 1;
178 }
179
155 return NULL; 180 return NULL;
156} 181}
157 182
@@ -263,6 +288,15 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
263 matches++; 288 matches++;
264 } 289 }
265 break; 290 break;
291 case CRIT_CON_ID: {
292 char *endptr;
293 size_t crit_id = strtoul(crit->raw, &endptr, 10);
294
295 if (*endptr == 0 && cont->id == crit_id) {
296 ++matches;
297 }
298 break;
299 }
266 case CRIT_CON_MARK: 300 case CRIT_CON_MARK:
267 if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) { 301 if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) {
268 // Make sure it isn't matching the NUL string 302 // Make sure it isn't matching the NUL string
@@ -271,6 +305,11 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
271 } 305 }
272 } 306 }
273 break; 307 break;
308 case CRIT_FLOATING:
309 if (cont->is_floating) {
310 matches++;
311 }
312 break;
274 case CRIT_ID: 313 case CRIT_ID:
275 if (!cont->app_id) { 314 if (!cont->app_id) {
276 // ignore 315 // ignore
@@ -290,6 +329,11 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
290 matches++; 329 matches++;
291 } 330 }
292 break; 331 break;
332 case CRIT_TILING:
333 if (!cont->is_floating) {
334 matches++;
335 }
336 break;
293 case CRIT_TITLE: 337 case CRIT_TITLE:
294 if (!cont->name) { 338 if (!cont->name) {
295 // ignore 339 // ignore
diff --git a/sway/handlers.c b/sway/handlers.c
index 052789ca..d37142a9 100644
--- a/sway/handlers.c
+++ b/sway/handlers.c
@@ -452,6 +452,7 @@ static bool handle_view_created(wlc_handle handle) {
452 wlc_view_focus(handle); 452 wlc_view_focus(handle);
453 wlc_view_bring_to_front(handle); 453 wlc_view_bring_to_front(handle);
454 newview = new_floating_view(handle); 454 newview = new_floating_view(handle);
455 /* fallthrough */
455 case WLC_BIT_POPUP: 456 case WLC_BIT_POPUP:
456 wlc_view_bring_to_front(handle); 457 wlc_view_bring_to_front(handle);
457 break; 458 break;
@@ -552,21 +553,24 @@ static void handle_view_destroyed(wlc_handle handle) {
552 bool fullscreen = swayc_is_fullscreen(view); 553 bool fullscreen = swayc_is_fullscreen(view);
553 remove_view_from_scratchpad(view); 554 remove_view_from_scratchpad(view);
554 swayc_t *parent = destroy_view(view); 555 swayc_t *parent = destroy_view(view);
555 if (fullscreen) { 556 if (parent) {
556 parent->fullscreen = NULL; 557 if (fullscreen) {
557 } 558 parent->fullscreen = NULL;
559 }
558 560
559 // Destroy empty workspaces 561 ipc_event_window(parent, "close");
560 if (parent->type == C_WORKSPACE &&
561 parent->children->length == 0 &&
562 parent->floating->length == 0 &&
563 swayc_active_workspace() != parent &&
564 !parent->visible) {
565 parent = destroy_workspace(parent);
566 }
567 562
568 arrange_windows(parent, -1, -1); 563 // Destroy empty workspaces
569 ipc_event_window(parent, "close"); 564 if (parent->type == C_WORKSPACE &&
565 parent->children->length == 0 &&
566 parent->floating->length == 0 &&
567 swayc_active_workspace() != parent &&
568 !parent->visible) {
569 parent = destroy_workspace(parent);
570 }
571
572 arrange_windows(parent, -1, -1);
573 }
570 } else { 574 } else {
571 // Is it unmanaged? 575 // Is it unmanaged?
572 int i; 576 int i;
@@ -582,6 +586,15 @@ static void handle_view_destroyed(wlc_handle handle) {
582 } 586 }
583 } 587 }
584 } 588 }
589 // Is it in the scratchpad?
590 for (i = 0; i < scratchpad->length; ++i) {
591 swayc_t *item = scratchpad->items[i];
592 if (item->handle == handle) {
593 list_del(scratchpad, i);
594 destroy_view(item);
595 break;
596 }
597 }
585 } 598 }
586 set_focused_container(get_focused_view(&root_container)); 599 set_focused_container(get_focused_view(&root_container));
587} 600}
@@ -805,11 +818,11 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier
805 struct sway_binding *binding = mode->bindings->items[i]; 818 struct sway_binding *binding = mode->bindings->items[i];
806 if ((modifiers->mods ^ binding->modifiers) == 0) { 819 if ((modifiers->mods ^ binding->modifiers) == 0) {
807 switch (state) { 820 switch (state) {
808 case WLC_KEY_STATE_PRESSED: { 821 case WLC_KEY_STATE_PRESSED:
809 if (!binding->release && valid_bindsym(binding)) { 822 if (!binding->release && valid_bindsym(binding)) {
810 list_add(candidates, binding); 823 list_add(candidates, binding);
811 } 824 }
812 } 825 break;
813 case WLC_KEY_STATE_RELEASED: 826 case WLC_KEY_STATE_RELEASED:
814 if (binding->release && handle_bindsym_release(binding)) { 827 if (binding->release && handle_bindsym_release(binding)) {
815 list_free(candidates); 828 list_free(candidates);
@@ -842,12 +855,13 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier
842 return EVENT_PASSTHROUGH; 855 return EVENT_PASSTHROUGH;
843} 856}
844 857
845static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct wlc_point *origin) { 858static bool handle_pointer_motion(wlc_handle handle, uint32_t time, double x, double y) {
846 if (desktop_shell.is_locked) { 859 if (desktop_shell.is_locked) {
847 return EVENT_PASSTHROUGH; 860 return EVENT_PASSTHROUGH;
848 } 861 }
849 862
850 struct wlc_point new_origin = *origin; 863 double new_x = x;
864 double new_y = y;
851 // Switch to adjacent output if touching output edge. 865 // Switch to adjacent output if touching output edge.
852 // 866 //
853 // Since this doesn't currently support moving windows between outputs we 867 // Since this doesn't currently support moving windows between outputs we
@@ -856,45 +870,43 @@ static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct
856 !pointer_state.left.held && !pointer_state.right.held && !pointer_state.scroll.held) { 870 !pointer_state.left.held && !pointer_state.right.held && !pointer_state.scroll.held) {
857 871
858 swayc_t *output = swayc_active_output(), *adjacent = NULL; 872 swayc_t *output = swayc_active_output(), *adjacent = NULL;
859 struct wlc_point abs_pos = *origin; 873 struct wlc_point abs_pos = { .x = x + output->x, .y = y + output->y };
860 abs_pos.x += output->x; 874 if (x <= 0) { // Left edge
861 abs_pos.y += output->y;
862 if (origin->x == 0) { // Left edge
863 if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT, &abs_pos, false))) { 875 if ((adjacent = swayc_adjacent_output(output, MOVE_LEFT, &abs_pos, false))) {
864 if (workspace_switch(swayc_active_workspace_for(adjacent))) { 876 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
865 new_origin.x = adjacent->width; 877 new_x = adjacent->width;
866 // adjust for differently aligned outputs (well, this is 878 // adjust for differently aligned outputs (well, this is
867 // only correct when the two outputs have the same 879 // only correct when the two outputs have the same
868 // resolution or the same dpi I guess, it should take 880 // resolution or the same dpi I guess, it should take
869 // physical attributes into account) 881 // physical attributes into account)
870 new_origin.y += (output->y - adjacent->y); 882 new_y += (output->y - adjacent->y);
871 } 883 }
872 } 884 }
873 } else if ((double)origin->x == output->width) { // Right edge 885 } else if (x >= output->width) { // Right edge
874 if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT, &abs_pos, false))) { 886 if ((adjacent = swayc_adjacent_output(output, MOVE_RIGHT, &abs_pos, false))) {
875 if (workspace_switch(swayc_active_workspace_for(adjacent))) { 887 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
876 new_origin.x = 0; 888 new_x = 0;
877 new_origin.y += (output->y - adjacent->y); 889 new_y += (output->y - adjacent->y);
878 } 890 }
879 } 891 }
880 } else if (origin->y == 0) { // Top edge 892 } else if (y <= 0) { // Top edge
881 if ((adjacent = swayc_adjacent_output(output, MOVE_UP, &abs_pos, false))) { 893 if ((adjacent = swayc_adjacent_output(output, MOVE_UP, &abs_pos, false))) {
882 if (workspace_switch(swayc_active_workspace_for(adjacent))) { 894 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
883 new_origin.y = adjacent->height; 895 new_y = adjacent->height;
884 new_origin.x += (output->x - adjacent->x); 896 new_x += (output->x - adjacent->x);
885 } 897 }
886 } 898 }
887 } else if ((double)origin->y == output->height) { // Bottom edge 899 } else if (y >= output->height) { // Bottom edge
888 if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN, &abs_pos, false))) { 900 if ((adjacent = swayc_adjacent_output(output, MOVE_DOWN, &abs_pos, false))) {
889 if (workspace_switch(swayc_active_workspace_for(adjacent))) { 901 if (workspace_switch(swayc_active_workspace_for(adjacent))) {
890 new_origin.y = 0; 902 new_y = 0;
891 new_origin.x += (output->x - adjacent->x); 903 new_x += (output->x - adjacent->x);
892 } 904 }
893 } 905 }
894 } 906 }
895 } 907 }
896 908
897 pointer_position_set(&new_origin, false); 909 pointer_position_set(new_x, new_y, false);
898 910
899 swayc_t *focused = get_focused_container(&root_container); 911 swayc_t *focused = get_focused_container(&root_container);
900 if (focused->type == C_VIEW) { 912 if (focused->type == C_VIEW) {
@@ -935,15 +947,15 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w
935 struct sway_binding *binding = mode->bindings->items[i]; 947 struct sway_binding *binding = mode->bindings->items[i];
936 if ((modifiers->mods ^ binding->modifiers) == 0) { 948 if ((modifiers->mods ^ binding->modifiers) == 0) {
937 switch (state) { 949 switch (state) {
938 case WLC_BUTTON_STATE_PRESSED: { 950 case WLC_BUTTON_STATE_PRESSED:
939 if (!binding->release && handle_bindsym(binding, button, 0)) { 951 if (!binding->release && handle_bindsym(binding, button, 0)) {
940 return EVENT_HANDLED; 952 return EVENT_HANDLED;
941 } 953 }
954 break;
955 case WLC_BUTTON_STATE_RELEASED:
956 if (binding->release && handle_bindsym(binding, button, 0)) {
957 return EVENT_HANDLED;
942 } 958 }
943 case WLC_BUTTON_STATE_RELEASED:
944 if (binding->release && handle_bindsym(binding, button, 0)) {
945 return EVENT_HANDLED;
946 }
947 break; 959 break;
948 } 960 }
949 } 961 }
@@ -1084,16 +1096,8 @@ bool handle_pointer_scroll(wlc_handle view, uint32_t time, const struct wlc_modi
1084 return EVENT_PASSTHROUGH; 1096 return EVENT_PASSTHROUGH;
1085} 1097}
1086 1098
1087static void clip_test_cb(void *data, const char *type, int fd) {
1088 const char *str = data;
1089 write(fd, str, strlen(str));
1090 close(fd);
1091}
1092
1093static void handle_wlc_ready(void) { 1099static void handle_wlc_ready(void) {
1094 sway_log(L_DEBUG, "Compositor is ready, executing cmds in queue"); 1100 sway_log(L_DEBUG, "Compositor is ready, executing cmds in queue");
1095 const char *type = "text/plain;charset=utf-8";
1096 wlc_set_selection("test", &type, 1, &clip_test_cb);
1097 // Execute commands until there are none left 1101 // Execute commands until there are none left
1098 config->active = true; 1102 config->active = true;
1099 while (config->cmd_queue->length) { 1103 while (config->cmd_queue->length) {
@@ -1122,7 +1126,7 @@ void register_wlc_handlers() {
1122 wlc_set_view_request_state_cb(handle_view_state_request); 1126 wlc_set_view_request_state_cb(handle_view_state_request);
1123 wlc_set_view_properties_updated_cb(handle_view_properties_updated); 1127 wlc_set_view_properties_updated_cb(handle_view_properties_updated);
1124 wlc_set_keyboard_key_cb(handle_key); 1128 wlc_set_keyboard_key_cb(handle_key);
1125 wlc_set_pointer_motion_cb(handle_pointer_motion); 1129 wlc_set_pointer_motion_cb_v2(handle_pointer_motion);
1126 wlc_set_pointer_button_cb(handle_pointer_button); 1130 wlc_set_pointer_button_cb(handle_pointer_button);
1127 wlc_set_pointer_scroll_cb(handle_pointer_scroll); 1131 wlc_set_pointer_scroll_cb(handle_pointer_scroll);
1128 wlc_set_compositor_ready_cb(handle_wlc_ready); 1132 wlc_set_compositor_ready_cb(handle_wlc_ready);
diff --git a/sway/input_state.c b/sway/input_state.c
index 68df17de..04aafd37 100644
--- a/sway/input_state.c
+++ b/sway/input_state.c
@@ -202,13 +202,13 @@ static void reset_initial_sibling(void) {
202 pointer_state.mode = 0; 202 pointer_state.mode = 0;
203} 203}
204 204
205void pointer_position_set(struct wlc_point *new_origin, bool force_focus) { 205void pointer_position_set(double new_x, double new_y, bool force_focus) {
206 struct wlc_point origin; 206 double x, y;
207 wlc_pointer_get_position(&origin); 207 wlc_pointer_get_position_v2(&x, &y);
208 pointer_state.delta.x = new_origin->x - origin.x; 208 pointer_state.delta.x = new_x - x;
209 pointer_state.delta.y = new_origin->y - origin.y; 209 pointer_state.delta.y = new_y - y;
210 210
211 wlc_pointer_set_position(new_origin); 211 wlc_pointer_set_position_v2(new_x, new_y);
212 212
213 // Update view under pointer 213 // Update view under pointer
214 swayc_t *prev_view = pointer_state.view; 214 swayc_t *prev_view = pointer_state.view;
@@ -226,10 +226,7 @@ void pointer_position_set(struct wlc_point *new_origin, bool force_focus) {
226} 226}
227 227
228void center_pointer_on(swayc_t *view) { 228void center_pointer_on(swayc_t *view) {
229 struct wlc_point new_origin; 229 pointer_position_set(view->x + view->width/2, view->y + view->height/2, true);
230 new_origin.x = view->x + view->width/2;
231 new_origin.y = view->y + view->height/2;
232 pointer_position_set(&new_origin, true);
233} 230}
234 231
235// Mode set left/right click 232// Mode set left/right click
@@ -269,10 +266,10 @@ static void pointer_mode_set_resizing(void) {
269 int midway_x = initial.ptr->x + initial.ptr->width/2; 266 int midway_x = initial.ptr->x + initial.ptr->width/2;
270 int midway_y = initial.ptr->y + initial.ptr->height/2; 267 int midway_y = initial.ptr->y + initial.ptr->height/2;
271 268
272 struct wlc_point origin; 269 double x, y;
273 wlc_pointer_get_position(&origin); 270 wlc_pointer_get_position_v2(&x, &y);
274 lock.left = origin.x > midway_x; 271 lock.left = x > midway_x;
275 lock.top = origin.y > midway_y; 272 lock.top = y > midway_y;
276 273
277 if (initial.ptr->is_floating) { 274 if (initial.ptr->is_floating) {
278 pointer_state.mode = M_RESIZING | M_FLOATING; 275 pointer_state.mode = M_RESIZING | M_FLOATING;
@@ -346,10 +343,10 @@ void pointer_mode_update(void) {
346 pointer_state.mode = 0; 343 pointer_state.mode = 0;
347 return; 344 return;
348 } 345 }
349 struct wlc_point origin; 346 double x, y;
350 wlc_pointer_get_position(&origin); 347 wlc_pointer_get_position_v2(&x, &y);
351 int dx = origin.x; 348 int dx = x;
352 int dy = origin.y; 349 int dy = y;
353 350
354 switch (pointer_state.mode) { 351 switch (pointer_state.mode) {
355 case M_FLOATING | M_DRAGGING: 352 case M_FLOATING | M_DRAGGING:
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 31de53f0..6ab63c75 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -138,7 +138,6 @@ static void ipc_json_describe_workspace(swayc_t *workspace, json_object *object)
138 138
139 json_object_object_add(object, "num", json_object_new_int(num)); 139 json_object_object_add(object, "num", json_object_new_int(num));
140 json_object_object_add(object, "output", (workspace->parent) ? json_object_new_string(workspace->parent->name) : NULL); 140 json_object_object_add(object, "output", (workspace->parent) ? json_object_new_string(workspace->parent->name) : NULL);
141 json_object_object_add(object, "urgent", json_object_new_boolean(false));
142 json_object_object_add(object, "type", json_object_new_string("workspace")); 141 json_object_object_add(object, "type", json_object_new_string("workspace"));
143 json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout)); 142 json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout));
144} 143}
@@ -156,38 +155,14 @@ static const char *ipc_json_get_scratchpad_state(swayc_t *c) {
156 155
157static void ipc_json_describe_view(swayc_t *c, json_object *object) { 156static void ipc_json_describe_view(swayc_t *c, json_object *object) {
158 json_object *props = json_object_new_object(); 157 json_object *props = json_object_new_object();
159 float percent = ipc_json_child_percentage(c);
160 const char *layout = (c->parent->type == C_CONTAINER) ?
161 ipc_json_layout_description(c->parent->layout) : "none";
162 const char *last_layout = (c->parent->type == C_CONTAINER) ?
163 ipc_json_layout_description(c->parent->prev_layout) : "none";
164 wlc_handle parent = wlc_view_get_parent(c->handle);
165
166 json_object_object_add(object, "type", json_object_new_string((c->is_floating) ? "floating_con" : "con")); 158 json_object_object_add(object, "type", json_object_new_string((c->is_floating) ? "floating_con" : "con"));
167 159
160 wlc_handle parent = wlc_view_get_parent(c->handle);
168 json_object_object_add(object, "scratchpad_state", 161 json_object_object_add(object, "scratchpad_state",
169 json_object_new_string(ipc_json_get_scratchpad_state(c))); 162 json_object_new_string(ipc_json_get_scratchpad_state(c)));
170 json_object_object_add(object, "percent", (percent > 0) ? json_object_new_double(percent) : NULL);
171 // TODO: make urgency actually work once Sway supports it
172 json_object_object_add(object, "urgent", json_object_new_boolean(false));
173 json_object_object_add(object, "layout",
174 (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout));
175 json_object_object_add(object, "last_split_layout",
176 (strcmp(last_layout, "null") == 0) ? NULL : json_object_new_string(last_layout));
177 json_object_object_add(object, "workspace_layout",
178 json_object_new_string(ipc_json_layout_description(swayc_parent_by_type(c, C_WORKSPACE)->workspace_layout)));
179
180 json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c)));
181 json_object_object_add(object, "current_border_width", json_object_new_int(c->border_thickness));
182
183 json_object_object_add(object, "rect", ipc_json_create_rect(c));
184 json_object_object_add(object, "deco_rect", ipc_json_create_rect_from_geometry(c->title_bar_geometry));
185 json_object_object_add(object, "geometry", ipc_json_create_rect_from_geometry(c->cached_geometry));
186 json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry));
187 163
188 json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL); 164 json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL);
189 165
190 json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat
191 json_object_object_add(props, "class", c->class ? json_object_new_string(c->class) : 166 json_object_object_add(props, "class", c->class ? json_object_new_string(c->class) :
192 c->app_id ? json_object_new_string(c->app_id) : NULL); 167 c->app_id ? json_object_new_string(c->app_id) : NULL);
193 json_object_object_add(props, "instance", c->instance ? json_object_new_string(c->instance) : 168 json_object_object_add(props, "instance", c->instance ? json_object_new_string(c->instance) :
@@ -203,9 +178,29 @@ static void ipc_json_describe_view(swayc_t *c, json_object *object) {
203 c->is_floating ? "auto_on" : "auto_off")); // we can't state the cause 178 c->is_floating ? "auto_on" : "auto_off")); // we can't state the cause
204 179
205 json_object_object_add(object, "app_id", c->app_id ? json_object_new_string(c->app_id) : NULL); 180 json_object_object_add(object, "app_id", c->app_id ? json_object_new_string(c->app_id) : NULL);
181
182 if (c->parent) {
183 const char *layout = (c->parent->type == C_CONTAINER) ?
184 ipc_json_layout_description(c->parent->layout) : "none";
185 const char *last_layout = (c->parent->type == C_CONTAINER) ?
186 ipc_json_layout_description(c->parent->prev_layout) : "none";
187 json_object_object_add(object, "layout",
188 (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout));
189 json_object_object_add(object, "last_split_layout",
190 (strcmp(last_layout, "null") == 0) ? NULL : json_object_new_string(last_layout));
191 json_object_object_add(object, "workspace_layout",
192 json_object_new_string(ipc_json_layout_description(swayc_parent_by_type(c, C_WORKSPACE)->workspace_layout)));
193 }
194}
195
196static void ipc_json_describe_root(swayc_t *c, json_object *object) {
197 json_object_object_add(object, "type", json_object_new_string("root"));
198 json_object_object_add(object, "layout", json_object_new_string("splith"));
206} 199}
207 200
208json_object *ipc_json_describe_container(swayc_t *c) { 201json_object *ipc_json_describe_container(swayc_t *c) {
202 float percent = ipc_json_child_percentage(c);
203
209 if (!(sway_assert(c, "Container must not be null."))) { 204 if (!(sway_assert(c, "Container must not be null."))) {
210 return NULL; 205 return NULL;
211 } 206 }
@@ -218,9 +213,19 @@ json_object *ipc_json_describe_container(swayc_t *c) {
218 json_object_object_add(object, "visible", json_object_new_boolean(c->visible)); 213 json_object_object_add(object, "visible", json_object_new_boolean(c->visible));
219 json_object_object_add(object, "focused", json_object_new_boolean(c == current_focus)); 214 json_object_object_add(object, "focused", json_object_new_boolean(c == current_focus));
220 215
216 json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c)));
217 json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry));
218 json_object_object_add(object, "deco_rect", ipc_json_create_rect_from_geometry(c->title_bar_geometry));
219 json_object_object_add(object, "geometry", ipc_json_create_rect_from_geometry(c->cached_geometry));
220 json_object_object_add(object, "percent", (percent > 0) ? json_object_new_double(percent) : NULL);
221 json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat
222 // TODO: make urgency actually work once Sway supports it
223 json_object_object_add(object, "urgent", json_object_new_boolean(false));
224 json_object_object_add(object, "current_border_width", json_object_new_int(c->border_thickness));
225
221 switch (c->type) { 226 switch (c->type) {
222 case C_ROOT: 227 case C_ROOT:
223 json_object_object_add(object, "type", json_object_new_string("root")); 228 ipc_json_describe_root(c, object);
224 break; 229 break;
225 230
226 case C_OUTPUT: 231 case C_OUTPUT:
@@ -451,21 +456,50 @@ json_object *ipc_json_describe_container_recursive(swayc_t *c) {
451 int i; 456 int i;
452 457
453 json_object *floating = json_object_new_array(); 458 json_object *floating = json_object_new_array();
454 if (c->type != C_VIEW && c->floating && c->floating->length > 0) { 459 if (c->type != C_VIEW && c->floating) {
455 for (i = 0; i < c->floating->length; ++i) { 460 for (i = 0; i < c->floating->length; ++i) {
456 json_object_array_add(floating, ipc_json_describe_container_recursive(c->floating->items[i])); 461 swayc_t *item = c->floating->items[i];
462 json_object_array_add(floating, ipc_json_describe_container_recursive(item));
457 } 463 }
458 } 464 }
459 json_object_object_add(object, "floating_nodes", floating); 465 json_object_object_add(object, "floating_nodes", floating);
460 466
461 json_object *children = json_object_new_array(); 467 json_object *children = json_object_new_array();
462 if (c->type != C_VIEW && c->children && c->children->length > 0) { 468 if (c->type != C_VIEW && c->children) {
463 for (i = 0; i < c->children->length; ++i) { 469 for (i = 0; i < c->children->length; ++i) {
464 json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i])); 470 json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i]));
465 } 471 }
466 } 472 }
467 json_object_object_add(object, "nodes", children); 473 json_object_object_add(object, "nodes", children);
468 474
475 json_object *focus = json_object_new_array();
476 if (c->type != C_VIEW) {
477 if (c->focused) {
478 json_object_array_add(focus, json_object_new_double(c->focused->id));
479 }
480 if (c->floating) {
481 for (i = 0; i < c->floating->length; ++i) {
482 swayc_t *item = c->floating->items[i];
483 if (item == c->focused) {
484 continue;
485 }
486
487 json_object_array_add(focus, json_object_new_double(item->id));
488 }
489 }
490 if (c->children) {
491 for (i = 0; i < c->children->length; ++i) {
492 swayc_t *item = c->children->items[i];
493 if (item == c->focused) {
494 continue;
495 }
496
497 json_object_array_add(focus, json_object_new_double(item->id));
498 }
499 }
500 }
501 json_object_object_add(object, "focus", focus);
502
469 if (c->type == C_ROOT) { 503 if (c->type == C_ROOT) {
470 json_object *scratchpad_json = json_object_new_array(); 504 json_object *scratchpad_json = json_object_new_array();
471 if (scratchpad->length > 0) { 505 if (scratchpad->length > 0) {
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 5e1e93ce..4ce2b7eb 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -41,11 +41,15 @@ static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
41 41
42struct ipc_client { 42struct ipc_client {
43 struct wlc_event_source *event_source; 43 struct wlc_event_source *event_source;
44 struct wlc_event_source *writable_event_source;
44 int fd; 45 int fd;
45 uint32_t payload_length; 46 uint32_t payload_length;
46 uint32_t security_policy; 47 uint32_t security_policy;
47 enum ipc_command_type current_command; 48 enum ipc_command_type current_command;
48 enum ipc_command_type subscribed_events; 49 enum ipc_command_type subscribed_events;
50 size_t write_buffer_len;
51 size_t write_buffer_size;
52 char *write_buffer;
49}; 53};
50 54
51static list_t *ipc_get_pixel_requests = NULL; 55static list_t *ipc_get_pixel_requests = NULL;
@@ -72,6 +76,7 @@ struct get_clipboard_request {
72struct sockaddr_un *ipc_user_sockaddr(void); 76struct sockaddr_un *ipc_user_sockaddr(void);
73int ipc_handle_connection(int fd, uint32_t mask, void *data); 77int ipc_handle_connection(int fd, uint32_t mask, void *data);
74int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); 78int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);
79int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data);
75void ipc_client_disconnect(struct ipc_client *client); 80void ipc_client_disconnect(struct ipc_client *client);
76void ipc_client_handle_command(struct ipc_client *client); 81void ipc_client_handle_command(struct ipc_client *client);
77bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); 82bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length);
@@ -182,6 +187,12 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {
182 close(client_fd); 187 close(client_fd);
183 return 0; 188 return 0;
184 } 189 }
190 if ((flags = fcntl(client_fd, F_GETFL)) == -1
191 || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) {
192 sway_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket");
193 close(client_fd);
194 return 0;
195 }
185 196
186 struct ipc_client* client = malloc(sizeof(struct ipc_client)); 197 struct ipc_client* client = malloc(sizeof(struct ipc_client));
187 if (!client) { 198 if (!client) {
@@ -193,10 +204,22 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {
193 client->fd = client_fd; 204 client->fd = client_fd;
194 client->subscribed_events = 0; 205 client->subscribed_events = 0;
195 client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); 206 client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client);
207 client->writable_event_source = NULL;
208
209 client->write_buffer_size = 128;
210 client->write_buffer_len = 0;
211 client->write_buffer = malloc(client->write_buffer_size);
212 if (!client->write_buffer) {
213 sway_log(L_ERROR, "Unable to allocate ipc client write buffer");
214 close(client_fd);
215 return 0;
216 }
196 217
197 pid_t pid = get_client_pid(client->fd); 218 pid_t pid = get_client_pid(client->fd);
198 client->security_policy = get_ipc_policy_mask(pid); 219 client->security_policy = get_ipc_policy_mask(pid);
199 220
221 sway_log(L_DEBUG, "New client: fd %d, pid %d", client_fd, pid);
222
200 list_add(ipc_client_list, client); 223 list_add(ipc_client_list, client);
201 224
202 return 0; 225 return 0;
@@ -219,6 +242,8 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
219 return 0; 242 return 0;
220 } 243 }
221 244
245 sway_log(L_DEBUG, "Client %d readable", client->fd);
246
222 int read_available; 247 int read_available;
223 if (ioctl(client_fd, FIONREAD, &read_available) == -1) { 248 if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
224 sway_log_errno(L_INFO, "Unable to read IPC socket buffer size"); 249 sway_log_errno(L_INFO, "Unable to read IPC socket buffer size");
@@ -240,6 +265,7 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
240 265
241 uint8_t buf[ipc_header_size]; 266 uint8_t buf[ipc_header_size];
242 uint32_t *buf32 = (uint32_t*)(buf + sizeof(ipc_magic)); 267 uint32_t *buf32 = (uint32_t*)(buf + sizeof(ipc_magic));
268 // Should be fully available, because read_available >= ipc_header_size
243 ssize_t received = recv(client_fd, buf, ipc_header_size, 0); 269 ssize_t received = recv(client_fd, buf, ipc_header_size, 0);
244 if (received == -1) { 270 if (received == -1) {
245 sway_log_errno(L_INFO, "Unable to receive header from IPC client"); 271 sway_log_errno(L_INFO, "Unable to receive header from IPC client");
@@ -263,6 +289,48 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
263 return 0; 289 return 0;
264} 290}
265 291
292int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
293 struct ipc_client *client = data;
294
295 if (mask & WLC_EVENT_ERROR) {
296 sway_log(L_ERROR, "IPC Client socket error, removing client");
297 ipc_client_disconnect(client);
298 return 0;
299 }
300
301 if (mask & WLC_EVENT_HANGUP) {
302 sway_log(L_DEBUG, "Client %d hung up", client->fd);
303 ipc_client_disconnect(client);
304 return 0;
305 }
306
307 if (client->write_buffer_len <= 0) {
308 return 0;
309 }
310
311 sway_log(L_DEBUG, "Client %d writable", client->fd);
312
313 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
314
315 if (written == -1 && errno == EAGAIN) {
316 return 0;
317 } else if (written == -1) {
318 sway_log_errno(L_INFO, "Unable to send data from queue to IPC client");
319 ipc_client_disconnect(client);
320 return 0;
321 }
322
323 memmove(client->write_buffer, client->write_buffer + written, client->write_buffer_len - written);
324 client->write_buffer_len -= written;
325
326 if (client->write_buffer_len == 0 && client->writable_event_source) {
327 wlc_event_source_remove(client->writable_event_source);
328 client->writable_event_source = NULL;
329 }
330
331 return 0;
332}
333
266void ipc_client_disconnect(struct ipc_client *client) { 334void ipc_client_disconnect(struct ipc_client *client) {
267 if (!sway_assert(client != NULL, "client != NULL")) { 335 if (!sway_assert(client != NULL, "client != NULL")) {
268 return; 336 return;
@@ -274,9 +342,13 @@ void ipc_client_disconnect(struct ipc_client *client) {
274 342
275 sway_log(L_INFO, "IPC Client %d disconnected", client->fd); 343 sway_log(L_INFO, "IPC Client %d disconnected", client->fd);
276 wlc_event_source_remove(client->event_source); 344 wlc_event_source_remove(client->event_source);
345 if (client->writable_event_source) {
346 wlc_event_source_remove(client->writable_event_source);
347 }
277 int i = 0; 348 int i = 0;
278 while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++; 349 while (i < ipc_client_list->length && ipc_client_list->items[i] != client) i++;
279 list_del(ipc_client_list, i); 350 list_del(ipc_client_list, i);
351 free(client->write_buffer);
280 close(client->fd); 352 close(client->fd);
281 free(client); 353 free(client);
282} 354}
@@ -608,6 +680,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
608 return; 680 return;
609 } 681 }
610 if (client->payload_length > 0) { 682 if (client->payload_length > 0) {
683 // Payload should be fully available
611 ssize_t received = recv(client->fd, buf, client->payload_length, 0); 684 ssize_t received = recv(client->fd, buf, client->payload_length, 0);
612 if (received == -1) 685 if (received == -1)
613 { 686 {
@@ -874,17 +947,36 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
874 data32[0] = payload_length; 947 data32[0] = payload_length;
875 data32[1] = client->current_command; 948 data32[1] = client->current_command;
876 949
877 if (write(client->fd, data, ipc_header_size) == -1) { 950 while (client->write_buffer_len + ipc_header_size + payload_length >=
878 sway_log_errno(L_INFO, "Unable to send header to IPC client"); 951 client->write_buffer_size) {
952 client->write_buffer_size *= 2;
953 }
954
955 // TODO: reduce the limit back to 4 MB when screenshooter is implemented
956 if (client->write_buffer_size > (1 << 28)) { // 256 MB
957 sway_log(L_ERROR, "Client write buffer too big, disconnecting client");
958 ipc_client_disconnect(client);
879 return false; 959 return false;
880 } 960 }
881 961
882 if (write(client->fd, payload, payload_length) == -1) { 962 char *new_buffer = realloc(client->write_buffer, client->write_buffer_size);
883 sway_log_errno(L_INFO, "Unable to send payload to IPC client"); 963 if (!new_buffer) {
964 sway_log(L_ERROR, "Unable to reallocate ipc client write buffer");
965 ipc_client_disconnect(client);
884 return false; 966 return false;
885 } 967 }
968 client->write_buffer = new_buffer;
969
970 memcpy(client->write_buffer + client->write_buffer_len, data, ipc_header_size);
971 client->write_buffer_len += ipc_header_size;
972 memcpy(client->write_buffer + client->write_buffer_len, payload, payload_length);
973 client->write_buffer_len += payload_length;
974
975 if (!client->writable_event_source) {
976 client->writable_event_source = wlc_event_loop_add_fd(client->fd, WLC_EVENT_WRITABLE, ipc_client_handle_writable, client);
977 }
886 978
887 sway_log(L_DEBUG, "Send IPC reply: %s", payload); 979 sway_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);
888 980
889 return true; 981 return true;
890} 982}
@@ -984,11 +1076,7 @@ void ipc_event_window(swayc_t *window, const char *change) {
984 sway_log(L_DEBUG, "Sending window::%s event", change); 1076 sway_log(L_DEBUG, "Sending window::%s event", change);
985 json_object *obj = json_object_new_object(); 1077 json_object *obj = json_object_new_object();
986 json_object_object_add(obj, "change", json_object_new_string(change)); 1078 json_object_object_add(obj, "change", json_object_new_string(change));
987 if (strcmp(change, "close") == 0 || !window) { 1079 json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window));
988 json_object_object_add(obj, "container", NULL);
989 } else {
990 json_object_object_add(obj, "container", ipc_json_describe_container(window));
991 }
992 1080
993 const char *json_string = json_object_to_json_string(obj); 1081 const char *json_string = json_object_to_json_string(obj);
994 ipc_send_event(json_string, IPC_EVENT_WINDOW); 1082 ipc_send_event(json_string, IPC_EVENT_WINDOW);
diff --git a/sway/main.c b/sway/main.c
index 82375e0b..6d13955c 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -53,6 +53,46 @@ static void wlc_log_handler(enum wlc_log_type type, const char *str) {
53 } 53 }
54} 54}
55 55
56void detect_raspi() {
57 bool raspi = false;
58 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r");
59 if (!f) {
60 return;
61 }
62 char *line;
63 while(!feof(f)) {
64 if (!(line = read_line(f))) {
65 break;
66 }
67 if (strstr(line, "Raspberry Pi")) {
68 raspi = true;
69 }
70 free(line);
71 }
72 fclose(f);
73 FILE *g = fopen("/proc/modules", "r");
74 if (!g) {
75 return;
76 }
77 bool vc4 = false;
78 while (!feof(g)) {
79 if (!(line = read_line(g))) {
80 break;
81 }
82 if (strstr(line, "vc4")) {
83 vc4 = true;
84 }
85 free(line);
86 }
87 fclose(g);
88 if (!vc4 && raspi) {
89 fprintf(stderr, "\x1B[1;31mWarning: You have a "
90 "Raspberry Pi, but the vc4 Module is "
91 "not loaded! Set 'dtoverlay=vc4-kms-v3d'"
92 "in /boot/config.txt and reboot.\x1B[0m\n");
93 }
94}
95
56void detect_proprietary() { 96void detect_proprietary() {
57 FILE *f = fopen("/proc/modules", "r"); 97 FILE *f = fopen("/proc/modules", "r");
58 if (!f) { 98 if (!f) {
@@ -366,6 +406,7 @@ int main(int argc, char **argv) {
366 log_distro(); 406 log_distro();
367 log_env(); 407 log_env();
368 detect_proprietary(); 408 detect_proprietary();
409 detect_raspi();
369 410
370 input_devices = create_list(); 411 input_devices = create_list();
371 412
diff --git a/sway/sway-security.7.txt b/sway/sway-security.7.txt
index ec6df1f3..aee3793c 100644
--- a/sway/sway-security.7.txt
+++ b/sway/sway-security.7.txt
@@ -237,4 +237,4 @@ Authors
237------- 237-------
238Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open 238Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
239source contributors. For more information about sway development, see 239source contributors. For more information about sway development, see
240<https://github.com/SirCmpwn/sway>. 240<https://github.com/swaywm/sway>.
diff --git a/sway/sway.1.txt b/sway/sway.1.txt
index bc827bd5..4a1aef99 100644
--- a/sway/sway.1.txt
+++ b/sway/sway.1.txt
@@ -119,7 +119,7 @@ Authors
119 119
120Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open 120Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
121source contributors. For more information about sway development, see 121source contributors. For more information about sway development, see
122<https://github.com/SirCmpwn/sway>. 122<https://github.com/swaywm/sway>.
123 123
124See Also 124See Also
125-------- 125--------
diff --git a/sway/workspace.c b/sway/workspace.c
index 29cacce9..e0367190 100644
--- a/sway/workspace.c
+++ b/sway/workspace.c
@@ -61,7 +61,7 @@ char *workspace_next_name(const char *output_name) {
61 // workspace n 61 // workspace n
62 char *cmd = argsep(&cmdlist, " "); 62 char *cmd = argsep(&cmdlist, " ");
63 if (cmdlist) { 63 if (cmdlist) {
64 name = argsep(&cmdlist, " ,;"); 64 name = argsep(&cmdlist, ",;");
65 } 65 }
66 66
67 if (strcmp("workspace", cmd) == 0 && name) { 67 if (strcmp("workspace", cmd) == 0 && name) {