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) { if (!containers) { current_container = get_focused_container(&root_container); } else if (containers->length == 0) { - break; + if (results) { + free_cmd_results(results); + } + results = cmd_results_new(CMD_FAILURE, argv[0], "No matching container"); + goto cleanup; } else { current_container = (swayc_t *)containers->items[i]; } 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 @@ enum criteria_type { // *must* keep in sync with criteria_strings[] CRIT_CLASS, + CRIT_CON_ID, CRIT_CON_MARK, + CRIT_FLOATING, CRIT_ID, CRIT_INSTANCE, + CRIT_TILING, CRIT_TITLE, CRIT_URGENT, CRIT_WINDOW_ROLE, @@ -25,9 +28,12 @@ enum criteria_type { // *must* keep in sync with criteria_strings[] static const char * const criteria_strings[CRIT_LAST] = { [CRIT_CLASS] = "class", + [CRIT_CON_ID] = "con_id", [CRIT_CON_MARK] = "con_mark", + [CRIT_FLOATING] = "floating", [CRIT_ID] = "id", [CRIT_INSTANCE] = "instance", + [CRIT_TILING] = "tiling", [CRIT_TITLE] = "title", [CRIT_URGENT] = "urgent", // either "latest" or "oldest" ... [CRIT_WINDOW_ROLE] = "window_role", @@ -108,6 +114,7 @@ static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str char **argv = *buf = calloc(max_tokens, sizeof(char*)); argv[0] = base; // this needs to be freed by caller + bool quoted = true; *argc = 1; // uneven = name, even = value while (*head && *argc < max_tokens) { @@ -128,7 +135,8 @@ static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str if (*(namep) == ' ') { namep = strrchr(namep, ' ') + 1; } - argv[(*argc)++] = namep; + argv[*argc] = namep; + *argc += 1; } } else if (*head == '"') { if (*argc % 2 != 0) { @@ -137,21 +145,38 @@ static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str "Found quoted value where it was not expected"); } else if (!valp) { // value starts here valp = head + 1; + quoted = true; } else { // value ends here - argv[(*argc)++] = valp; + argv[*argc] = valp; + *argc += 1; *head = '\0'; valp = NULL; namep = head + 1; } - } else if (*argc % 2 == 0 && !valp && *head != ' ') { - // We're expecting a quoted value, haven't found one yet, and this - // is not an empty space. - return strdup("Unable to parse criteria: " - "Names must be unquoted, values must be quoted"); + } else if (*argc % 2 == 0 && *head != ' ') { + // parse unquoted values + if (!valp) { + quoted = false; + valp = head; // value starts here + } + } else if (valp && !quoted && *head == ' ') { + // value ends here + argv[*argc] = valp; + *argc += 1; + *head = '\0'; + valp = NULL; + namep = head + 1; } head++; } + + // catch last unquoted value if needed + if (valp && !quoted && !*head) { + argv[*argc] = valp; + *argc += 1; + } + return NULL; } @@ -263,6 +288,15 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) { matches++; } break; + case CRIT_CON_ID: { + char *endptr; + size_t crit_id = strtoul(crit->raw, &endptr, 10); + + if (*endptr == 0 && cont->id == crit_id) { + ++matches; + } + break; + } case CRIT_CON_MARK: if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) { // Make sure it isn't matching the NUL string @@ -271,6 +305,11 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) { } } break; + case CRIT_FLOATING: + if (cont->is_floating) { + matches++; + } + break; case CRIT_ID: if (!cont->app_id) { // ignore @@ -290,6 +329,11 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) { matches++; } break; + case CRIT_TILING: + if (!cont->is_floating) { + matches++; + } + break; case CRIT_TITLE: if (!cont->name) { // ignore diff --git a/sway/handlers.c b/sway/handlers.c index 4a2298cd..db0c5e24 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -557,6 +557,8 @@ static void handle_view_destroyed(wlc_handle handle) { parent->fullscreen = NULL; } + ipc_event_window(parent, "close"); + // Destroy empty workspaces if (parent->type == C_WORKSPACE && parent->children->length == 0 && @@ -567,7 +569,6 @@ static void handle_view_destroyed(wlc_handle handle) { } arrange_windows(parent, -1, -1); - ipc_event_window(parent, "close"); } else { // Is it unmanaged? int i; diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 31de53f0..94768aa4 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) json_object_object_add(object, "num", json_object_new_int(num)); json_object_object_add(object, "output", (workspace->parent) ? json_object_new_string(workspace->parent->name) : NULL); - json_object_object_add(object, "urgent", json_object_new_boolean(false)); json_object_object_add(object, "type", json_object_new_string("workspace")); json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout)); } @@ -156,7 +155,6 @@ static const char *ipc_json_get_scratchpad_state(swayc_t *c) { static void ipc_json_describe_view(swayc_t *c, json_object *object) { json_object *props = json_object_new_object(); - float percent = ipc_json_child_percentage(c); const char *layout = (c->parent->type == C_CONTAINER) ? ipc_json_layout_description(c->parent->layout) : "none"; const char *last_layout = (c->parent->type == C_CONTAINER) ? @@ -167,9 +165,6 @@ static void ipc_json_describe_view(swayc_t *c, json_object *object) { json_object_object_add(object, "scratchpad_state", json_object_new_string(ipc_json_get_scratchpad_state(c))); - json_object_object_add(object, "percent", (percent > 0) ? json_object_new_double(percent) : NULL); - // TODO: make urgency actually work once Sway supports it - json_object_object_add(object, "urgent", json_object_new_boolean(false)); json_object_object_add(object, "layout", (strcmp(layout, "null") == 0) ? NULL : json_object_new_string(layout)); json_object_object_add(object, "last_split_layout", @@ -177,17 +172,8 @@ static void ipc_json_describe_view(swayc_t *c, json_object *object) { json_object_object_add(object, "workspace_layout", json_object_new_string(ipc_json_layout_description(swayc_parent_by_type(c, C_WORKSPACE)->workspace_layout))); - json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c))); - json_object_object_add(object, "current_border_width", json_object_new_int(c->border_thickness)); - - json_object_object_add(object, "rect", ipc_json_create_rect(c)); - json_object_object_add(object, "deco_rect", ipc_json_create_rect_from_geometry(c->title_bar_geometry)); - json_object_object_add(object, "geometry", ipc_json_create_rect_from_geometry(c->cached_geometry)); - json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry)); - json_object_object_add(object, "name", (c->name) ? json_object_new_string(c->name) : NULL); - json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat json_object_object_add(props, "class", c->class ? json_object_new_string(c->class) : c->app_id ? json_object_new_string(c->app_id) : NULL); json_object_object_add(props, "instance", c->instance ? json_object_new_string(c->instance) : @@ -205,7 +191,14 @@ static void ipc_json_describe_view(swayc_t *c, json_object *object) { json_object_object_add(object, "app_id", c->app_id ? json_object_new_string(c->app_id) : NULL); } +static void ipc_json_describe_root(swayc_t *c, json_object *object) { + json_object_object_add(object, "type", json_object_new_string("root")); + json_object_object_add(object, "layout", json_object_new_string("splith")); +} + json_object *ipc_json_describe_container(swayc_t *c) { + float percent = ipc_json_child_percentage(c); + if (!(sway_assert(c, "Container must not be null."))) { return NULL; } @@ -218,9 +211,19 @@ json_object *ipc_json_describe_container(swayc_t *c) { json_object_object_add(object, "visible", json_object_new_boolean(c->visible)); json_object_object_add(object, "focused", json_object_new_boolean(c == current_focus)); + json_object_object_add(object, "border", json_object_new_string(ipc_json_border_description(c))); + json_object_object_add(object, "window_rect", ipc_json_create_rect_from_geometry(c->actual_geometry)); + json_object_object_add(object, "deco_rect", ipc_json_create_rect_from_geometry(c->title_bar_geometry)); + json_object_object_add(object, "geometry", ipc_json_create_rect_from_geometry(c->cached_geometry)); + json_object_object_add(object, "percent", (percent > 0) ? json_object_new_double(percent) : NULL); + json_object_object_add(object, "window", json_object_new_int(c->handle)); // for the sake of i3 compat + // TODO: make urgency actually work once Sway supports it + json_object_object_add(object, "urgent", json_object_new_boolean(false)); + json_object_object_add(object, "current_border_width", json_object_new_int(c->border_thickness)); + switch (c->type) { case C_ROOT: - json_object_object_add(object, "type", json_object_new_string("root")); + ipc_json_describe_root(c, object); break; case C_OUTPUT: @@ -451,21 +454,50 @@ json_object *ipc_json_describe_container_recursive(swayc_t *c) { int i; json_object *floating = json_object_new_array(); - if (c->type != C_VIEW && c->floating && c->floating->length > 0) { + if (c->type != C_VIEW && c->floating) { for (i = 0; i < c->floating->length; ++i) { - json_object_array_add(floating, ipc_json_describe_container_recursive(c->floating->items[i])); + swayc_t *item = c->floating->items[i]; + json_object_array_add(floating, ipc_json_describe_container_recursive(item)); } } json_object_object_add(object, "floating_nodes", floating); json_object *children = json_object_new_array(); - if (c->type != C_VIEW && c->children && c->children->length > 0) { + if (c->type != C_VIEW && c->children) { for (i = 0; i < c->children->length; ++i) { json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i])); } } json_object_object_add(object, "nodes", children); + json_object *focus = json_object_new_array(); + if (c->type != C_VIEW) { + if (c->focused) { + json_object_array_add(focus, json_object_new_double(c->focused->id)); + } + if (c->floating) { + for (i = 0; i < c->floating->length; ++i) { + swayc_t *item = c->floating->items[i]; + if (item == c->focused) { + continue; + } + + json_object_array_add(focus, json_object_new_double(item->id)); + } + } + if (c->children) { + for (i = 0; i < c->children->length; ++i) { + swayc_t *item = c->children->items[i]; + if (item == c->focused) { + continue; + } + + json_object_array_add(focus, json_object_new_double(item->id)); + } + } + } + json_object_object_add(object, "focus", focus); + if (c->type == C_ROOT) { json_object *scratchpad_json = json_object_new_array(); if (scratchpad->length > 0) { diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 26d0be65..9122d548 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -792,11 +792,7 @@ void ipc_event_window(swayc_t *window, const char *change) { sway_log(L_DEBUG, "Sending window::%s event", change); json_object *obj = json_object_new_object(); json_object_object_add(obj, "change", json_object_new_string(change)); - if (strcmp(change, "close") == 0 || !window) { - json_object_object_add(obj, "container", NULL); - } else { - json_object_object_add(obj, "container", ipc_json_describe_container(window)); - } + json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); const char *json_string = json_object_to_json_string(obj); ipc_send_event(json_string, IPC_EVENT_WINDOW);