diff --git a/include/commands_parser.h b/include/commands_parser.h index 3ca21075..e2557eac 100644 --- a/include/commands_parser.h +++ b/include/commands_parser.h @@ -12,6 +12,18 @@ #include #include +#include "parser_util.h" +#include "queue.h" + +/** + * Helper data structure for an operation window (window on which the operation + * will be performed). Used to build the TAILQ owindows. + * + */ +typedef struct owindow { + Con *con; + TAILQ_ENTRY(owindow) owindows; +} owindow; /** * Holds an intermediate representation of the result of a call to any command. @@ -19,6 +31,9 @@ * internally use this struct when calling cmd_floating and cmd_border. */ struct CommandResultIR { + /* The parser context this command is executing in. */ + struct cmd_parser_ctx *ctx; + /* The JSON generator to append a reply to (may be NULL). */ yajl_gen json_gen; @@ -35,6 +50,28 @@ struct CommandResultIR { bool needs_tree_render; }; +/* Define the owindows head structure here so it's complete */ +TAILQ_HEAD(owindows_head, owindow); + +/** + * Context structure for the command parser, making it re-entrant. + */ +struct cmd_parser_ctx { + int state; + Match current_match; + + /* The (small) stack where identified literals are stored during the parsing + * of a single command (like $workspace). */ + struct stack stack; + + /* List of operation windows (windows on which operations will be performed). + * Used to build the TAILQ owindows. */ + struct owindows_head owindows; + + struct CommandResultIR subcommand_output; + struct CommandResultIR command_output; +}; + typedef struct CommandResult CommandResult; /** diff --git a/src/commands.c b/src/commands.c index 3f30ce2c..91cb302c 100644 --- a/src/commands.c +++ b/src/commands.c @@ -61,15 +61,15 @@ HANDLE_INVALID_MATCH; \ \ if (match_is_empty(current_match)) { \ - while (!TAILQ_EMPTY(&owindows)) { \ - owindow *ow = TAILQ_FIRST(&owindows); \ - TAILQ_REMOVE(&owindows, ow, owindows); \ + while (!TAILQ_EMPTY(&OWINDOWS)) { \ + owindow *ow = TAILQ_FIRST(&OWINDOWS); \ + TAILQ_REMOVE(&OWINDOWS, ow, owindows); \ free(ow); \ } \ owindow *ow = smalloc(sizeof(owindow)); \ ow->con = focused; \ - TAILQ_INIT(&owindows); \ - TAILQ_INSERT_TAIL(&owindows, ow, owindows); \ + TAILQ_INIT(&OWINDOWS); \ + TAILQ_INSERT_TAIL(&OWINDOWS, ow, owindows); \ } \ } while (0) @@ -124,19 +124,8 @@ static Con *maybe_auto_back_and_forth_workspace(Con *workspace) { * Criteria functions. ******************************************************************************/ -/* - * Helper data structure for an operation window (window on which the operation - * will be performed). Used to build the TAILQ owindows. - * - */ -typedef struct owindow { - Con *con; - TAILQ_ENTRY(owindow) owindows; -} owindow; - -typedef TAILQ_HEAD(owindows_head, owindow) owindows_head; - -static owindows_head owindows; +/* Macro to access owindows from the command output context */ +#define OWINDOWS (cmd_output->ctx->owindows) /* * Initializes the specified 'Match' data structure and the initial state of @@ -158,12 +147,12 @@ void cmd_criteria_match_windows(I3_CMD) { DLOG("match specification finished, matching...\n"); /* Clear old queue */ - while (!TAILQ_EMPTY(&owindows)) { - owindow *ow = TAILQ_FIRST(&owindows); - TAILQ_REMOVE(&owindows, ow, owindows); + while (!TAILQ_EMPTY(&OWINDOWS)) { + owindow *ow = TAILQ_FIRST(&OWINDOWS); + TAILQ_REMOVE(&OWINDOWS, ow, owindows); free(ow); } - TAILQ_INIT(&owindows); + TAILQ_INIT(&OWINDOWS); /* Go through all cons and find matches */ Con *con; @@ -220,7 +209,7 @@ void cmd_criteria_match_windows(I3_CMD) { DLOG("matching: %p / %s\n", con, con->name); owindow *ow = smalloc(sizeof(owindow)); ow->con = con; - TAILQ_INSERT_TAIL(&owindows, ow, owindows); + TAILQ_INSERT_TAIL(&OWINDOWS, ow, owindows); } } } @@ -234,9 +223,9 @@ void cmd_criteria_add(I3_CMD, const char *ctype, const char *cvalue) { match_parse_property(current_match, ctype, cvalue); } -static void move_matches_to_workspace(Con *ws) { +static void move_matches_to_workspace(struct owindows_head *owindows, Con *ws) { owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, ws, true, false, false); } @@ -245,17 +234,17 @@ static void move_matches_to_workspace(Con *ws) { #define CHECK_MOVE_CON_TO_WORKSPACE \ do { \ HANDLE_EMPTY_MATCH; \ - if (TAILQ_EMPTY(&owindows)) { \ + if (TAILQ_EMPTY(&OWINDOWS)) { \ yerror("Nothing to move: specified criteria don't match any window"); \ return; \ } else { \ bool found = false; \ - owindow *current = TAILQ_FIRST(&owindows); \ + owindow *current = TAILQ_FIRST(&OWINDOWS); \ while (current) { \ owindow *next = TAILQ_NEXT(current, owindows); \ \ if (current->con->type == CT_WORKSPACE && !con_has_children(current->con)) { \ - TAILQ_REMOVE(&owindows, current, owindows); \ + TAILQ_REMOVE(&OWINDOWS, current, owindows); \ } else { \ found = true; \ } \ @@ -296,7 +285,7 @@ void cmd_move_con_to_workspace(I3_CMD, const char *which) { return; } - move_matches_to_workspace(ws); + move_matches_to_workspace(&OWINDOWS, ws); cmd_output->needs_tree_render = true; // XXX: default reply for now, make this a better reply @@ -316,7 +305,7 @@ void cmd_move_con_to_workspace_back_and_forth(I3_CMD) { HANDLE_EMPTY_MATCH; - move_matches_to_workspace(ws); + move_matches_to_workspace(&OWINDOWS, ws); cmd_output->needs_tree_render = true; // XXX: default reply for now, make this a better reply @@ -343,7 +332,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, const char *name, const char *no_aut ws = maybe_auto_back_and_forth_workspace(ws); } - move_matches_to_workspace(ws); + move_matches_to_workspace(&OWINDOWS, ws); cmd_output->needs_tree_render = true; // XXX: default reply for now, make this a better reply @@ -375,7 +364,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, const char *which, const char *no_ ws = maybe_auto_back_and_forth_workspace(ws); } - move_matches_to_workspace(ws); + move_matches_to_workspace(&OWINDOWS, ws); cmd_output->needs_tree_render = true; // XXX: default reply for now, make this a better reply @@ -564,7 +553,7 @@ void cmd_resize(I3_CMD, const char *way, const char *direction, long resize_px, HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { /* Don't handle dock windows (issue #1201) */ if (current->con->window && current->con->window->dock) { DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con); @@ -644,7 +633,7 @@ void cmd_resize_set(I3_CMD, long cwidth, const char *mode_width, long cheight, c owindow *current; bool success = true; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { Con *floating_con; if ((floating_con = con_inside_floating(current->con))) { Con *output = con_get_output(floating_con); @@ -712,7 +701,7 @@ void cmd_border(I3_CMD, const char *border_style_str, long border_width) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); border_style_t border_style; @@ -947,14 +936,14 @@ void cmd_workspace_name(I3_CMD, const char *name, const char *_no_auto_back_and_ void cmd_mark(I3_CMD, const char *mark, const char *mode, const char *toggle) { HANDLE_EMPTY_MATCH; - owindow *current = TAILQ_FIRST(&owindows); + owindow *current = TAILQ_FIRST(&OWINDOWS); if (current == NULL) { yerror("Given criteria don't match a window"); return; } /* Marks must be unique, i.e., no two windows must have the same mark. */ - if (current != TAILQ_LAST(&owindows, owindows_head)) { + if (current != TAILQ_LAST(&OWINDOWS, owindows_head)) { yerror("A mark must not be put onto more than one window"); return; } @@ -982,7 +971,7 @@ void cmd_unmark(I3_CMD, const char *mark) { con_unmark(NULL, mark); } else { owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { con_unmark(current->con, mark); } } @@ -1103,7 +1092,7 @@ void cmd_move_con_to_output(I3_CMD, const char *name, bool move_workspace) { bool success = false; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { Con *ws = con_get_workspace(current->con); if (con_is_internal(ws)) { continue; @@ -1141,7 +1130,7 @@ void cmd_move_con_to_mark(I3_CMD, const char *mark) { bool result = true; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { DLOG("moving matched window %p / %s to mark \"%s\"\n", current->con, current->con->name, mark); result &= con_move_to_mark(current->con, mark); } @@ -1161,7 +1150,7 @@ void cmd_floating(I3_CMD, const char *floating_mode) { HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); if (strcmp(floating_mode, "toggle") == 0) { DLOG("should toggle mode\n"); @@ -1174,6 +1163,7 @@ void cmd_floating(I3_CMD, const char *floating_mode) { floating_disable(current->con); } } + run_assignments(current->con->window); } cmd_output->needs_tree_render = true; @@ -1190,7 +1180,7 @@ void cmd_split(I3_CMD, const char *direction) { owindow *current; LOG("splitting in direction %c\n", direction[0]); - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { if (con_is_docked(current->con)) { ELOG("Cannot split a docked container, skipping.\n"); continue; @@ -1244,7 +1234,7 @@ void cmd_kill(I3_CMD, const char *kill_mode_str) { HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { con_close(current->con, kill_mode); } @@ -1264,7 +1254,7 @@ void cmd_exec(I3_CMD, const char *nosn, const char *command) { int count = 0; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { count++; } @@ -1274,7 +1264,7 @@ void cmd_exec(I3_CMD, const char *nosn, const char *command) { count); } - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { DLOG("should execute %s, no_startup_id = %d\n", command, no_startup_id); start_application(command, no_startup_id); } @@ -1286,7 +1276,7 @@ void cmd_exec(I3_CMD, const char *nosn, const char *command) { do { \ int count = 0; \ owindow *current; \ - TAILQ_FOREACH (current, &owindows, owindows) { \ + TAILQ_FOREACH (current, &OWINDOWS, owindows) { \ count++; \ } \ \ @@ -1318,7 +1308,7 @@ void cmd_focus_direction(I3_CMD, const char *direction_str) { } owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { Con *ws = con_get_workspace(current->con); if (!ws || con_is_internal(ws)) { continue; @@ -1345,7 +1335,7 @@ void cmd_focus_sibling(I3_CMD, const char *direction_str) { const position_t direction = (STARTS_WITH(direction_str, "prev")) ? BEFORE : AFTER; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { Con *ws = con_get_workspace(current->con); if (!ws || con_is_internal(ws)) { continue; @@ -1451,7 +1441,7 @@ void cmd_focus(I3_CMD, bool focus_workspace) { yerror("You have to specify which window/container should be focused"); return; - } else if (TAILQ_EMPTY(&owindows)) { + } else if (TAILQ_EMPTY(&OWINDOWS)) { yerror("No window matches given criteria"); return; } @@ -1460,7 +1450,7 @@ void cmd_focus(I3_CMD, bool focus_workspace) { Con *__i3_scratch = workspace_get("__i3_scratch"); owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { Con *ws = con_get_workspace(current->con); /* If no workspace could be found, this was a dock window. * Just skip it, you cannot focus dock windows. */ @@ -1504,7 +1494,7 @@ void cmd_fullscreen(I3_CMD, const char *action, const char *fullscreen_mode) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); if (strcmp(action, "toggle") == 0) { con_toggle_fullscreen(current->con, mode); @@ -1529,7 +1519,7 @@ void cmd_sticky(I3_CMD, const char *action) { HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { if (current->con->window == NULL) { ELOG("only containers holding a window can be made sticky, skipping con = %p\n", current->con); continue; @@ -1572,7 +1562,7 @@ void cmd_move_direction(I3_CMD, const char *direction_str, long amount, const ch const bool is_ppt = mode && strcmp(mode, "ppt") == 0; DLOG("moving in direction %s, %ld %s\n", direction_str, amount, mode); - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { if (con_is_floating(current->con)) { DLOG("floating move with %ld %s\n", amount, mode); Rect newrect = current->con->parent->rect; @@ -1620,7 +1610,7 @@ void cmd_layout(I3_CMD, const char *layout_str) { DLOG("changing layout to %s (%d)\n", layout_str, layout); owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { if (con_is_docked(current->con)) { ELOG("cannot change layout of a docked container, skipping it.\n"); continue; @@ -1652,7 +1642,7 @@ void cmd_layout_toggle(I3_CMD, const char *toggle_mode) { if (match_is_empty(current_match)) { con_toggle_layout(focused, toggle_mode); } else { - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_toggle_layout(current->con, toggle_mode); } @@ -1774,7 +1764,7 @@ void cmd_focus_output(I3_CMD, const char *name) { HANDLE_EMPTY_MATCH; - if (TAILQ_EMPTY(&owindows)) { + if (TAILQ_EMPTY(&OWINDOWS)) { ysuccess(true); return; } @@ -1785,7 +1775,7 @@ void cmd_focus_output(I3_CMD, const char *name) { * there is no match, fall back to the focused one. */ owindow *current; Con *con = focused; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { if (!con_is_internal(con_get_workspace(current->con))) { con = current->con; break; @@ -1828,7 +1818,7 @@ void cmd_move_window_to_position(I3_CMD, long x, const char *mode_x, long y, con owindow *current; HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { if (!con_is_floating(current->con)) { ELOG("Cannot change position. The window/container is not floating\n"); @@ -1867,7 +1857,7 @@ void cmd_move_window_to_center(I3_CMD, const char *method) { HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { Con *floating_con = con_inside_floating(current->con); if (floating_con == NULL) { ELOG("con %p / %s is not floating, cannot move it to the center.\n", @@ -1911,7 +1901,7 @@ void cmd_move_window_to_mouse(I3_CMD) { HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { Con *floating_con = con_inside_floating(current->con); if (floating_con == NULL) { DLOG("con %p / %s is not floating, cannot move it to the mouse position.\n", @@ -1937,7 +1927,7 @@ void cmd_move_scratchpad(I3_CMD) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); scratchpad_move(current->con); } @@ -1959,7 +1949,7 @@ void cmd_scratchpad_show(I3_CMD) { if (match_is_empty(current_match)) { result = scratchpad_show(NULL); } else { - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); result |= scratchpad_show(current->con); } @@ -1977,7 +1967,7 @@ void cmd_scratchpad_show(I3_CMD) { void cmd_swap(I3_CMD, const char *mode, const char *arg) { HANDLE_EMPTY_MATCH; - owindow *match = TAILQ_FIRST(&owindows); + owindow *match = TAILQ_FIRST(&OWINDOWS); if (match == NULL) { yerror("No match found for swapping."); return; @@ -2016,7 +2006,7 @@ void cmd_swap(I3_CMD, const char *mode, const char *arg) { return; } - if (match != TAILQ_LAST(&owindows, owindows_head)) { + if (match != TAILQ_LAST(&OWINDOWS, owindows_head)) { LOG("More than one container matched the swap command, only using the first one."); } @@ -2037,7 +2027,7 @@ void cmd_title_format(I3_CMD, const char *format) { HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { DLOG("setting title_format for %p / %s\n", current->con, current->con->name); FREE(current->con->title_format); @@ -2088,7 +2078,7 @@ void cmd_title_window_icon(I3_CMD, const char *enable, int padding) { HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH (current, &OWINDOWS, owindows) { if (is_toggle) { const int current_padding = current->con->window_icon_padding; if (padding > 0) { diff --git a/src/commands_parser.c b/src/commands_parser.c index 46bd4029..c355f2a8 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -26,9 +26,9 @@ #include "all.h" #include "parser_util.h" -// Macros to make the YAJL API a bit easier to use. -#define y(x, ...) (command_output.json_gen != NULL ? yajl_gen_##x(command_output.json_gen, ##__VA_ARGS__) : 0) -#define ystr(str) (command_output.json_gen != NULL ? yajl_gen_string(command_output.json_gen, (unsigned char *)str, strlen(str)) : 0) +/* Macros to make the YAJL API a bit easier to use. */ +#define y(x, ...) (cmd_ctx.command_output.json_gen != NULL ? yajl_gen_##x(cmd_ctx.command_output.json_gen, ##__VA_ARGS__) : 0) +#define ystr(str) (cmd_ctx.command_output.json_gen != NULL ? yajl_gen_string(cmd_ctx.command_output.json_gen, (unsigned char *)str, strlen(str)) : 0) /******************************************************************************* * The data structures used for parsing. Essentially the current state and a @@ -61,37 +61,28 @@ typedef struct tokenptr { * The parser itself. ******************************************************************************/ -static cmdp_state state; -static Match current_match; -/******************************************************************************* - * The (small) stack where identified literals are stored during the parsing - * of a single command (like $workspace). - ******************************************************************************/ -static struct stack stack; -static struct CommandResultIR subcommand_output; -static struct CommandResultIR command_output; - #include "GENERATED_command_call.h" -static void next_state(const cmdp_token *token) { +static void next_state(const cmdp_token *token, struct cmd_parser_ctx *cmd_ctx) { if (token->next_state == __CALL) { - subcommand_output.json_gen = command_output.json_gen; - subcommand_output.client = command_output.client; - subcommand_output.needs_tree_render = false; - GENERATED_call(¤t_match, &stack, token->extra.call_identifier, &subcommand_output); - state = subcommand_output.next_state; + cmd_ctx->subcommand_output.ctx = cmd_ctx; + cmd_ctx->subcommand_output.json_gen = cmd_ctx->command_output.json_gen; + cmd_ctx->subcommand_output.client = cmd_ctx->command_output.client; + cmd_ctx->subcommand_output.needs_tree_render = false; + GENERATED_call(&cmd_ctx->current_match, &cmd_ctx->stack, token->extra.call_identifier, &cmd_ctx->subcommand_output); + cmd_ctx->state = cmd_ctx->subcommand_output.next_state; /* If any subcommand requires a tree_render(), we need to make the * whole parser result request a tree_render(). */ - if (subcommand_output.needs_tree_render) { - command_output.needs_tree_render = true; + if (cmd_ctx->subcommand_output.needs_tree_render) { + cmd_ctx->command_output.needs_tree_render = true; } - parser_clear_stack(&stack); + parser_clear_stack(&cmd_ctx->stack); return; } - state = token->next_state; - if (state == INITIAL) { - parser_clear_stack(&stack); + cmd_ctx->state = token->next_state; + if (cmd_ctx->state == INITIAL) { + parser_clear_stack(&cmd_ctx->stack); } } @@ -166,16 +157,19 @@ char *parse_string(const char **walk, const bool as_word) { */ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client) { DLOG("COMMAND: *%.4000s*\n", input); - state = INITIAL; + struct cmd_parser_ctx cmd_ctx = {0}; + + cmd_ctx.state = INITIAL; CommandResult *result = scalloc(1, sizeof(CommandResult)); - command_output.client = client; + cmd_ctx.command_output.ctx = &cmd_ctx; + cmd_ctx.command_output.client = client; /* A YAJL JSON generator used for formatting replies. */ - command_output.json_gen = gen; + cmd_ctx.command_output.json_gen = gen; y(array_open); - command_output.needs_tree_render = false; + cmd_ctx.command_output.needs_tree_render = false; const char *walk = input; const size_t len = strlen(input); @@ -185,7 +179,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client // TODO: make this testable #ifndef TEST_PARSER - cmd_criteria_init(¤t_match, &subcommand_output); + cmd_criteria_init(&cmd_ctx.current_match, &cmd_ctx.subcommand_output); #endif /* The "<=" operator is intentional: We also handle the terminating 0-byte @@ -198,7 +192,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client walk++; } - const cmdp_token_ptr *ptr = &(tokens[state]); + const cmdp_token_ptr *ptr = &(tokens[cmd_ctx.state]); token_handled = false; for (c = 0; c < ptr->n; c++) { token = &(ptr->array[c]); @@ -207,10 +201,10 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client if (token->name[0] == '\'') { if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) { if (token->identifier != NULL) { - parser_push_string(&stack, token->identifier, token->name + 1); + parser_push_string(&cmd_ctx.stack, token->identifier, token->name + 1); } walk += strlen(token->name) - 1; - next_state(token); + next_state(token, &cmd_ctx); token_handled = true; break; } @@ -233,12 +227,12 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client } if (token->identifier != NULL) { - parser_push_long(&stack, token->identifier, num); + parser_push_long(&cmd_ctx.stack, token->identifier, num); } /* Set walk to the first non-number character */ walk = end; - next_state(token); + next_state(token, &cmd_ctx); token_handled = true; break; } @@ -248,7 +242,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client char *str = parse_string(&walk, (token->name[0] != 's')); if (str != NULL) { if (token->identifier) { - parser_push_string(&stack, token->identifier, str); + parser_push_string(&cmd_ctx.stack, token->identifier, str); } free(str); /* If we are at the end of a quoted string, skip the ending @@ -256,7 +250,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client if (*walk == '"') { walk++; } - next_state(token); + next_state(token, &cmd_ctx); token_handled = true; break; } @@ -264,7 +258,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client if (strcmp(token->name, "end") == 0) { if (*walk == '\0' || *walk == ',' || *walk == ';') { - next_state(token); + next_state(token, &cmd_ctx); token_handled = true; /* To make sure we start with an appropriate matching * datastructure for commands which do *not* specify any @@ -273,7 +267,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client // TODO: make this testable #ifndef TEST_PARSER if (*walk == '\0' || *walk == ';') { - cmd_criteria_init(¤t_match, &subcommand_output); + cmd_criteria_init(&cmd_ctx.current_match, &cmd_ctx.subcommand_output); } #endif walk++; @@ -356,14 +350,20 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client y(map_close); free(position); - parser_clear_stack(&stack); + parser_clear_stack(&cmd_ctx.stack); break; } } y(array_close); - result->needs_tree_render = command_output.needs_tree_render; + result->needs_tree_render = cmd_ctx.command_output.needs_tree_render; + /* Clean up owindows entries */ + while (!TAILQ_EMPTY(&cmd_ctx.owindows)) { + struct owindow *ow = TAILQ_FIRST(&cmd_ctx.owindows); + TAILQ_REMOVE(&cmd_ctx.owindows, ow, owindows); + free(ow); + } return result; } diff --git a/src/handlers.c b/src/handlers.c index c3446636..85bc039f 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -895,6 +895,7 @@ static void handle_client_message(xcb_client_message_event_t *event) { con->sticky = true; ewmh_update_sticky(con->window->id, true); output_push_sticky_windows(focused); + run_assignments(con->window); } } else { Con *ws = ewmh_get_workspace_by_index(index); @@ -1304,25 +1305,6 @@ static bool handle_strut_partial_change(Con *con, xcb_get_property_reply_t *prop return true; } -/* - * Handles the _I3_FLOATING_WINDOW property to properly run assignments for - * floating window changes. - * - * This is needed to correctly run the assignments after changes in floating - * windows which are triggered by user commands (floating enable | disable). In - * that case, we can't call run_assignments because it will modify the parser - * state when it needs to parse the user-specified action, breaking the parser - * state for the original command. - * - */ -static bool handle_i3_floating(Con *con, xcb_get_property_reply_t *prop) { - DLOG("floating change for con %p\n", con); - - remanage_window(con); - - return true; -} - static bool handle_windowicon_change(Con *con, xcb_get_property_reply_t *prop) { window_update_icon(con->window, prop); @@ -1352,7 +1334,6 @@ static struct property_handler_t property_handlers[] = { {0, 128, handle_class_change}, {0, UINT_MAX, handle_strut_partial_change}, {0, UINT_MAX, handle_window_type}, - {0, UINT_MAX, handle_i3_floating}, {0, 128, handle_machine_change}, {0, 5 * sizeof(uint64_t), handle_motif_hints_change}, {0, UINT_MAX, handle_windowicon_change}}; @@ -1376,10 +1357,9 @@ void property_handlers_init(void) { property_handlers[7].atom = XCB_ATOM_WM_CLASS; property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL; property_handlers[9].atom = A__NET_WM_WINDOW_TYPE; - property_handlers[10].atom = A_I3_FLOATING_WINDOW; - property_handlers[11].atom = XCB_ATOM_WM_CLIENT_MACHINE; - property_handlers[12].atom = A__MOTIF_WM_HINTS; - property_handlers[13].atom = A__NET_WM_ICON; + property_handlers[10].atom = XCB_ATOM_WM_CLIENT_MACHINE; + property_handlers[11].atom = A__MOTIF_WM_HINTS; + property_handlers[12].atom = A__NET_WM_ICON; } static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { diff --git a/src/move.c b/src/move.c index f7f62c8b..036f65ec 100644 --- a/src/move.c +++ b/src/move.c @@ -293,6 +293,7 @@ void tree_move(Con *con, direction_t direction) { if (con_is_floating(con)) { /* this is a floating con, we just disable floating */ floating_disable(con); + run_assignments(con->window); return; } if (con_inside_floating(con)) { diff --git a/testcases/t/271-for_window_tilingfloating.t b/testcases/t/271-for_window_tilingfloating.t index 240053d2..32ecaee7 100644 --- a/testcases/t/271-for_window_tilingfloating.t +++ b/testcases/t/271-for_window_tilingfloating.t @@ -56,7 +56,6 @@ is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floating', 'floating_auto' ], "mark cmd '[id=' . $A->{id} . '] floating enable'; cmd '[id=' . $B->{id} . '] floating disable'; -sync_with_i3; # Assignments run based on the _I3_FLOATING_WINDOW property. No sync is race-y. @nodes = @{get_ws($tmp)->{nodes}}; cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); @@ -76,7 +75,6 @@ is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'tiling_auto', 'floating', 'floating # Use 'mark' to clear old marks cmd '[id=' . $A->{id} . '] mark A, floating disable'; cmd '[id=' . $B->{id} . '] mark B, floating enable'; -sync_with_i3; @nodes = @{get_ws($tmp)->{nodes}}; cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); @@ -117,7 +115,6 @@ is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floating' ], "mark set for 'floatin cmd '[tiling_from="auto" con_mark="tiling"] mark --add tiling_auto'; cmd '[floating_from="auto" con_mark="floating"] mark --add floating_auto'; -sync_with_i3; @nodes = @{get_ws($tmp)->{nodes}}; cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace');