diff options
-rw-r--r-- | include/sway/tree/view.h | 2 | ||||
-rw-r--r-- | sway/criteria.c | 204 | ||||
-rw-r--r-- | sway/tree/view.c | 7 |
3 files changed, 171 insertions, 42 deletions
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index bf86ca8c..7c07842b 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -202,6 +202,8 @@ const char *view_get_instance(struct sway_view *view); | |||
202 | 202 | ||
203 | uint32_t view_get_x11_window_id(struct sway_view *view); | 203 | uint32_t view_get_x11_window_id(struct sway_view *view); |
204 | 204 | ||
205 | const char *view_get_window_role(struct sway_view *view); | ||
206 | |||
205 | uint32_t view_get_window_type(struct sway_view *view); | 207 | uint32_t view_get_window_type(struct sway_view *view); |
206 | 208 | ||
207 | const char *view_get_type(struct sway_view *view); | 209 | const char *view_get_type(struct sway_view *view); |
diff --git a/sway/criteria.c b/sway/criteria.c index 7da790e6..294b2922 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -190,19 +190,128 @@ static bool generate_regex(pcre **regex, char *value) { | |||
190 | return true; | 190 | return true; |
191 | } | 191 | } |
192 | 192 | ||
193 | enum criteria_token { | ||
194 | T_APP_ID, | ||
195 | T_CLASS, | ||
196 | T_CON_ID, | ||
197 | T_CON_MARK, | ||
198 | T_FLOATING, | ||
199 | T_ID, | ||
200 | T_INSTANCE, | ||
201 | T_TILING, | ||
202 | T_TITLE, | ||
203 | T_URGENT, | ||
204 | T_WINDOW_ROLE, | ||
205 | T_WINDOW_TYPE, | ||
206 | T_WORKSPACE, | ||
207 | |||
208 | T_INVALID, | ||
209 | }; | ||
210 | |||
211 | static enum criteria_token token_from_name(char *name) { | ||
212 | if (strcmp(name, "app_id") == 0) { | ||
213 | return T_APP_ID; | ||
214 | } else if (strcmp(name, "class") == 0) { | ||
215 | return T_CLASS; | ||
216 | } else if (strcmp(name, "con_id") == 0) { | ||
217 | return T_CON_ID; | ||
218 | } else if (strcmp(name, "con_mark") == 0) { | ||
219 | return T_CON_MARK; | ||
220 | } else if (strcmp(name, "id") == 0) { | ||
221 | return T_ID; | ||
222 | } else if (strcmp(name, "instance") == 0) { | ||
223 | return T_INSTANCE; | ||
224 | } else if (strcmp(name, "title") == 0) { | ||
225 | return T_TITLE; | ||
226 | } else if (strcmp(name, "urgent") == 0) { | ||
227 | return T_URGENT; | ||
228 | } else if (strcmp(name, "window_role") == 0) { | ||
229 | return T_WINDOW_ROLE; | ||
230 | } else if (strcmp(name, "window_type") == 0) { | ||
231 | return T_WINDOW_TYPE; | ||
232 | } else if (strcmp(name, "workspace") == 0) { | ||
233 | return T_WORKSPACE; | ||
234 | } | ||
235 | return T_INVALID; | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * Get a property of the focused view. | ||
240 | * | ||
241 | * Note that we are taking the focused view at the time of criteria parsing, not | ||
242 | * at the time of execution. This is because __focused__ only makes sense when | ||
243 | * using criteria via IPC. Using __focused__ in config is not useful because | ||
244 | * criteria is only executed once per view. | ||
245 | */ | ||
246 | static char *get_focused_prop(enum criteria_token token) { | ||
247 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
248 | struct sway_container *focus = seat_get_focus(seat); | ||
249 | |||
250 | if (!focus || focus->type != C_VIEW) { | ||
251 | return NULL; | ||
252 | } | ||
253 | struct sway_view *view = focus->sway_view; | ||
254 | const char *value = NULL; | ||
255 | |||
256 | switch (token) { | ||
257 | case T_APP_ID: | ||
258 | value = view_get_app_id(view); | ||
259 | break; | ||
260 | case T_CLASS: | ||
261 | value = view_get_class(view); | ||
262 | break; | ||
263 | case T_INSTANCE: | ||
264 | value = view_get_instance(view); | ||
265 | break; | ||
266 | case T_TITLE: | ||
267 | value = view_get_class(view); | ||
268 | break; | ||
269 | case T_WINDOW_ROLE: | ||
270 | value = view_get_class(view); | ||
271 | break; | ||
272 | case T_WORKSPACE: | ||
273 | { | ||
274 | struct sway_container *ws = container_parent(focus, C_WORKSPACE); | ||
275 | if (ws) { | ||
276 | value = ws->name; | ||
277 | } | ||
278 | } | ||
279 | break; | ||
280 | case T_CON_ID: // These do not support __focused__ | ||
281 | case T_CON_MARK: | ||
282 | case T_FLOATING: | ||
283 | case T_ID: | ||
284 | case T_TILING: | ||
285 | case T_URGENT: | ||
286 | case T_WINDOW_TYPE: | ||
287 | case T_INVALID: | ||
288 | break; | ||
289 | } | ||
290 | if (value) { | ||
291 | return strdup(value); | ||
292 | } | ||
293 | return NULL; | ||
294 | } | ||
295 | |||
193 | static bool parse_token(struct criteria *criteria, char *name, char *value) { | 296 | static bool parse_token(struct criteria *criteria, char *name, char *value) { |
297 | enum criteria_token token = token_from_name(name); | ||
298 | if (token == T_INVALID) { | ||
299 | const char *fmt = "Token '%s' is not recognized"; | ||
300 | int len = strlen(fmt) + strlen(name) - 1; | ||
301 | error = malloc(len); | ||
302 | snprintf(error, len, fmt, name); | ||
303 | return false; | ||
304 | } | ||
305 | |||
306 | char *effective_value = NULL; | ||
307 | if (value && strcmp(value, "__focused__") == 0) { | ||
308 | effective_value = get_focused_prop(token); | ||
309 | } else if (value) { | ||
310 | effective_value = strdup(value); | ||
311 | } | ||
312 | |||
194 | // Require value, unless token is floating or tiled | 313 | // Require value, unless token is floating or tiled |
195 | if (!value && (strcmp(name, "title") == 0 | 314 | if (!effective_value && token != T_FLOATING && token != T_TILING) { |
196 | || strcmp(name, "app_id") == 0 | ||
197 | || strcmp(name, "class") == 0 | ||
198 | || strcmp(name, "instance") == 0 | ||
199 | || strcmp(name, "con_id") == 0 | ||
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"; | 315 | const char *fmt = "Token '%s' requires a value"; |
207 | int len = strlen(fmt) + strlen(name) - 1; | 316 | int len = strlen(fmt) + strlen(name) - 1; |
208 | error = malloc(len); | 317 | error = malloc(len); |
@@ -210,53 +319,64 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
210 | return false; | 319 | return false; |
211 | } | 320 | } |
212 | 321 | ||
213 | if (strcmp(name, "title") == 0) { | 322 | char *endptr = NULL; |
214 | generate_regex(&criteria->title, value); | 323 | switch (token) { |
215 | } else if (strcmp(name, "app_id") == 0) { | 324 | case T_TITLE: |
216 | generate_regex(&criteria->app_id, value); | 325 | generate_regex(&criteria->title, effective_value); |
217 | } else if (strcmp(name, "class") == 0) { | 326 | break; |
218 | generate_regex(&criteria->class, value); | 327 | case T_APP_ID: |
219 | } else if (strcmp(name, "instance") == 0) { | 328 | generate_regex(&criteria->app_id, effective_value); |
220 | generate_regex(&criteria->instance, value); | 329 | break; |
221 | } else if (strcmp(name, "con_id") == 0) { | 330 | case T_CLASS: |
222 | char *endptr; | 331 | generate_regex(&criteria->class, effective_value); |
223 | criteria->con_id = strtoul(value, &endptr, 10); | 332 | break; |
333 | case T_INSTANCE: | ||
334 | generate_regex(&criteria->instance, effective_value); | ||
335 | break; | ||
336 | case T_CON_ID: | ||
337 | criteria->con_id = strtoul(effective_value, &endptr, 10); | ||
224 | if (*endptr != 0) { | 338 | if (*endptr != 0) { |
225 | error = strdup("The value for 'con_id' should be numeric"); | 339 | error = strdup("The value for 'con_id' should be numeric"); |
226 | } | 340 | } |
227 | } else if (strcmp(name, "con_mark") == 0) { | 341 | break; |
228 | generate_regex(&criteria->con_mark, value); | 342 | case T_CON_MARK: |
229 | } else if (strcmp(name, "window_role") == 0) { | 343 | generate_regex(&criteria->con_mark, effective_value); |
230 | generate_regex(&criteria->window_role, value); | 344 | break; |
231 | } else if (strcmp(name, "window_type") == 0) { | 345 | case T_WINDOW_ROLE: |
346 | generate_regex(&criteria->window_role, effective_value); | ||
347 | break; | ||
348 | case T_WINDOW_TYPE: | ||
232 | // TODO: This is a string but will be stored as an enum or integer | 349 | // TODO: This is a string but will be stored as an enum or integer |
233 | } else if (strcmp(name, "id") == 0) { | 350 | break; |
234 | char *endptr; | 351 | case T_ID: |
235 | criteria->id = strtoul(value, &endptr, 10); | 352 | criteria->id = strtoul(effective_value, &endptr, 10); |
236 | if (*endptr != 0) { | 353 | if (*endptr != 0) { |
237 | error = strdup("The value for 'id' should be numeric"); | 354 | error = strdup("The value for 'id' should be numeric"); |
238 | } | 355 | } |
239 | } else if (strcmp(name, "floating") == 0) { | 356 | break; |
357 | case T_FLOATING: | ||
240 | criteria->floating = true; | 358 | criteria->floating = true; |
241 | } else if (strcmp(name, "tiling") == 0) { | 359 | break; |
360 | case T_TILING: | ||
242 | criteria->tiling = true; | 361 | criteria->tiling = true; |
243 | } else if (strcmp(name, "urgent") == 0) { | 362 | break; |
244 | if (strcmp(value, "latest") == 0) { | 363 | case T_URGENT: |
364 | if (strcmp(effective_value, "latest") == 0) { | ||
245 | criteria->urgent = 'l'; | 365 | criteria->urgent = 'l'; |
246 | } else if (strcmp(value, "oldest") == 0) { | 366 | } else if (strcmp(effective_value, "oldest") == 0) { |
247 | criteria->urgent = 'o'; | 367 | criteria->urgent = 'o'; |
248 | } else { | 368 | } else { |
249 | error = | 369 | error = |
250 | strdup("The value for 'urgent' must be 'latest' or 'oldest'"); | 370 | strdup("The value for 'urgent' must be 'latest' or 'oldest'"); |
251 | } | 371 | } |
252 | } else if (strcmp(name, "workspace") == 0) { | 372 | break; |
253 | criteria->workspace = strdup(value); | 373 | case T_WORKSPACE: |
254 | } else { | 374 | criteria->workspace = strdup(effective_value); |
255 | const char *fmt = "Token '%s' is not recognized"; | 375 | break; |
256 | int len = strlen(fmt) + strlen(name) - 1; | 376 | case T_INVALID: |
257 | error = malloc(len); | 377 | break; |
258 | snprintf(error, len, fmt, name); | ||
259 | } | 378 | } |
379 | free(effective_value); | ||
260 | 380 | ||
261 | if (error) { | 381 | if (error) { |
262 | return false; | 382 | return false; |
diff --git a/sway/tree/view.c b/sway/tree/view.c index 8fc45f52..f872bef6 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -81,6 +81,13 @@ uint32_t view_get_x11_window_id(struct sway_view *view) { | |||
81 | return 0; | 81 | return 0; |
82 | } | 82 | } |
83 | 83 | ||
84 | const char *view_get_window_role(struct sway_view *view) { | ||
85 | if (view->impl->get_string_prop) { | ||
86 | return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE); | ||
87 | } | ||
88 | return NULL; | ||
89 | } | ||
90 | |||
84 | uint32_t view_get_window_type(struct sway_view *view) { | 91 | uint32_t view_get_window_type(struct sway_view *view) { |
85 | if (view->impl->get_int_prop) { | 92 | if (view->impl->get_int_prop) { |
86 | return view->impl->get_int_prop(view, VIEW_PROP_WINDOW_TYPE); | 93 | return view->impl->get_int_prop(view, VIEW_PROP_WINDOW_TYPE); |