diff options
Diffstat (limited to 'sway')
-rw-r--r-- | sway/commands.c | 42 | ||||
-rw-r--r-- | sway/commands/assign.c | 58 | ||||
-rw-r--r-- | sway/commands/for_window.c | 35 | ||||
-rw-r--r-- | sway/criteria.c | 684 | ||||
-rw-r--r-- | sway/desktop/wl_shell.c | 4 | ||||
-rw-r--r-- | sway/desktop/xdg_shell_v6.c | 4 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 69 | ||||
-rw-r--r-- | sway/tree/view.c | 113 |
8 files changed, 488 insertions, 521 deletions
diff --git a/sway/commands.c b/sway/commands.c index 811f6cfa..2e1cdc2c 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -12,7 +12,6 @@ | |||
12 | #include "sway/security.h" | 12 | #include "sway/security.h" |
13 | #include "sway/input/input-manager.h" | 13 | #include "sway/input/input-manager.h" |
14 | #include "sway/input/seat.h" | 14 | #include "sway/input/seat.h" |
15 | #include "sway/tree/view.h" | ||
16 | #include "stringop.h" | 15 | #include "stringop.h" |
17 | #include "log.h" | 16 | #include "log.h" |
18 | 17 | ||
@@ -284,7 +283,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { | |||
284 | char *head = exec; | 283 | char *head = exec; |
285 | char *cmdlist; | 284 | char *cmdlist; |
286 | char *cmd; | 285 | char *cmd; |
287 | list_t *views = NULL; | 286 | list_t *containers = NULL; |
288 | 287 | ||
289 | if (seat == NULL) { | 288 | if (seat == NULL) { |
290 | // passing a NULL seat means we just pick the default seat | 289 | // passing a NULL seat means we just pick the default seat |
@@ -301,18 +300,31 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { | |||
301 | // Extract criteria (valid for this command list only). | 300 | // Extract criteria (valid for this command list only). |
302 | bool has_criteria = false; | 301 | bool has_criteria = false; |
303 | if (*head == '[') { | 302 | if (*head == '[') { |
304 | char *error = NULL; | 303 | has_criteria = true; |
305 | struct criteria *criteria = criteria_parse(head, &error); | 304 | ++head; |
306 | if (!criteria) { | 305 | char *criteria_string = argsep(&head, "]"); |
307 | results = cmd_results_new(CMD_INVALID, head, | 306 | if (head) { |
308 | "%s", error); | 307 | ++head; |
309 | free(error); | 308 | list_t *tokens = create_list(); |
309 | char *error; | ||
310 | |||
311 | if ((error = extract_crit_tokens(tokens, criteria_string))) { | ||
312 | wlr_log(L_DEBUG, "criteria string parse error: %s", error); | ||
313 | results = cmd_results_new(CMD_INVALID, criteria_string, | ||
314 | "Can't parse criteria string: %s", error); | ||
315 | free(error); | ||
316 | free(tokens); | ||
317 | goto cleanup; | ||
318 | } | ||
319 | containers = container_for_crit_tokens(tokens); | ||
320 | |||
321 | free(tokens); | ||
322 | } else { | ||
323 | if (!results) { | ||
324 | results = cmd_results_new(CMD_INVALID, criteria_string, "Unmatched ["); | ||
325 | } | ||
310 | goto cleanup; | 326 | goto cleanup; |
311 | } | 327 | } |
312 | views = criteria_get_views(criteria); | ||
313 | head += strlen(criteria->raw); | ||
314 | criteria_destroy(criteria); | ||
315 | has_criteria = true; | ||
316 | // Skip leading whitespace | 328 | // Skip leading whitespace |
317 | head += strspn(head, whitespace); | 329 | head += strspn(head, whitespace); |
318 | } | 330 | } |
@@ -369,9 +381,8 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { | |||
369 | } | 381 | } |
370 | free_cmd_results(res); | 382 | free_cmd_results(res); |
371 | } else { | 383 | } else { |
372 | for (int i = 0; i < views->length; ++i) { | 384 | for (int i = 0; i < containers->length; ++i) { |
373 | struct sway_view *view = views->items[i]; | 385 | config->handler_context.current_container = containers->items[i]; |
374 | config->handler_context.current_container = view->swayc; | ||
375 | struct cmd_results *res = handler->handle(argc-1, argv+1); | 386 | struct cmd_results *res = handler->handle(argc-1, argv+1); |
376 | if (res->status != CMD_SUCCESS) { | 387 | if (res->status != CMD_SUCCESS) { |
377 | free_argv(argc, argv); | 388 | free_argv(argc, argv); |
@@ -389,7 +400,6 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { | |||
389 | } while(head); | 400 | } while(head); |
390 | cleanup: | 401 | cleanup: |
391 | free(exec); | 402 | free(exec); |
392 | free(views); | ||
393 | if (!results) { | 403 | if (!results) { |
394 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); | 404 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); |
395 | } | 405 | } |
diff --git a/sway/commands/assign.c b/sway/commands/assign.c index 9d15e166..eb7329aa 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c | |||
@@ -5,7 +5,6 @@ | |||
5 | #include "sway/criteria.h" | 5 | #include "sway/criteria.h" |
6 | #include "list.h" | 6 | #include "list.h" |
7 | #include "log.h" | 7 | #include "log.h" |
8 | #include "stringop.h" | ||
9 | 8 | ||
10 | struct cmd_results *cmd_assign(int argc, char **argv) { | 9 | struct cmd_results *cmd_assign(int argc, char **argv) { |
11 | struct cmd_results *error = NULL; | 10 | struct cmd_results *error = NULL; |
@@ -13,39 +12,46 @@ struct cmd_results *cmd_assign(int argc, char **argv) { | |||
13 | return error; | 12 | return error; |
14 | } | 13 | } |
15 | 14 | ||
16 | // Create criteria | 15 | char *criteria = *argv++; |
17 | char *err_str = NULL; | ||
18 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | ||
19 | if (!criteria) { | ||
20 | error = cmd_results_new(CMD_INVALID, "assign", err_str); | ||
21 | free(err_str); | ||
22 | return error; | ||
23 | } | ||
24 | |||
25 | ++argv; | ||
26 | int target_len = argc - 1; | ||
27 | 16 | ||
28 | if (strncmp(*argv, "→", strlen("→")) == 0) { | 17 | if (strncmp(*argv, "→", strlen("→")) == 0) { |
29 | if (argc < 3) { | 18 | if (argc < 3) { |
30 | return cmd_results_new(CMD_INVALID, "assign", "Missing workspace"); | 19 | return cmd_results_new(CMD_INVALID, "assign", "Missing workspace"); |
31 | } | 20 | } |
32 | ++argv; | 21 | argv++; |
33 | --target_len; | ||
34 | } | 22 | } |
35 | 23 | ||
36 | if (strcmp(*argv, "output") == 0) { | 24 | char *movecmd = "move container to workspace "; |
37 | criteria->type = CT_ASSIGN_OUTPUT; | 25 | size_t arglen = strlen(movecmd) + strlen(*argv) + 1; |
38 | ++argv; | 26 | char *cmdlist = calloc(1, arglen); |
39 | --target_len; | 27 | if (!cmdlist) { |
40 | } else { | 28 | return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate command list"); |
41 | criteria->type = CT_ASSIGN_WORKSPACE; | ||
42 | } | 29 | } |
30 | snprintf(cmdlist, arglen, "%s%s", movecmd, *argv); | ||
43 | 31 | ||
44 | criteria->target = join_args(argv, target_len); | 32 | struct criteria *crit = malloc(sizeof(struct criteria)); |
45 | 33 | if (!crit) { | |
46 | list_add(config->criteria, criteria); | 34 | free(cmdlist); |
47 | wlr_log(L_DEBUG, "assign: '%s' -> '%s' added", criteria->raw, | 35 | return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate criteria"); |
48 | criteria->target); | 36 | } |
37 | crit->crit_raw = strdup(criteria); | ||
38 | crit->cmdlist = cmdlist; | ||
39 | crit->tokens = create_list(); | ||
40 | char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw); | ||
49 | 41 | ||
50 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 42 | if (err_str) { |
43 | error = cmd_results_new(CMD_INVALID, "assign", err_str); | ||
44 | free(err_str); | ||
45 | free_criteria(crit); | ||
46 | } else if (crit->tokens->length == 0) { | ||
47 | error = cmd_results_new(CMD_INVALID, "assign", "Found no name/value pairs in criteria"); | ||
48 | free_criteria(crit); | ||
49 | } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) { | ||
50 | wlr_log(L_DEBUG, "assign: Duplicate, skipping."); | ||
51 | free_criteria(crit); | ||
52 | } else { | ||
53 | wlr_log(L_DEBUG, "assign: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist); | ||
54 | list_add(config->criteria, crit); | ||
55 | } | ||
56 | return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
51 | } | 57 | } |
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index 8c425a1d..dd5461f0 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c | |||
@@ -11,20 +11,31 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { | |||
11 | if ((error = checkarg(argc, "for_window", EXPECTED_AT_LEAST, 2))) { | 11 | if ((error = checkarg(argc, "for_window", EXPECTED_AT_LEAST, 2))) { |
12 | return error; | 12 | return error; |
13 | } | 13 | } |
14 | // add command to a criteria/command pair that is run against views when they appear. | ||
15 | char *criteria = argv[0], *cmdlist = join_args(argv + 1, argc - 1); | ||
14 | 16 | ||
15 | char *err_str = NULL; | 17 | struct criteria *crit = calloc(sizeof(struct criteria), 1); |
16 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | 18 | if (!crit) { |
17 | if (!criteria) { | 19 | return cmd_results_new(CMD_FAILURE, "for_window", "Unable to allocate criteria"); |
20 | } | ||
21 | crit->crit_raw = strdup(criteria); | ||
22 | crit->cmdlist = cmdlist; | ||
23 | crit->tokens = create_list(); | ||
24 | char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw); | ||
25 | |||
26 | if (err_str) { | ||
18 | error = cmd_results_new(CMD_INVALID, "for_window", err_str); | 27 | error = cmd_results_new(CMD_INVALID, "for_window", err_str); |
19 | free(err_str); | 28 | free(err_str); |
20 | return error; | 29 | free_criteria(crit); |
30 | } else if (crit->tokens->length == 0) { | ||
31 | error = cmd_results_new(CMD_INVALID, "for_window", "Found no name/value pairs in criteria"); | ||
32 | free_criteria(crit); | ||
33 | } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) { | ||
34 | wlr_log(L_DEBUG, "for_window: Duplicate, skipping."); | ||
35 | free_criteria(crit); | ||
36 | } else { | ||
37 | wlr_log(L_DEBUG, "for_window: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist); | ||
38 | list_add(config->criteria, crit); | ||
21 | } | 39 | } |
22 | 40 | return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL); | |
23 | criteria->type = CT_COMMAND; | ||
24 | criteria->cmdlist = join_args(argv + 1, argc - 1); | ||
25 | |||
26 | list_add(config->criteria, criteria); | ||
27 | wlr_log(L_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist); | ||
28 | |||
29 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
30 | } | 41 | } |
diff --git a/sway/criteria.c b/sway/criteria.c index 7da790e6..22e9a49b 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -11,381 +11,435 @@ | |||
11 | #include "list.h" | 11 | #include "list.h" |
12 | #include "log.h" | 12 | #include "log.h" |
13 | 13 | ||
14 | bool criteria_is_empty(struct criteria *criteria) { | 14 | enum criteria_type { // *must* keep in sync with criteria_strings[] |
15 | return !criteria->title | 15 | CRIT_APP_ID, |
16 | && !criteria->app_id | 16 | CRIT_CLASS, |
17 | && !criteria->class | 17 | CRIT_CON_ID, |
18 | && !criteria->instance | 18 | CRIT_CON_MARK, |
19 | && !criteria->con_mark | 19 | CRIT_FLOATING, |
20 | && !criteria->con_id | 20 | CRIT_ID, |
21 | && !criteria->id | 21 | CRIT_INSTANCE, |
22 | && !criteria->window_role | 22 | CRIT_TILING, |
23 | && !criteria->window_type | 23 | CRIT_TITLE, |
24 | && !criteria->floating | 24 | CRIT_URGENT, |
25 | && !criteria->tiling | 25 | CRIT_WINDOW_ROLE, |
26 | && !criteria->urgent | 26 | CRIT_WINDOW_TYPE, |
27 | && !criteria->workspace; | 27 | CRIT_WORKSPACE, |
28 | } | 28 | CRIT_LAST |
29 | 29 | }; | |
30 | void criteria_destroy(struct criteria *criteria) { | ||
31 | pcre_free(criteria->title); | ||
32 | pcre_free(criteria->app_id); | ||
33 | pcre_free(criteria->class); | ||
34 | pcre_free(criteria->instance); | ||
35 | pcre_free(criteria->con_mark); | ||
36 | pcre_free(criteria->window_role); | ||
37 | free(criteria->workspace); | ||
38 | |||
39 | free(criteria->raw); | ||
40 | free(criteria); | ||
41 | } | ||
42 | |||
43 | static int regex_cmp(const char *item, const pcre *regex) { | ||
44 | return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); | ||
45 | } | ||
46 | 30 | ||
47 | static bool criteria_matches_view(struct criteria *criteria, | 31 | static const char * const criteria_strings[CRIT_LAST] = { |
48 | struct sway_view *view) { | 32 | [CRIT_APP_ID] = "app_id", |
49 | if (criteria->title) { | 33 | [CRIT_CLASS] = "class", |
50 | const char *title = view_get_title(view); | 34 | [CRIT_CON_ID] = "con_id", |
51 | if (!title || regex_cmp(title, criteria->title) != 0) { | 35 | [CRIT_CON_MARK] = "con_mark", |
52 | return false; | 36 | [CRIT_FLOATING] = "floating", |
53 | } | 37 | [CRIT_ID] = "id", |
54 | } | 38 | [CRIT_INSTANCE] = "instance", |
39 | [CRIT_TILING] = "tiling", | ||
40 | [CRIT_TITLE] = "title", | ||
41 | [CRIT_URGENT] = "urgent", // either "latest" or "oldest" ... | ||
42 | [CRIT_WINDOW_ROLE] = "window_role", | ||
43 | [CRIT_WINDOW_TYPE] = "window_type", | ||
44 | [CRIT_WORKSPACE] = "workspace" | ||
45 | }; | ||
55 | 46 | ||
56 | if (criteria->app_id) { | 47 | /** |
57 | const char *app_id = view_get_app_id(view); | 48 | * A single criteria token (ie. value/regex pair), |
58 | if (!app_id || regex_cmp(app_id, criteria->app_id) != 0) { | 49 | * e.g. 'class="some class regex"'. |
59 | return false; | 50 | */ |
60 | } | 51 | struct crit_token { |
61 | } | 52 | enum criteria_type type; |
53 | pcre *regex; | ||
54 | char *raw; | ||
55 | }; | ||
62 | 56 | ||
63 | if (criteria->class) { | 57 | static void free_crit_token(struct crit_token *crit) { |
64 | const char *class = view_get_class(view); | 58 | pcre_free(crit->regex); |
65 | if (!class || regex_cmp(class, criteria->class) != 0) { | 59 | free(crit->raw); |
66 | return false; | 60 | free(crit); |
67 | } | 61 | } |
68 | } | ||
69 | 62 | ||
70 | if (criteria->instance) { | 63 | static void free_crit_tokens(list_t *crit_tokens) { |
71 | const char *instance = view_get_instance(view); | 64 | for (int i = 0; i < crit_tokens->length; i++) { |
72 | if (!instance || regex_cmp(instance, criteria->instance) != 0) { | 65 | free_crit_token(crit_tokens->items[i]); |
73 | return false; | ||
74 | } | ||
75 | } | 66 | } |
67 | list_free(crit_tokens); | ||
68 | } | ||
76 | 69 | ||
77 | if (criteria->con_mark) { | 70 | // Extracts criteria string from its brackets. Returns new (duplicate) |
78 | // TODO | 71 | // substring. |
79 | return false; | 72 | static char *criteria_from(const char *arg) { |
73 | char *criteria = NULL; | ||
74 | if (*arg == '[') { | ||
75 | criteria = strdup(arg + 1); | ||
76 | } else { | ||
77 | criteria = strdup(arg); | ||
80 | } | 78 | } |
81 | 79 | ||
82 | if (criteria->con_id) { // Internal ID | 80 | int last = strlen(criteria) - 1; |
83 | if (!view->swayc || view->swayc->id != criteria->con_id) { | 81 | if (criteria[last] == ']') { |
84 | return false; | 82 | criteria[last] = '\0'; |
85 | } | ||
86 | } | 83 | } |
84 | return criteria; | ||
85 | } | ||
87 | 86 | ||
88 | if (criteria->id) { // X11 window ID | 87 | // Return instances of c found in str. |
89 | uint32_t x11_window_id = view_get_x11_window_id(view); | 88 | static int countchr(char *str, char c) { |
90 | if (!x11_window_id || x11_window_id != criteria->id) { | 89 | int found = 0; |
91 | return false; | 90 | for (int i = 0; str[i]; i++) { |
91 | if (str[i] == c) { | ||
92 | ++found; | ||
92 | } | 93 | } |
93 | } | 94 | } |
95 | return found; | ||
96 | } | ||
94 | 97 | ||
95 | if (criteria->window_role) { | 98 | // criteria_str is e.g. '[class="some class regex" instance="instance name"]'. |
96 | // TODO | 99 | // |
97 | } | 100 | // Will create array of pointers in buf, where first is duplicate of given |
98 | 101 | // string (must be freed) and the rest are pointers to names and values in the | |
99 | if (criteria->window_type) { | 102 | // base string (every other, naturally). argc will be populated with the length |
100 | uint32_t type = view_get_window_type(view); | 103 | // of buf. |
101 | if (!type || type != criteria->window_type) { | 104 | // |
102 | return false; | 105 | // Returns error string or NULL if successful. |
106 | static char *crit_tokens(int *argc, char ***buf, | ||
107 | const char * const criteria_str) { | ||
108 | wlr_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str); | ||
109 | char *base = criteria_from(criteria_str); | ||
110 | char *head = base; | ||
111 | char *namep = head; // start of criteria name | ||
112 | char *valp = NULL; // start of value | ||
113 | |||
114 | // We're going to place EOS markers where we need to and fill up an array | ||
115 | // of pointers to the start of each token (either name or value). | ||
116 | int pairs = countchr(base, '='); | ||
117 | int max_tokens = pairs * 2 + 1; // this gives us at least enough slots | ||
118 | |||
119 | char **argv = *buf = calloc(max_tokens, sizeof(char*)); | ||
120 | argv[0] = base; // this needs to be freed by caller | ||
121 | bool quoted = true; | ||
122 | |||
123 | *argc = 1; // uneven = name, even = value | ||
124 | while (*head && *argc < max_tokens) { | ||
125 | if (namep != head && *(head - 1) == '\\') { | ||
126 | // escaped character: don't try to parse this | ||
127 | } else if (*head == '=' && namep != head) { | ||
128 | if (*argc % 2 != 1) { | ||
129 | // we're not expecting a name | ||
130 | return strdup("Unable to parse criteria: " | ||
131 | "Found out of place equal sign"); | ||
132 | } else { | ||
133 | // name ends here | ||
134 | char *end = head; // don't want to rewind the head | ||
135 | while (*(end - 1) == ' ') { | ||
136 | --end; | ||
137 | } | ||
138 | *end = '\0'; | ||
139 | if (*(namep) == ' ') { | ||
140 | namep = strrchr(namep, ' ') + 1; | ||
141 | } | ||
142 | argv[*argc] = namep; | ||
143 | *argc += 1; | ||
144 | } | ||
145 | } else if (*head == '"') { | ||
146 | if (*argc % 2 != 0) { | ||
147 | // we're not expecting a value | ||
148 | return strdup("Unable to parse criteria: " | ||
149 | "Found quoted value where it was not expected"); | ||
150 | } else if (!valp) { // value starts here | ||
151 | valp = head + 1; | ||
152 | quoted = true; | ||
153 | } else { | ||
154 | // value ends here | ||
155 | argv[*argc] = valp; | ||
156 | *argc += 1; | ||
157 | *head = '\0'; | ||
158 | valp = NULL; | ||
159 | namep = head + 1; | ||
160 | } | ||
161 | } else if (*argc % 2 == 0 && *head != ' ') { | ||
162 | // parse unquoted values | ||
163 | if (!valp) { | ||
164 | quoted = false; | ||
165 | valp = head; // value starts here | ||
166 | } | ||
167 | } else if (valp && !quoted && *head == ' ') { | ||
168 | // value ends here | ||
169 | argv[*argc] = valp; | ||
170 | *argc += 1; | ||
171 | *head = '\0'; | ||
172 | valp = NULL; | ||
173 | namep = head + 1; | ||
103 | } | 174 | } |
175 | head++; | ||
104 | } | 176 | } |
105 | 177 | ||
106 | if (criteria->floating) { | 178 | // catch last unquoted value if needed |
107 | // TODO | 179 | if (valp && !quoted && !*head) { |
108 | return false; | 180 | argv[*argc] = valp; |
109 | } | 181 | *argc += 1; |
110 | |||
111 | if (criteria->tiling) { | ||
112 | // TODO | ||
113 | } | ||
114 | |||
115 | if (criteria->urgent) { | ||
116 | // TODO | ||
117 | return false; | ||
118 | } | ||
119 | |||
120 | if (criteria->workspace) { | ||
121 | if (!view->swayc) { | ||
122 | return false; | ||
123 | } | ||
124 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
125 | if (!ws || strcmp(ws->name, criteria->workspace) != 0) { | ||
126 | return false; | ||
127 | } | ||
128 | } | 182 | } |
129 | 183 | ||
130 | return true; | 184 | return NULL; |
131 | } | 185 | } |
132 | 186 | ||
133 | list_t *criteria_for_view(struct sway_view *view, enum criteria_type types) { | 187 | // Returns error string on failure or NULL otherwise. |
134 | list_t *criterias = config->criteria; | 188 | static char *parse_criteria_name(enum criteria_type *type, char *name) { |
135 | list_t *matches = create_list(); | 189 | *type = CRIT_LAST; |
136 | for (int i = 0; i < criterias->length; ++i) { | 190 | for (int i = 0; i < CRIT_LAST; i++) { |
137 | struct criteria *criteria = criterias->items[i]; | 191 | if (strcmp(criteria_strings[i], name) == 0) { |
138 | if ((criteria->type & types) && criteria_matches_view(criteria, view)) { | 192 | *type = (enum criteria_type) i; |
139 | list_add(matches, criteria); | 193 | break; |
140 | } | 194 | } |
141 | } | 195 | } |
142 | return matches; | 196 | if (*type == CRIT_LAST) { |
143 | } | 197 | const char *fmt = "Criteria type '%s' is invalid or unsupported."; |
144 | 198 | int len = strlen(name) + strlen(fmt) - 1; | |
145 | struct match_data { | 199 | char *error = malloc(len); |
146 | struct criteria *criteria; | 200 | snprintf(error, len, fmt, name); |
147 | list_t *matches; | 201 | return error; |
148 | }; | 202 | } else if (*type == CRIT_URGENT || *type == CRIT_WINDOW_ROLE || |
149 | 203 | *type == CRIT_WINDOW_TYPE) { | |
150 | static void criteria_get_views_iterator(struct sway_container *container, | 204 | // (we're just being helpful here) |
151 | void *data) { | 205 | const char *fmt = "\"%s\" criteria currently unsupported, " |
152 | struct match_data *match_data = data; | 206 | "no window will match this"; |
153 | if (container->type == C_VIEW) { | 207 | int len = strlen(fmt) + strlen(name) - 1; |
154 | if (criteria_matches_view(match_data->criteria, container->sway_view)) { | 208 | char *error = malloc(len); |
155 | list_add(match_data->matches, container->sway_view); | 209 | snprintf(error, len, fmt, name); |
156 | } | 210 | return error; |
157 | } | 211 | } |
212 | return NULL; | ||
158 | } | 213 | } |
159 | 214 | ||
160 | list_t *criteria_get_views(struct criteria *criteria) { | ||
161 | list_t *matches = create_list(); | ||
162 | struct match_data data = { | ||
163 | .criteria = criteria, | ||
164 | .matches = matches, | ||
165 | }; | ||
166 | container_for_each_descendant_dfs(&root_container, | ||
167 | criteria_get_views_iterator, &data); | ||
168 | return matches; | ||
169 | } | ||
170 | |||
171 | // The error pointer is used for parsing functions, and saves having to pass it | ||
172 | // as an argument in several places. | ||
173 | char *error = NULL; | ||
174 | |||
175 | // Returns error string on failure or NULL otherwise. | 215 | // Returns error string on failure or NULL otherwise. |
176 | static bool generate_regex(pcre **regex, char *value) { | 216 | static char *generate_regex(pcre **regex, char *value) { |
177 | const char *reg_err; | 217 | const char *reg_err; |
178 | int offset; | 218 | int offset; |
179 | 219 | ||
180 | *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL); | 220 | *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL); |
181 | 221 | ||
182 | if (!*regex) { | 222 | if (!*regex) { |
183 | const char *fmt = "Regex compilation for '%s' failed: %s"; | 223 | const char *fmt = "Regex compilation (for '%s') failed: %s"; |
184 | int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; | 224 | int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; |
185 | error = malloc(len); | 225 | char *error = malloc(len); |
186 | snprintf(error, len, fmt, value, reg_err); | 226 | snprintf(error, len, fmt, value, reg_err); |
187 | return false; | 227 | return error; |
188 | } | 228 | } |
229 | return NULL; | ||
230 | } | ||
189 | 231 | ||
190 | return true; | 232 | // Test whether the criterion corresponds to the currently focused window |
233 | static bool crit_is_focused(const char *value) { | ||
234 | return !strcmp(value, "focused") || !strcmp(value, "__focused__"); | ||
191 | } | 235 | } |
192 | 236 | ||
193 | static bool parse_token(struct criteria *criteria, char *name, char *value) { | 237 | // Populate list with crit_tokens extracted from criteria string, returns error |
194 | // Require value, unless token is floating or tiled | 238 | // string or NULL if successful. |
195 | if (!value && (strcmp(name, "title") == 0 | 239 | char *extract_crit_tokens(list_t *tokens, const char * const criteria) { |
196 | || strcmp(name, "app_id") == 0 | 240 | int argc; |
197 | || strcmp(name, "class") == 0 | 241 | char **argv = NULL, *error = NULL; |
198 | || strcmp(name, "instance") == 0 | 242 | if ((error = crit_tokens(&argc, &argv, criteria))) { |
199 | || strcmp(name, "con_id") == 0 | 243 | goto ect_cleanup; |
200 | || strcmp(name, "con_mark") == 0 | ||
201 | || strcmp(name, "window_role") == 0 | ||
202 | || strcmp(name, "window_type") == 0 | ||
203 | || strcmp(name, "id") == 0 | ||
204 | || strcmp(name, "urgent") == 0 | ||
205 | || strcmp(name, "workspace") == 0)) { | ||
206 | const char *fmt = "Token '%s' requires a value"; | ||
207 | int len = strlen(fmt) + strlen(name) - 1; | ||
208 | error = malloc(len); | ||
209 | snprintf(error, len, fmt, name); | ||
210 | return false; | ||
211 | } | 244 | } |
212 | 245 | for (int i = 1; i + 1 < argc; i += 2) { | |
213 | if (strcmp(name, "title") == 0) { | 246 | char* name = argv[i], *value = argv[i + 1]; |
214 | generate_regex(&criteria->title, value); | 247 | struct crit_token *token = calloc(1, sizeof(struct crit_token)); |
215 | } else if (strcmp(name, "app_id") == 0) { | 248 | token->raw = strdup(value); |
216 | generate_regex(&criteria->app_id, value); | 249 | |
217 | } else if (strcmp(name, "class") == 0) { | 250 | if ((error = parse_criteria_name(&token->type, name))) { |
218 | generate_regex(&criteria->class, value); | 251 | free_crit_token(token); |
219 | } else if (strcmp(name, "instance") == 0) { | 252 | goto ect_cleanup; |
220 | generate_regex(&criteria->instance, value); | 253 | } else if (token->type == CRIT_URGENT || crit_is_focused(value)) { |
221 | } else if (strcmp(name, "con_id") == 0) { | 254 | wlr_log(L_DEBUG, "%s -> \"%s\"", name, value); |
222 | char *endptr; | 255 | list_add(tokens, token); |
223 | criteria->con_id = strtoul(value, &endptr, 10); | 256 | } else if((error = generate_regex(&token->regex, value))) { |
224 | if (*endptr != 0) { | 257 | free_crit_token(token); |
225 | error = strdup("The value for 'con_id' should be numeric"); | 258 | goto ect_cleanup; |
226 | } | ||
227 | } else if (strcmp(name, "con_mark") == 0) { | ||
228 | generate_regex(&criteria->con_mark, value); | ||
229 | } else if (strcmp(name, "window_role") == 0) { | ||
230 | generate_regex(&criteria->window_role, value); | ||
231 | } else if (strcmp(name, "window_type") == 0) { | ||
232 | // TODO: This is a string but will be stored as an enum or integer | ||
233 | } else if (strcmp(name, "id") == 0) { | ||
234 | char *endptr; | ||
235 | criteria->id = strtoul(value, &endptr, 10); | ||
236 | if (*endptr != 0) { | ||
237 | error = strdup("The value for 'id' should be numeric"); | ||
238 | } | ||
239 | } else if (strcmp(name, "floating") == 0) { | ||
240 | criteria->floating = true; | ||
241 | } else if (strcmp(name, "tiling") == 0) { | ||
242 | criteria->tiling = true; | ||
243 | } else if (strcmp(name, "urgent") == 0) { | ||
244 | if (strcmp(value, "latest") == 0) { | ||
245 | criteria->urgent = 'l'; | ||
246 | } else if (strcmp(value, "oldest") == 0) { | ||
247 | criteria->urgent = 'o'; | ||
248 | } else { | 259 | } else { |
249 | error = | 260 | wlr_log(L_DEBUG, "%s -> /%s/", name, value); |
250 | strdup("The value for 'urgent' must be 'latest' or 'oldest'"); | 261 | list_add(tokens, token); |
251 | } | 262 | } |
252 | } else if (strcmp(name, "workspace") == 0) { | ||
253 | criteria->workspace = strdup(value); | ||
254 | } else { | ||
255 | const char *fmt = "Token '%s' is not recognized"; | ||
256 | int len = strlen(fmt) + strlen(name) - 1; | ||
257 | error = malloc(len); | ||
258 | snprintf(error, len, fmt, name); | ||
259 | } | 263 | } |
260 | 264 | ect_cleanup: | |
261 | if (error) { | 265 | free(argv[0]); // base string |
262 | return false; | 266 | free(argv); |
263 | } | 267 | return error; |
264 | |||
265 | return true; | ||
266 | } | 268 | } |
267 | 269 | ||
268 | static void skip_spaces(char **head) { | 270 | static int regex_cmp(const char *item, const pcre *regex) { |
269 | while (**head == ' ') { | 271 | return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); |
270 | ++*head; | ||
271 | } | ||
272 | } | 272 | } |
273 | 273 | ||
274 | // Remove escaping slashes from value | 274 | // test a single view if it matches list of criteria tokens (all of them). |
275 | static void unescape(char *value) { | 275 | static bool criteria_test(struct sway_container *cont, list_t *tokens) { |
276 | if (!strchr(value, '\\')) { | 276 | if (cont->type != C_CONTAINER && cont->type != C_VIEW) { |
277 | return; | 277 | return false; |
278 | } | ||
279 | char *copy = calloc(strlen(value) + 1, 1); | ||
280 | char *readhead = value; | ||
281 | char *writehead = copy; | ||
282 | while (*readhead) { | ||
283 | if (*readhead == '\\' && *(readhead + 1) == '"') { | ||
284 | // skip the slash | ||
285 | ++readhead; | ||
286 | } | ||
287 | *writehead = *readhead; | ||
288 | ++writehead; | ||
289 | ++readhead; | ||
290 | } | 278 | } |
291 | strcpy(value, copy); | 279 | int matches = 0; |
292 | free(copy); | 280 | for (int i = 0; i < tokens->length; i++) { |
293 | } | 281 | struct crit_token *crit = tokens->items[i]; |
282 | switch (crit->type) { | ||
283 | case CRIT_CLASS: | ||
284 | { | ||
285 | const char *class = view_get_class(cont->sway_view); | ||
286 | if (!class) { | ||
287 | break; | ||
288 | } | ||
289 | if (crit->regex && regex_cmp(class, crit->regex) == 0) { | ||
290 | matches++; | ||
291 | } | ||
292 | break; | ||
293 | } | ||
294 | case CRIT_CON_ID: | ||
295 | { | ||
296 | char *endptr; | ||
297 | size_t crit_id = strtoul(crit->raw, &endptr, 10); | ||
294 | 298 | ||
295 | /** | 299 | if (*endptr == 0 && cont->id == crit_id) { |
296 | * Parse a raw criteria string such as [class="foo" instance="bar"] into a | 300 | ++matches; |
297 | * criteria struct. | 301 | } |
298 | * | 302 | break; |
299 | * If errors are found, NULL will be returned and the error argument will be | ||
300 | * populated with an error string. | ||
301 | */ | ||
302 | struct criteria *criteria_parse(char *raw, char **error_arg) { | ||
303 | free(error); | ||
304 | error = NULL; | ||
305 | |||
306 | char *head = raw; | ||
307 | skip_spaces(&head); | ||
308 | if (*head != '[') { | ||
309 | *error_arg = strdup("No criteria"); | ||
310 | return NULL; | ||
311 | } | ||
312 | ++head; | ||
313 | |||
314 | struct criteria *criteria = calloc(sizeof(struct criteria), 1); | ||
315 | char *name = NULL, *value = NULL; | ||
316 | bool in_quotes = false; | ||
317 | |||
318 | while (*head && *head != ']') { | ||
319 | skip_spaces(&head); | ||
320 | // Parse token name | ||
321 | char *namestart = head; | ||
322 | while ((*head >= 'a' && *head <= 'z') || *head == '_') { | ||
323 | ++head; | ||
324 | } | ||
325 | name = calloc(head - namestart + 1, 1); | ||
326 | strncpy(name, namestart, head - namestart); | ||
327 | // Parse token value | ||
328 | skip_spaces(&head); | ||
329 | value = NULL; | ||
330 | if (*head == '=') { | ||
331 | ++head; | ||
332 | skip_spaces(&head); | ||
333 | if (*head == '"') { | ||
334 | in_quotes = true; | ||
335 | ++head; | ||
336 | } | 303 | } |
337 | char *valuestart = head; | 304 | case CRIT_CON_MARK: |
338 | if (in_quotes) { | 305 | // TODO |
339 | while (*head && (*head != '"' || *(head - 1) == '\\')) { | 306 | break; |
340 | ++head; | 307 | case CRIT_FLOATING: |
308 | // TODO | ||
309 | break; | ||
310 | case CRIT_ID: | ||
311 | // TODO | ||
312 | break; | ||
313 | case CRIT_APP_ID: | ||
314 | { | ||
315 | const char *app_id = view_get_app_id(cont->sway_view); | ||
316 | if (!app_id) { | ||
317 | break; | ||
341 | } | 318 | } |
342 | if (!*head) { | 319 | |
343 | *error_arg = strdup("Quote mismatch in criteria"); | 320 | if (crit->regex && regex_cmp(app_id, crit->regex) == 0) { |
344 | goto cleanup; | 321 | matches++; |
345 | } | 322 | } |
346 | } else { | 323 | break; |
347 | while (*head && *head != ' ' && *head != ']') { | 324 | } |
348 | ++head; | 325 | case CRIT_INSTANCE: |
326 | { | ||
327 | const char *instance = view_get_instance(cont->sway_view); | ||
328 | if (!instance) { | ||
329 | break; | ||
330 | } | ||
331 | |||
332 | if (crit->regex && regex_cmp(instance, crit->regex) == 0) { | ||
333 | matches++; | ||
349 | } | 334 | } |
335 | break; | ||
350 | } | 336 | } |
351 | value = calloc(head - valuestart + 1, 1); | 337 | case CRIT_TILING: |
352 | strncpy(value, valuestart, head - valuestart); | 338 | // TODO |
353 | if (in_quotes) { | 339 | break; |
354 | ++head; | 340 | case CRIT_TITLE: |
355 | in_quotes = false; | 341 | { |
342 | const char *title = view_get_title(cont->sway_view); | ||
343 | if (!title) { | ||
344 | break; | ||
345 | } | ||
346 | |||
347 | if (crit->regex && regex_cmp(title, crit->regex) == 0) { | ||
348 | matches++; | ||
349 | } | ||
350 | break; | ||
356 | } | 351 | } |
357 | unescape(value); | 352 | case CRIT_URGENT: |
353 | // TODO "latest" or "oldest" | ||
354 | break; | ||
355 | case CRIT_WINDOW_ROLE: | ||
356 | // TODO | ||
357 | break; | ||
358 | case CRIT_WINDOW_TYPE: | ||
359 | // TODO | ||
360 | break; | ||
361 | case CRIT_WORKSPACE: | ||
362 | // TODO | ||
363 | break; | ||
364 | default: | ||
365 | sway_abort("Invalid criteria type (%i)", crit->type); | ||
366 | break; | ||
358 | } | 367 | } |
359 | wlr_log(L_DEBUG, "Found pair: %s=%s", name, value); | 368 | } |
360 | if (!parse_token(criteria, name, value)) { | 369 | return matches == tokens->length; |
361 | *error_arg = error; | 370 | } |
362 | goto cleanup; | 371 | |
372 | int criteria_cmp(const void *a, const void *b) { | ||
373 | if (a == b) { | ||
374 | return 0; | ||
375 | } else if (!a) { | ||
376 | return -1; | ||
377 | } else if (!b) { | ||
378 | return 1; | ||
379 | } | ||
380 | const struct criteria *crit_a = a, *crit_b = b; | ||
381 | int cmp = lenient_strcmp(crit_a->cmdlist, crit_b->cmdlist); | ||
382 | if (cmp != 0) { | ||
383 | return cmp; | ||
384 | } | ||
385 | return lenient_strcmp(crit_a->crit_raw, crit_b->crit_raw); | ||
386 | } | ||
387 | |||
388 | void free_criteria(struct criteria *crit) { | ||
389 | if (crit->tokens) { | ||
390 | free_crit_tokens(crit->tokens); | ||
391 | } | ||
392 | if (crit->cmdlist) { | ||
393 | free(crit->cmdlist); | ||
394 | } | ||
395 | if (crit->crit_raw) { | ||
396 | free(crit->crit_raw); | ||
397 | } | ||
398 | free(crit); | ||
399 | } | ||
400 | |||
401 | bool criteria_any(struct sway_container *cont, list_t *criteria) { | ||
402 | for (int i = 0; i < criteria->length; i++) { | ||
403 | struct criteria *bc = criteria->items[i]; | ||
404 | if (criteria_test(cont, bc->tokens)) { | ||
405 | return true; | ||
363 | } | 406 | } |
364 | skip_spaces(&head); | ||
365 | free(name); | ||
366 | free(value); | ||
367 | name = NULL; | ||
368 | value = NULL; | ||
369 | } | 407 | } |
370 | if (*head != ']') { | 408 | return false; |
371 | *error_arg = strdup("No closing brace found in criteria"); | 409 | } |
372 | goto cleanup; | 410 | |
411 | list_t *criteria_for(struct sway_container *cont) { | ||
412 | list_t *criteria = config->criteria, *matches = create_list(); | ||
413 | for (int i = 0; i < criteria->length; i++) { | ||
414 | struct criteria *bc = criteria->items[i]; | ||
415 | if (criteria_test(cont, bc->tokens)) { | ||
416 | list_add(matches, bc); | ||
417 | } | ||
373 | } | 418 | } |
419 | return matches; | ||
420 | } | ||
374 | 421 | ||
375 | if (criteria_is_empty(criteria)) { | 422 | struct list_tokens { |
376 | *error_arg = strdup("Criteria is empty"); | 423 | list_t *list; |
377 | goto cleanup; | 424 | list_t *tokens; |
425 | }; | ||
426 | |||
427 | static void container_match_add(struct sway_container *container, | ||
428 | struct list_tokens *list_tokens) { | ||
429 | if (criteria_test(container, list_tokens->tokens)) { | ||
430 | list_add(list_tokens->list, container); | ||
378 | } | 431 | } |
432 | } | ||
379 | 433 | ||
380 | ++head; | 434 | list_t *container_for_crit_tokens(list_t *tokens) { |
381 | int len = head - raw; | 435 | struct list_tokens list_tokens = |
382 | criteria->raw = calloc(len + 1, 1); | 436 | (struct list_tokens){create_list(), tokens}; |
383 | strncpy(criteria->raw, raw, len); | ||
384 | return criteria; | ||
385 | 437 | ||
386 | cleanup: | 438 | container_for_each_descendant_dfs(&root_container, |
387 | free(name); | 439 | (void (*)(struct sway_container *, void *))container_match_add, |
388 | free(value); | 440 | &list_tokens); |
389 | criteria_destroy(criteria); | 441 | |
390 | return NULL; | 442 | // TODO look in the scratchpad |
443 | |||
444 | return list_tokens.list; | ||
391 | } | 445 | } |
diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c index cb3774f7..99e8947b 100644 --- a/sway/desktop/wl_shell.c +++ b/sway/desktop/wl_shell.c | |||
@@ -20,7 +20,7 @@ static struct sway_wl_shell_view *wl_shell_view_from_view( | |||
20 | return (struct sway_wl_shell_view *)view; | 20 | return (struct sway_wl_shell_view *)view; |
21 | } | 21 | } |
22 | 22 | ||
23 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { | 23 | static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { |
24 | if (wl_shell_view_from_view(view) == NULL) { | 24 | if (wl_shell_view_from_view(view) == NULL) { |
25 | return NULL; | 25 | return NULL; |
26 | } | 26 | } |
@@ -70,7 +70,7 @@ static void set_fullscreen(struct sway_view *view, bool fullscreen) { | |||
70 | } | 70 | } |
71 | 71 | ||
72 | static const struct sway_view_impl view_impl = { | 72 | static const struct sway_view_impl view_impl = { |
73 | .get_string_prop = get_string_prop, | 73 | .get_prop = get_prop, |
74 | .configure = configure, | 74 | .configure = configure, |
75 | .close = _close, | 75 | .close = _close, |
76 | .destroy = destroy, | 76 | .destroy = destroy, |
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index f685ef71..8ecb330d 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c | |||
@@ -80,7 +80,7 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( | |||
80 | return (struct sway_xdg_shell_v6_view *)view; | 80 | return (struct sway_xdg_shell_v6_view *)view; |
81 | } | 81 | } |
82 | 82 | ||
83 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { | 83 | static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { |
84 | if (xdg_shell_v6_view_from_view(view) == NULL) { | 84 | if (xdg_shell_v6_view_from_view(view) == NULL) { |
85 | return NULL; | 85 | return NULL; |
86 | } | 86 | } |
@@ -158,7 +158,7 @@ static void destroy(struct sway_view *view) { | |||
158 | } | 158 | } |
159 | 159 | ||
160 | static const struct sway_view_impl view_impl = { | 160 | static const struct sway_view_impl view_impl = { |
161 | .get_string_prop = get_string_prop, | 161 | .get_prop = get_prop, |
162 | .configure = configure, | 162 | .configure = configure, |
163 | .set_activated = set_activated, | 163 | .set_activated = set_activated, |
164 | .set_fullscreen = set_fullscreen, | 164 | .set_fullscreen = set_fullscreen, |
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 554c070e..8f935760 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -126,7 +126,7 @@ static struct sway_xwayland_view *xwayland_view_from_view( | |||
126 | return (struct sway_xwayland_view *)view; | 126 | return (struct sway_xwayland_view *)view; |
127 | } | 127 | } |
128 | 128 | ||
129 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { | 129 | static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { |
130 | if (xwayland_view_from_view(view) == NULL) { | 130 | if (xwayland_view_from_view(view) == NULL) { |
131 | return NULL; | 131 | return NULL; |
132 | } | 132 | } |
@@ -135,27 +135,11 @@ static const char *get_string_prop(struct sway_view *view, enum sway_view_prop p | |||
135 | return view->wlr_xwayland_surface->title; | 135 | return view->wlr_xwayland_surface->title; |
136 | case VIEW_PROP_CLASS: | 136 | case VIEW_PROP_CLASS: |
137 | return view->wlr_xwayland_surface->class; | 137 | return view->wlr_xwayland_surface->class; |
138 | case VIEW_PROP_INSTANCE: | ||
139 | return view->wlr_xwayland_surface->instance; | ||
140 | default: | 138 | default: |
141 | return NULL; | 139 | return NULL; |
142 | } | 140 | } |
143 | } | 141 | } |
144 | 142 | ||
145 | static uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) { | ||
146 | if (xwayland_view_from_view(view) == NULL) { | ||
147 | return 0; | ||
148 | } | ||
149 | switch (prop) { | ||
150 | case VIEW_PROP_X11_WINDOW_ID: | ||
151 | return view->wlr_xwayland_surface->window_id; | ||
152 | case VIEW_PROP_WINDOW_TYPE: | ||
153 | return *view->wlr_xwayland_surface->window_type; | ||
154 | default: | ||
155 | return 0; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | static void configure(struct sway_view *view, double ox, double oy, int width, | 143 | static void configure(struct sway_view *view, double ox, double oy, int width, |
160 | int height) { | 144 | int height) { |
161 | struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); | 145 | struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); |
@@ -216,17 +200,13 @@ static void destroy(struct sway_view *view) { | |||
216 | wl_list_remove(&xwayland_view->destroy.link); | 200 | wl_list_remove(&xwayland_view->destroy.link); |
217 | wl_list_remove(&xwayland_view->request_configure.link); | 201 | wl_list_remove(&xwayland_view->request_configure.link); |
218 | wl_list_remove(&xwayland_view->request_fullscreen.link); | 202 | wl_list_remove(&xwayland_view->request_fullscreen.link); |
219 | wl_list_remove(&xwayland_view->set_title.link); | ||
220 | wl_list_remove(&xwayland_view->set_class.link); | ||
221 | wl_list_remove(&xwayland_view->set_window_type.link); | ||
222 | wl_list_remove(&xwayland_view->map.link); | 203 | wl_list_remove(&xwayland_view->map.link); |
223 | wl_list_remove(&xwayland_view->unmap.link); | 204 | wl_list_remove(&xwayland_view->unmap.link); |
224 | free(xwayland_view); | 205 | free(xwayland_view); |
225 | } | 206 | } |
226 | 207 | ||
227 | static const struct sway_view_impl view_impl = { | 208 | static const struct sway_view_impl view_impl = { |
228 | .get_string_prop = get_string_prop, | 209 | .get_prop = get_prop, |
229 | .get_int_prop = get_int_prop, | ||
230 | .configure = configure, | 210 | .configure = configure, |
231 | .set_activated = set_activated, | 211 | .set_activated = set_activated, |
232 | .set_fullscreen = set_fullscreen, | 212 | .set_fullscreen = set_fullscreen, |
@@ -243,6 +223,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
243 | view_update_size(view, xwayland_view->pending_width, | 223 | view_update_size(view, xwayland_view->pending_width, |
244 | xwayland_view->pending_height); | 224 | xwayland_view->pending_height); |
245 | view_damage_from(view); | 225 | view_damage_from(view); |
226 | view_update_title(view, false); | ||
246 | } | 227 | } |
247 | 228 | ||
248 | static void handle_unmap(struct wl_listener *listener, void *data) { | 229 | static void handle_unmap(struct wl_listener *listener, void *data) { |
@@ -304,40 +285,6 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
304 | view_set_fullscreen(view, xsurface->fullscreen); | 285 | view_set_fullscreen(view, xsurface->fullscreen); |
305 | } | 286 | } |
306 | 287 | ||
307 | static void handle_set_title(struct wl_listener *listener, void *data) { | ||
308 | struct sway_xwayland_view *xwayland_view = | ||
309 | wl_container_of(listener, xwayland_view, set_title); | ||
310 | struct sway_view *view = &xwayland_view->view; | ||
311 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
312 | if (!xsurface->mapped) { | ||
313 | return; | ||
314 | } | ||
315 | view_update_title(view, false); | ||
316 | view_execute_criteria(view); | ||
317 | } | ||
318 | |||
319 | static void handle_set_class(struct wl_listener *listener, void *data) { | ||
320 | struct sway_xwayland_view *xwayland_view = | ||
321 | wl_container_of(listener, xwayland_view, set_class); | ||
322 | struct sway_view *view = &xwayland_view->view; | ||
323 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
324 | if (!xsurface->mapped) { | ||
325 | return; | ||
326 | } | ||
327 | view_execute_criteria(view); | ||
328 | } | ||
329 | |||
330 | static void handle_set_window_type(struct wl_listener *listener, void *data) { | ||
331 | struct sway_xwayland_view *xwayland_view = | ||
332 | wl_container_of(listener, xwayland_view, set_window_type); | ||
333 | struct sway_view *view = &xwayland_view->view; | ||
334 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
335 | if (!xsurface->mapped) { | ||
336 | return; | ||
337 | } | ||
338 | view_execute_criteria(view); | ||
339 | } | ||
340 | |||
341 | void handle_xwayland_surface(struct wl_listener *listener, void *data) { | 288 | void handle_xwayland_surface(struct wl_listener *listener, void *data) { |
342 | struct sway_server *server = wl_container_of(listener, server, | 289 | struct sway_server *server = wl_container_of(listener, server, |
343 | xwayland_surface); | 290 | xwayland_surface); |
@@ -376,16 +323,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { | |||
376 | &xwayland_view->request_fullscreen); | 323 | &xwayland_view->request_fullscreen); |
377 | xwayland_view->request_fullscreen.notify = handle_request_fullscreen; | 324 | xwayland_view->request_fullscreen.notify = handle_request_fullscreen; |
378 | 325 | ||
379 | wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); | ||
380 | xwayland_view->set_title.notify = handle_set_title; | ||
381 | |||
382 | wl_signal_add(&xsurface->events.set_class, &xwayland_view->set_class); | ||
383 | xwayland_view->set_class.notify = handle_set_class; | ||
384 | |||
385 | wl_signal_add(&xsurface->events.set_window_type, | ||
386 | &xwayland_view->set_window_type); | ||
387 | xwayland_view->set_window_type.notify = handle_set_window_type; | ||
388 | |||
389 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); | 326 | wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); |
390 | xwayland_view->unmap.notify = handle_unmap; | 327 | xwayland_view->unmap.notify = handle_unmap; |
391 | 328 | ||
diff --git a/sway/tree/view.c b/sway/tree/view.c index 7d921e0e..424c1084 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -3,7 +3,6 @@ | |||
3 | #include <wayland-server.h> | 3 | #include <wayland-server.h> |
4 | #include <wlr/render/wlr_renderer.h> | 4 | #include <wlr/render/wlr_renderer.h> |
5 | #include <wlr/types/wlr_output_layout.h> | 5 | #include <wlr/types/wlr_output_layout.h> |
6 | #include "list.h" | ||
7 | #include "log.h" | 6 | #include "log.h" |
8 | #include "sway/criteria.h" | 7 | #include "sway/criteria.h" |
9 | #include "sway/commands.h" | 8 | #include "sway/commands.h" |
@@ -20,7 +19,6 @@ void view_init(struct sway_view *view, enum sway_view_type type, | |||
20 | const struct sway_view_impl *impl) { | 19 | const struct sway_view_impl *impl) { |
21 | view->type = type; | 20 | view->type = type; |
22 | view->impl = impl; | 21 | view->impl = impl; |
23 | view->executed_criteria = create_list(); | ||
24 | wl_signal_init(&view->events.unmap); | 22 | wl_signal_init(&view->events.unmap); |
25 | } | 23 | } |
26 | 24 | ||
@@ -33,8 +31,6 @@ void view_destroy(struct sway_view *view) { | |||
33 | view_unmap(view); | 31 | view_unmap(view); |
34 | } | 32 | } |
35 | 33 | ||
36 | list_free(view->executed_criteria); | ||
37 | |||
38 | container_destroy(view->swayc); | 34 | container_destroy(view->swayc); |
39 | 35 | ||
40 | if (view->impl->destroy) { | 36 | if (view->impl->destroy) { |
@@ -45,47 +41,33 @@ void view_destroy(struct sway_view *view) { | |||
45 | } | 41 | } |
46 | 42 | ||
47 | const char *view_get_title(struct sway_view *view) { | 43 | const char *view_get_title(struct sway_view *view) { |
48 | if (view->impl->get_string_prop) { | 44 | if (view->impl->get_prop) { |
49 | return view->impl->get_string_prop(view, VIEW_PROP_TITLE); | 45 | return view->impl->get_prop(view, VIEW_PROP_TITLE); |
50 | } | 46 | } |
51 | return NULL; | 47 | return NULL; |
52 | } | 48 | } |
53 | 49 | ||
54 | const char *view_get_app_id(struct sway_view *view) { | 50 | const char *view_get_app_id(struct sway_view *view) { |
55 | if (view->impl->get_string_prop) { | 51 | if (view->impl->get_prop) { |
56 | return view->impl->get_string_prop(view, VIEW_PROP_APP_ID); | 52 | return view->impl->get_prop(view, VIEW_PROP_APP_ID); |
57 | } | 53 | } |
58 | return NULL; | 54 | return NULL; |
59 | } | 55 | } |
60 | 56 | ||
61 | const char *view_get_class(struct sway_view *view) { | 57 | const char *view_get_class(struct sway_view *view) { |
62 | if (view->impl->get_string_prop) { | 58 | if (view->impl->get_prop) { |
63 | return view->impl->get_string_prop(view, VIEW_PROP_CLASS); | 59 | return view->impl->get_prop(view, VIEW_PROP_CLASS); |
64 | } | 60 | } |
65 | return NULL; | 61 | return NULL; |
66 | } | 62 | } |
67 | 63 | ||
68 | const char *view_get_instance(struct sway_view *view) { | 64 | const char *view_get_instance(struct sway_view *view) { |
69 | if (view->impl->get_string_prop) { | 65 | if (view->impl->get_prop) { |
70 | return view->impl->get_string_prop(view, VIEW_PROP_INSTANCE); | 66 | return view->impl->get_prop(view, VIEW_PROP_INSTANCE); |
71 | } | 67 | } |
72 | return NULL; | 68 | return NULL; |
73 | } | 69 | } |
74 | 70 | ||
75 | uint32_t view_get_x11_window_id(struct sway_view *view) { | ||
76 | if (view->impl->get_int_prop) { | ||
77 | return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); | ||
78 | } | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | uint32_t view_get_window_type(struct sway_view *view) { | ||
83 | if (view->impl->get_int_prop) { | ||
84 | return view->impl->get_int_prop(view, VIEW_PROP_WINDOW_TYPE); | ||
85 | } | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | const char *view_get_type(struct sway_view *view) { | 71 | const char *view_get_type(struct sway_view *view) { |
90 | switch(view->type) { | 72 | switch(view->type) { |
91 | case SWAY_VIEW_WL_SHELL: | 73 | case SWAY_VIEW_WL_SHELL: |
@@ -181,13 +163,15 @@ void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) { | |||
181 | struct sway_container *focus, *focus_ws; | 163 | struct sway_container *focus, *focus_ws; |
182 | wl_list_for_each(seat, &input_manager->seats, link) { | 164 | wl_list_for_each(seat, &input_manager->seats, link) { |
183 | focus = seat_get_focus(seat); | 165 | focus = seat_get_focus(seat); |
184 | focus_ws = focus; | 166 | if (focus) { |
185 | if (focus_ws->type != C_WORKSPACE) { | 167 | focus_ws = focus; |
186 | focus_ws = container_parent(focus_ws, C_WORKSPACE); | 168 | if (focus && focus_ws->type != C_WORKSPACE) { |
187 | } | 169 | focus_ws = container_parent(focus_ws, C_WORKSPACE); |
188 | seat_set_focus(seat, view->swayc); | 170 | } |
189 | if (focus_ws != workspace) { | 171 | seat_set_focus(seat, view->swayc); |
190 | seat_set_focus(seat, focus); | 172 | if (focus_ws != workspace) { |
173 | seat_set_focus(seat, focus); | ||
174 | } | ||
191 | } | 175 | } |
192 | } | 176 | } |
193 | } else { | 177 | } else { |
@@ -300,36 +284,19 @@ static void view_handle_container_reparent(struct wl_listener *listener, | |||
300 | } | 284 | } |
301 | } | 285 | } |
302 | 286 | ||
303 | static bool view_has_executed_criteria(struct sway_view *view, | 287 | static void view_execute_criteria(struct sway_view *view) { |
304 | struct criteria *criteria) { | 288 | if (!sway_assert(view->swayc, "cannot run criteria for unmapped view")) { |
305 | for (int i = 0; i < view->executed_criteria->length; ++i) { | ||
306 | struct criteria *item = view->executed_criteria->items[i]; | ||
307 | if (item == criteria) { | ||
308 | return true; | ||
309 | } | ||
310 | } | ||
311 | return false; | ||
312 | } | ||
313 | |||
314 | void view_execute_criteria(struct sway_view *view) { | ||
315 | if (!view->swayc) { | ||
316 | return; | 289 | return; |
317 | } | 290 | } |
318 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 291 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
319 | struct sway_container *prior_workspace = | 292 | struct sway_container *prior_workspace = |
320 | container_parent(view->swayc, C_WORKSPACE); | 293 | container_parent(view->swayc, C_WORKSPACE); |
321 | list_t *criterias = criteria_for_view(view, CT_COMMAND); | 294 | list_t *criteria = criteria_for(view->swayc); |
322 | for (int i = 0; i < criterias->length; i++) { | 295 | for (int i = 0; i < criteria->length; i++) { |
323 | struct criteria *criteria = criterias->items[i]; | 296 | struct criteria *crit = criteria->items[i]; |
324 | wlr_log(L_DEBUG, "Checking criteria %s", criteria->raw); | 297 | wlr_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'", |
325 | if (view_has_executed_criteria(view, criteria)) { | 298 | crit->crit_raw, view, crit->cmdlist); |
326 | wlr_log(L_DEBUG, "Criteria already executed"); | 299 | struct cmd_results *res = execute_command(crit->cmdlist, NULL); |
327 | continue; | ||
328 | } | ||
329 | wlr_log(L_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", | ||
330 | criteria->raw, view, criteria->cmdlist); | ||
331 | list_add(view->executed_criteria, criteria); | ||
332 | struct cmd_results *res = execute_command(criteria->cmdlist, NULL); | ||
333 | if (res->status != CMD_SUCCESS) { | 300 | if (res->status != CMD_SUCCESS) { |
334 | wlr_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); | 301 | wlr_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); |
335 | } | 302 | } |
@@ -338,7 +305,7 @@ void view_execute_criteria(struct sway_view *view) { | |||
338 | // so always refocus in-between command lists | 305 | // so always refocus in-between command lists |
339 | seat_set_focus(seat, view->swayc); | 306 | seat_set_focus(seat, view->swayc); |
340 | } | 307 | } |
341 | list_free(criterias); | 308 | list_free(criteria); |
342 | seat_set_focus(seat, seat_get_focus_inactive(seat, prior_workspace)); | 309 | seat_set_focus(seat, seat_get_focus_inactive(seat, prior_workspace)); |
343 | } | 310 | } |
344 | 311 | ||
@@ -348,26 +315,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
348 | } | 315 | } |
349 | 316 | ||
350 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 317 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
351 | struct sway_container *focus = seat_get_focus(seat); | 318 | struct sway_container *focus = seat_get_focus_inactive(seat, |
352 | struct sway_container *cont = NULL; | 319 | &root_container); |
353 | 320 | struct sway_container *cont = container_view_create(focus, view); | |
354 | // Check if there's any `assign` criteria for the view | ||
355 | list_t *criterias = criteria_for_view(view, | ||
356 | CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); | ||
357 | if (criterias->length) { | ||
358 | struct criteria *criteria = criterias->items[0]; | ||
359 | if (criteria->type == CT_ASSIGN_WORKSPACE) { | ||
360 | struct sway_container *workspace = workspace_by_name(criteria->target); | ||
361 | if (!workspace) { | ||
362 | workspace = workspace_create(NULL, criteria->target); | ||
363 | } | ||
364 | focus = seat_get_focus_inactive(seat, workspace); | ||
365 | } else { | ||
366 | // TODO: CT_ASSIGN_OUTPUT | ||
367 | } | ||
368 | } | ||
369 | free(criterias); | ||
370 | cont = container_view_create(focus, view); | ||
371 | 321 | ||
372 | view->surface = wlr_surface; | 322 | view->surface = wlr_surface; |
373 | view->swayc = cont; | 323 | view->swayc = cont; |
@@ -385,11 +335,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
385 | arrange_children_of(cont->parent); | 335 | arrange_children_of(cont->parent); |
386 | input_manager_set_focus(input_manager, cont); | 336 | input_manager_set_focus(input_manager, cont); |
387 | 337 | ||
388 | view_update_title(view, false); | ||
389 | view_execute_criteria(view); | ||
390 | |||
391 | container_damage_whole(cont); | 338 | container_damage_whole(cont); |
392 | view_handle_container_reparent(&view->container_reparent, NULL); | 339 | view_handle_container_reparent(&view->container_reparent, NULL); |
340 | |||
341 | view_execute_criteria(view); | ||
393 | } | 342 | } |
394 | 343 | ||
395 | void view_unmap(struct sway_view *view) { | 344 | void view_unmap(struct sway_view *view) { |