mirror of
https://github.com/i3/i3.git
synced 2025-12-05 01:10:19 +00:00
commands_parser.c: Make re-entrant (#6523)
Refactor to work with various for_window matching related issues
Depends on https://github.com/i3/i3/pull/6514
Related to https://github.com/i3/i3/issues/3588#issuecomment-544186208
Related to https://github.com/i3/i3/issues/4346
Related to https://github.com/i3/i3/issues/6037
Reverts commit 1cc2548027 / #6509
This commit is contained in:
@@ -12,6 +12,18 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <yajl/yajl_gen.h>
|
||||
#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;
|
||||
|
||||
/**
|
||||
|
||||
122
src/commands.c
122
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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user