+#undef I3__FILE__
+#define I3__FILE__ "commands.c"
/*
* vim:ts=4:sw=4:expandtab
*
#include <stdarg.h>
#include "all.h"
+#include "shmlog.h"
// Macros to make the YAJL API a bit easier to use.
-#define y(x, ...) yajl_gen_ ## x (cmd_output->json_gen, ##__VA_ARGS__)
-#define ystr(str) yajl_gen_string(cmd_output->json_gen, (unsigned char*)str, strlen(str))
-#define ysuccess(success) do { \
- y(map_open); \
- ystr("success"); \
- y(bool, success); \
- y(map_close); \
-} while (0)
+#define y(x, ...) (cmd_output->json_gen != NULL ? yajl_gen_##x(cmd_output->json_gen, ##__VA_ARGS__) : 0)
+#define ystr(str) (cmd_output->json_gen != NULL ? yajl_gen_string(cmd_output->json_gen, (unsigned char *)str, strlen(str)) : 0)
+#define ysuccess(success) \
+ do { \
+ if (cmd_output->json_gen != NULL) { \
+ y(map_open); \
+ ystr("success"); \
+ y(bool, success); \
+ y(map_close); \
+ } \
+ } while (0)
+#define yerror(message) \
+ do { \
+ if (cmd_output->json_gen != NULL) { \
+ y(map_open); \
+ ystr("success"); \
+ y(bool, false); \
+ ystr("error"); \
+ ystr(message); \
+ y(map_close); \
+ } \
+ } while (0)
/** When the command did not include match criteria (!), we use the currently
* focused container. Do not confuse this case with a command which included
* criteria but which did not match any windows. This macro has to be called in
* every command.
*/
-#define HANDLE_EMPTY_MATCH do { \
- if (match_is_empty(current_match)) { \
- owindow *ow = smalloc(sizeof(owindow)); \
- ow->con = focused; \
- TAILQ_INIT(&owindows); \
- TAILQ_INSERT_TAIL(&owindows, ow, owindows); \
- } \
-} while (0)
-
-static owindows_head owindows;
+#define HANDLE_EMPTY_MATCH \
+ do { \
+ if (match_is_empty(current_match)) { \
+ owindow *ow = smalloc(sizeof(owindow)); \
+ ow->con = focused; \
+ TAILQ_INIT(&owindows); \
+ TAILQ_INSERT_TAIL(&owindows, ow, owindows); \
+ } \
+ } while (0)
/*
* Returns true if a is definitely greater than b (using the given epsilon)
*
*/
static bool definitelyGreaterThan(float a, float b, float epsilon) {
- return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
+ return (a - b) > ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}
/*
static Output *get_output_from_string(Output *current_output, const char *output_str) {
Output *output;
- if (strcasecmp(output_str, "left") == 0) {
- output = get_output_next(D_LEFT, current_output);
- if (!output)
- output = get_output_most(D_RIGHT, current_output);
- } else if (strcasecmp(output_str, "right") == 0) {
- output = get_output_next(D_RIGHT, current_output);
- if (!output)
- output = get_output_most(D_LEFT, current_output);
- } else if (strcasecmp(output_str, "up") == 0) {
- output = get_output_next(D_UP, current_output);
- if (!output)
- output = get_output_most(D_DOWN, current_output);
- } else if (strcasecmp(output_str, "down") == 0) {
- output = get_output_next(D_DOWN, current_output);
- if (!output)
- output = get_output_most(D_UP, current_output);
- } else output = get_output_by_name(output_str);
+ if (strcasecmp(output_str, "left") == 0)
+ output = get_output_next_wrap(D_LEFT, current_output);
+ else if (strcasecmp(output_str, "right") == 0)
+ output = get_output_next_wrap(D_RIGHT, current_output);
+ else if (strcasecmp(output_str, "up") == 0)
+ output = get_output_next_wrap(D_UP, current_output);
+ else if (strcasecmp(output_str, "down") == 0)
+ output = get_output_next_wrap(D_DOWN, current_output);
+ else
+ output = get_output_by_name(output_str);
+
+ return output;
+}
+
+/*
+ * Returns the output containing the given container.
+ */
+static Output *get_output_of_con(Con *con) {
+ Con *output_con = con_get_output(con);
+ Output *output = get_output_by_name(output_con->name);
+ assert(output != NULL);
return output;
}
* and return true, signaling that no further workspace switching should occur in the calling function.
*
*/
-static bool maybe_back_and_forth(struct CommandResult *cmd_output, char *name) {
+static bool maybe_back_and_forth(struct CommandResultIR *cmd_output, char *name) {
Con *ws = con_get_workspace(focused);
/* If we switched to a different workspace, do nothing */
return true;
}
+/*
+ * Return the passed workspace unless it is the current one and auto back and
+ * forth is enabled, in which case the back_and_forth workspace is returned.
+ */
+static Con *maybe_auto_back_and_forth_workspace(Con *workspace) {
+ Con *current, *baf;
+
+ if (!config.workspace_auto_back_and_forth)
+ return workspace;
+
+ current = con_get_workspace(focused);
+
+ if (current == workspace) {
+ baf = workspace_back_and_forth_get();
+ if (baf != NULL) {
+ DLOG("Substituting workspace with back_and_forth, as it is focused.\n");
+ return baf;
+ }
+ }
+
+ return workspace;
+}
+
// This code is commented out because we might recycle it for popping up error
// messages on parser errors.
#if 0
* 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;
+
/*
* Initializes the specified 'Match' data structure and the initial state of
* commands.c for matching target windows of a command.
owindow *ow;
DLOG("Initializing criteria, current_match = %p\n", current_match);
+ match_free(current_match);
match_init(current_match);
while (!TAILQ_EMPTY(&owindows)) {
ow = TAILQ_FIRST(&owindows);
if (current_match->con_id == current->con) {
DLOG("matches container!\n");
TAILQ_INSERT_TAIL(&owindows, current, owindows);
+ } else {
+ DLOG("doesnt match\n");
+ free(current);
}
} else if (current_match->mark != NULL && current->con->mark != NULL &&
regex_matches(current_match->mark, current->con->mark)) {
DLOG("match by mark\n");
TAILQ_INSERT_TAIL(&owindows, current, owindows);
} else {
- if (current->con->window == NULL)
- continue;
- if (match_matches_window(current_match, current->con->window)) {
+ if (current->con->window && match_matches_window(current_match, current->con->window)) {
DLOG("matches window!\n");
TAILQ_INSERT_TAIL(&owindows, current, owindows);
} else {
}
if (strcmp(ctype, "window_role") == 0) {
- current_match->role = regex_new(cvalue);
+ current_match->window_role = regex_new(cvalue);
return;
}
(end && *end != '\0')) {
ELOG("Could not parse con id \"%s\"\n", cvalue);
} else {
- current_match->con_id = (Con*)parsed;
- printf("id as int = %p\n", current_match->con_id);
+ current_match->con_id = (Con *)parsed;
+ DLOG("id as int = %p\n", current_match->con_id);
}
return;
}
ELOG("Could not parse window id \"%s\"\n", cvalue);
} else {
current_match->id = parsed;
- printf("window id as int = %d\n", current_match->id);
+ DLOG("window id as int = %d\n", current_match->id);
}
return;
}
/*
* Implementation of 'move [window|container] [to] workspace
- * next|prev|next_on_output|prev_on_output'.
+ * next|prev|next_on_output|prev_on_output|current'.
*
*/
void cmd_move_con_to_workspace(I3_CMD, char *which) {
DLOG("which=%s\n", which);
+ /* We have nothing to move:
+ * when criteria was specified but didn't match any window or
+ * when criteria wasn't specified and we don't have any window focused. */
+ if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
+ (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
+ !con_has_children(focused))) {
+ ysuccess(false);
+ return;
+ }
+
HANDLE_EMPTY_MATCH;
/* get the workspace */
ws = workspace_next_on_output();
else if (strcmp(which, "prev_on_output") == 0)
ws = workspace_prev_on_output();
+ else if (strcmp(which, "current") == 0)
+ ws = con_get_workspace(focused);
else {
ELOG("BUG: called with which=%s\n", which);
ysuccess(false);
ysuccess(true);
}
+/**
+ * Implementation of 'move [window|container] [to] workspace back_and_forth'.
+ *
+ */
+void cmd_move_con_to_workspace_back_and_forth(I3_CMD) {
+ owindow *current;
+ Con *ws;
+
+ ws = workspace_back_and_forth_get();
+
+ if (ws == NULL) {
+ yerror("No workspace was previously active.");
+ return;
+ }
+
+ HANDLE_EMPTY_MATCH;
+
+ TAILQ_FOREACH(current, &owindows, owindows) {
+ DLOG("matching: %p / %s\n", current->con, current->con->name);
+ con_move_to_workspace(current->con, ws, true, false);
+ }
+
+ cmd_output->needs_tree_render = true;
+ // XXX: default reply for now, make this a better reply
+ ysuccess(true);
+}
+
/*
* Implementation of 'move [window|container] [to] workspace <name>'.
*
*/
void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
- if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) {
- LOG("You cannot switch to the i3 internal workspaces.\n");
+ if (strncasecmp(name, "__", strlen("__")) == 0) {
+ LOG("You cannot move containers to i3-internal workspaces (\"%s\").\n", name);
ysuccess(false);
return;
}
owindow *current;
- /* Error out early to not create a non-existing workspace (in
- * workspace_get()) if we are not actually able to move anything. */
- if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
+ /* We have nothing to move:
+ * when criteria was specified but didn't match any window or
+ * when criteria wasn't specified and we don't have any window focused. */
+ if (!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) {
+ ELOG("No windows match your criteria, cannot move.\n");
+ ysuccess(false);
+ return;
+ } else if (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
+ !con_has_children(focused)) {
ysuccess(false);
return;
}
/* get the workspace */
Con *ws = workspace_get(name, NULL);
+ ws = maybe_auto_back_and_forth_workspace(ws);
+
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH(current, &owindows, owindows) {
}
/*
- * Implementation of 'move [window|container] [to] workspace number <number>'.
+ * Implementation of 'move [window|container] [to] workspace number <name>'.
*
*/
void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
owindow *current;
- /* Error out early to not create a non-existing workspace (in
- * workspace_get()) if we are not actually able to move anything. */
- if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
+ /* We have nothing to move:
+ * when criteria was specified but didn't match any window or
+ * when criteria wasn't specified and we don't have any window focused. */
+ if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
+ (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
+ !con_has_children(focused))) {
ysuccess(false);
return;
}
- LOG("should move window to workspace with number %d\n", which);
+ LOG("should move window to workspace %s\n", which);
/* get the workspace */
Con *output, *workspace = NULL;
- char *endptr = NULL;
- long parsed_num = strtol(which, &endptr, 10);
- if (parsed_num == LONG_MIN ||
- parsed_num == LONG_MAX ||
- parsed_num < 0 ||
- *endptr != '\0') {
- LOG("Could not parse \"%s\" as a number.\n", which);
- y(map_open);
- ystr("success");
- y(bool, false);
- ystr("error");
+ long parsed_num = ws_name_to_number(which);
+
+ if (parsed_num == -1) {
+ LOG("Could not parse initial part of \"%s\" as a number.\n", which);
// TODO: better error message
- ystr("Could not parse number");
- y(map_close);
+ yerror("Could not parse number");
return;
}
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(output),
- child->num == parsed_num);
+ GREP_FIRST(workspace, output_get_content(output),
+ child->num == parsed_num);
if (!workspace) {
- y(map_open);
- ystr("success");
- y(bool, false);
- ystr("error");
- // TODO: better error message
- ystr("No such workspace");
- y(map_close);
- return;
+ workspace = workspace_get(which, NULL);
}
+ workspace = maybe_auto_back_and_forth_workspace(workspace);
+
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH(current, &owindows, owindows) {
static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) {
LOG("floating resize\n");
+ Rect old_rect = floating_con->rect;
+ Con *focused_con = con_descend_focused(floating_con);
+
+ /* ensure that resize will take place even if pixel increment is smaller than
+ * height increment or width increment.
+ * fixes #1011 */
+ if (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0 ||
+ strcmp(direction, "height") == 0) {
+ if (px < 0)
+ px = (-px < focused_con->height_increment) ? -focused_con->height_increment : px;
+ else
+ px = (px < focused_con->height_increment) ? focused_con->height_increment : px;
+ } else if (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0) {
+ if (px < 0)
+ px = (-px < focused_con->width_increment) ? -focused_con->width_increment : px;
+ else
+ px = (px < focused_con->width_increment) ? focused_con->width_increment : px;
+ }
+
if (strcmp(direction, "up") == 0) {
- floating_con->rect.y -= px;
floating_con->rect.height += px;
- } else if (strcmp(direction, "down") == 0) {
+ } else if (strcmp(direction, "down") == 0 || strcmp(direction, "height") == 0) {
floating_con->rect.height += px;
} else if (strcmp(direction, "left") == 0) {
- floating_con->rect.x -= px;
floating_con->rect.width += px;
} else {
floating_con->rect.width += px;
}
+
+ floating_check_size(floating_con);
+
+ /* Did we actually resize anything or did the size constraints prevent us?
+ * If we could not resize, exit now to not move the window. */
+ if (memcmp(&old_rect, &(floating_con->rect), sizeof(Rect)) == 0)
+ return;
+
+ if (strcmp(direction, "up") == 0) {
+ floating_con->rect.y -= (floating_con->rect.height - old_rect.height);
+ } else if (strcmp(direction, "left") == 0) {
+ floating_con->rect.x -= (floating_con->rect.width - old_rect.width);
+ }
+
+ /* If this is a scratchpad window, don't auto center it from now on. */
+ if (floating_con->scratchpad_state == SCRATCHPAD_FRESH)
+ floating_con->scratchpad_state = SCRATCHPAD_CHANGED;
}
-static void cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int ppt) {
+static bool cmd_resize_tiling_direction(I3_CMD, Con *current, char *way, char *direction, int ppt) {
LOG("tiling resize\n");
- /* get the appropriate current container (skip stacked/tabbed cons) */
- Con *current = focused;
- while (current->parent->layout == L_STACKED ||
- current->parent->layout == L_TABBED)
- current = current->parent;
-
- /* Then further go up until we find one with the matching orientation. */
- orientation_t search_orientation =
- (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0 ? HORIZ : VERT);
+ Con *second = NULL;
+ Con *first = current;
+ direction_t search_direction;
+ if (!strcmp(direction, "left"))
+ search_direction = D_LEFT;
+ else if (!strcmp(direction, "right"))
+ search_direction = D_RIGHT;
+ else if (!strcmp(direction, "up"))
+ search_direction = D_UP;
+ else
+ search_direction = D_DOWN;
- while (current->type != CT_WORKSPACE &&
- current->type != CT_FLOATING_CON &&
- current->parent->orientation != search_orientation)
- current = current->parent;
+ bool res = resize_find_tiling_participants(&first, &second, search_direction);
+ if (!res) {
+ LOG("No second container in this direction found.\n");
+ ysuccess(false);
+ return false;
+ }
/* get the default percentage */
- int children = con_num_children(current->parent);
- Con *other;
+ int children = con_num_children(first->parent);
LOG("ins. %d children\n", children);
double percentage = 1.0 / children;
LOG("default percentage = %f\n", percentage);
- orientation_t orientation = current->parent->orientation;
-
- if ((orientation == HORIZ &&
- (strcmp(direction, "up") == 0 || strcmp(direction, "down") == 0)) ||
- (orientation == VERT &&
- (strcmp(direction, "left") == 0 || strcmp(direction, "right") == 0))) {
- LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
- (orientation == HORIZ ? "horizontal" : "vertical"));
- ysuccess(false);
- return;
- }
-
- if (strcmp(direction, "up") == 0 || strcmp(direction, "left") == 0) {
- other = TAILQ_PREV(current, nodes_head, nodes);
- } else {
- other = TAILQ_NEXT(current, nodes);
- }
- if (other == TAILQ_END(workspaces)) {
- LOG("No other container in this direction found, cannot resize.\n");
- ysuccess(false);
- return;
- }
- LOG("other->percent = %f\n", other->percent);
- LOG("current->percent before = %f\n", current->percent);
- if (current->percent == 0.0)
- current->percent = percentage;
- if (other->percent == 0.0)
- other->percent = percentage;
- double new_current_percent = current->percent + ((double)ppt / 100.0);
- double new_other_percent = other->percent - ((double)ppt / 100.0);
- LOG("new_current_percent = %f\n", new_current_percent);
- LOG("new_other_percent = %f\n", new_other_percent);
+ /* resize */
+ LOG("second->percent = %f\n", second->percent);
+ LOG("first->percent before = %f\n", first->percent);
+ if (first->percent == 0.0)
+ first->percent = percentage;
+ if (second->percent == 0.0)
+ second->percent = percentage;
+ double new_first_percent = first->percent + ((double)ppt / 100.0);
+ double new_second_percent = second->percent - ((double)ppt / 100.0);
+ LOG("new_first_percent = %f\n", new_first_percent);
+ LOG("new_second_percent = %f\n", new_second_percent);
/* Ensure that the new percentages are positive and greater than
* 0.05 to have a reasonable minimum size. */
- if (definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON) &&
- definitelyGreaterThan(new_other_percent, 0.05, DBL_EPSILON)) {
- current->percent += ((double)ppt / 100.0);
- other->percent -= ((double)ppt / 100.0);
- LOG("current->percent after = %f\n", current->percent);
- LOG("other->percent after = %f\n", other->percent);
+ if (definitelyGreaterThan(new_first_percent, 0.05, DBL_EPSILON) &&
+ definitelyGreaterThan(new_second_percent, 0.05, DBL_EPSILON)) {
+ first->percent += ((double)ppt / 100.0);
+ second->percent -= ((double)ppt / 100.0);
+ LOG("first->percent after = %f\n", first->percent);
+ LOG("second->percent after = %f\n", second->percent);
} else {
LOG("Not resizing, already at minimum size\n");
}
+
+ return true;
}
-static void cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, int ppt) {
+static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char *direction, int ppt) {
LOG("width/height resize\n");
/* get the appropriate current container (skip stacked/tabbed cons) */
- Con *current = focused;
while (current->parent->layout == L_STACKED ||
current->parent->layout == L_TABBED)
current = current->parent;
while (current->type != CT_WORKSPACE &&
current->type != CT_FLOATING_CON &&
- current->parent->orientation != search_orientation)
+ con_orientation(current->parent) != search_orientation)
current = current->parent;
/* get the default percentage */
double percentage = 1.0 / children;
LOG("default percentage = %f\n", percentage);
- orientation_t orientation = current->parent->orientation;
+ orientation_t orientation = con_orientation(current->parent);
if ((orientation == HORIZ &&
strcmp(direction, "height") == 0) ||
LOG("You cannot resize in that direction. Your focus is in a %s split container currently.\n",
(orientation == HORIZ ? "horizontal" : "vertical"));
ysuccess(false);
- return;
+ return false;
}
if (children == 1) {
LOG("This is the only container, cannot resize.\n");
ysuccess(false);
- return;
+ return false;
}
/* Ensure all the other children have a percentage set. */
if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) {
LOG("Not resizing, already at minimum size (child %p would end up with a size of %.f\n", child, child->percent - subtract_percent);
ysuccess(false);
- return;
+ return false;
}
}
if (!definitelyGreaterThan(new_current_percent, 0.05, DBL_EPSILON)) {
LOG("Not resizing, already at minimum size\n");
ysuccess(false);
- return;
+ return false;
}
current->percent += ((double)ppt / 100.0);
child->percent -= subtract_percent;
LOG("child->percent after (%p) = %f\n", child, child->percent);
}
+
+ return true;
}
/*
ppt *= -1;
}
- Con *floating_con;
- if ((floating_con = con_inside_floating(focused))) {
- cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px);
- } else {
- if (strcmp(direction, "width") == 0 ||
- strcmp(direction, "height") == 0)
- cmd_resize_tiling_width_height(current_match, cmd_output, way, direction, ppt);
- else cmd_resize_tiling_direction(current_match, cmd_output, way, direction, ppt);
+ HANDLE_EMPTY_MATCH;
+
+ owindow *current;
+ 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);
+ continue;
+ }
+
+ Con *floating_con;
+ if ((floating_con = con_inside_floating(current->con))) {
+ cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px);
+ } else {
+ if (strcmp(direction, "width") == 0 ||
+ strcmp(direction, "height") == 0) {
+ if (!cmd_resize_tiling_width_height(current_match, cmd_output, current->con, way, direction, ppt))
+ return;
+ } else {
+ if (!cmd_resize_tiling_direction(current_match, cmd_output, current->con, way, direction, ppt))
+ return;
+ }
+ }
}
cmd_output->needs_tree_render = true;
}
/*
- * Implementation of 'border normal|none|1pixel|toggle'.
+ * Implementation of 'border normal|none|1pixel|toggle|pixel'.
*
*/
-void cmd_border(I3_CMD, char *border_style_str) {
- DLOG("border style should be changed to %s\n", border_style_str);
+void cmd_border(I3_CMD, char *border_style_str, char *border_width) {
+ DLOG("border style should be changed to %s with border width %s\n", border_style_str, border_width);
owindow *current;
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
int border_style = current->con->border_style;
+ char *end;
+ int tmp_border_width = -1;
+ tmp_border_width = strtol(border_width, &end, 10);
+ if (end == border_width) {
+ /* no valid digits found */
+ tmp_border_width = -1;
+ }
if (strcmp(border_style_str, "toggle") == 0) {
border_style++;
border_style %= 3;
+ if (border_style == BS_NORMAL)
+ tmp_border_width = 2;
+ else if (border_style == BS_NONE)
+ tmp_border_width = 0;
+ else if (border_style == BS_PIXEL)
+ tmp_border_width = 1;
} else {
if (strcmp(border_style_str, "normal") == 0)
border_style = BS_NORMAL;
- else if (strcmp(border_style_str, "none") == 0)
+ else if (strcmp(border_style_str, "pixel") == 0)
+ border_style = BS_PIXEL;
+ else if (strcmp(border_style_str, "1pixel") == 0) {
+ border_style = BS_PIXEL;
+ tmp_border_width = 1;
+ } else if (strcmp(border_style_str, "none") == 0)
border_style = BS_NONE;
- else if (strcmp(border_style_str, "1pixel") == 0)
- border_style = BS_1PIXEL;
else {
ELOG("BUG: called with border_style=%s\n", border_style_str);
ysuccess(false);
return;
}
}
- con_set_border_style(current->con, border_style);
+ con_set_border_style(current->con, border_style, tmp_border_width);
}
cmd_output->needs_tree_render = true;
*/
void cmd_append_layout(I3_CMD, char *path) {
LOG("Appending layout \"%s\"\n", path);
- tree_append_json(path);
+ Con *parent = focused;
+ /* We need to append the layout to a split container, since a leaf
+ * container must not have any children (by definition).
+ * Note that we explicitly check for workspaces, since they are okay for
+ * this purpose, but con_accepts_window() returns false for workspaces. */
+ while (parent->type != CT_WORKSPACE && !con_accepts_window(parent))
+ parent = parent->parent;
+ DLOG("Appending to parent=%p instead of focused=%p\n",
+ parent, focused);
+ char *errormsg = NULL;
+ tree_append_json(parent, path, &errormsg);
+ if (errormsg != NULL) {
+ yerror(errormsg);
+ free(errormsg);
+ /* Note that we continue executing since tree_append_json() has
+ * side-effects — user-provided layouts can be partly valid, partly
+ * invalid, leading to half of the placeholder containers being
+ * created. */
+ } else {
+ ysuccess(true);
+ }
+
+ // XXX: This is a bit of a kludge. Theoretically, render_con(parent,
+ // false); should be enough, but when sending 'workspace 4; append_layout
+ // /tmp/foo.json', the needs_tree_render == true of the workspace command
+ // is not executed yet and will be batched with append_layout’s
+ // needs_tree_render after the parser finished. We should check if that is
+ // necessary at all.
+ render_con(croot, false);
+
+ restore_open_placeholder_windows(parent);
cmd_output->needs_tree_render = true;
- // XXX: default reply for now, make this a better reply
- ysuccess(true);
}
/*
}
/*
- * Implementation of 'workspace number <number>'
+ * Implementation of 'workspace number <name>'
*
*/
void cmd_workspace_number(I3_CMD, char *which) {
Con *output, *workspace = NULL;
- char *endptr = NULL;
- long parsed_num = strtol(which, &endptr, 10);
- if (parsed_num == LONG_MIN ||
- parsed_num == LONG_MAX ||
- parsed_num < 0 ||
- *endptr != '\0') {
- LOG("Could not parse \"%s\" as a number.\n", which);
- y(map_open);
- ystr("success");
- y(bool, false);
- ystr("error");
- // TODO: better error message
- ystr("Could not parse number");
- y(map_close);
+ long parsed_num = ws_name_to_number(which);
+ if (parsed_num == -1) {
+ LOG("Could not parse initial part of \"%s\" as a number.\n", which);
+ // TODO: better error message
+ yerror("Could not parse number");
return;
}
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(output),
- child->num == parsed_num);
+ GREP_FIRST(workspace, output_get_content(output),
+ child->num == parsed_num);
if (!workspace) {
- LOG("There is no workspace with number %d, creating a new one.\n", parsed_num);
+ LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
ysuccess(true);
- /* terminate the which string after the endposition of the number */
- *endptr = '\0';
- if (maybe_back_and_forth(cmd_output, which))
- return;
workspace_show_by_name(which);
cmd_output->needs_tree_render = true;
return;
}
- if (maybe_back_and_forth(cmd_output, which))
+ if (maybe_back_and_forth(cmd_output, workspace->name))
return;
workspace_show(workspace);
*
*/
void cmd_workspace_name(I3_CMD, char *name) {
- if (strncasecmp(name, "__i3_", strlen("__i3_")) == 0) {
- LOG("You cannot switch to the i3 internal workspaces.\n");
+ if (strncasecmp(name, "__", strlen("__")) == 0) {
+ LOG("You cannot switch to the i3-internal workspaces (\"%s\").\n", name);
ysuccess(false);
return;
}
DLOG("should switch to workspace %s\n", name);
if (maybe_back_and_forth(cmd_output, name))
- return;
+ return;
workspace_show_by_name(name);
cmd_output->needs_tree_render = true;
ysuccess(true);
}
+/*
+ * Implementation of 'unmark [mark]'
+ *
+ */
+void cmd_unmark(I3_CMD, char *mark) {
+ if (mark == NULL) {
+ Con *con;
+ TAILQ_FOREACH(con, &all_cons, all_cons) {
+ FREE(con->mark);
+ }
+ DLOG("removed all window marks");
+ } else {
+ Con *con;
+ TAILQ_FOREACH(con, &all_cons, all_cons) {
+ if (con->mark && strcmp(con->mark, mark) == 0)
+ FREE(con->mark);
+ }
+ DLOG("removed window mark %s\n", mark);
+ }
+
+ cmd_output->needs_tree_render = true;
+ // XXX: default reply for now, make this a better reply
+ ysuccess(true);
+}
+
/*
* Implementation of 'mode <string>'.
*
// TODO: fix the handling of criteria
TAILQ_FOREACH(current, &owindows, owindows)
- current_output = get_output_containing(current->con->rect.x, current->con->rect.y);
+ current_output = get_output_of_con(current->con);
assert(current_output != NULL);
// TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
if (strcasecmp(name, "up") == 0)
- output = get_output_next(D_UP, current_output);
+ output = get_output_next_wrap(D_UP, current_output);
else if (strcasecmp(name, "down") == 0)
- output = get_output_next(D_DOWN, current_output);
+ output = get_output_next_wrap(D_DOWN, current_output);
else if (strcasecmp(name, "left") == 0)
- output = get_output_next(D_LEFT, current_output);
+ output = get_output_next_wrap(D_LEFT, current_output);
else if (strcasecmp(name, "right") == 0)
- output = get_output_next(D_RIGHT, current_output);
+ output = get_output_next_wrap(D_RIGHT, current_output);
else
output = get_output_by_name(name);
owindow *current;
TAILQ_FOREACH(current, &owindows, owindows) {
- Output *current_output = get_output_containing(current->con->rect.x,
- current->con->rect.y);
+ Output *current_output = get_output_of_con(current->con);
+ if (!current_output) {
+ ELOG("Cannot get current output. This is a bug in i3.\n");
+ ysuccess(false);
+ return;
+ }
Output *output = get_output_from_string(current_output, name);
if (!output) {
- LOG("No such output\n");
+ ELOG("Could not get output from string \"%s\"\n", name);
ysuccess(false);
return;
}
Con *content = output_get_content(output->con);
LOG("got output %p with content %p\n", output, content);
+ Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head));
+ LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
+
Con *ws = con_get_workspace(current->con);
LOG("should move workspace %p / %s\n", ws, ws->name);
+ bool workspace_was_visible = workspace_is_visible(ws);
if (con_num_children(ws->parent) == 1) {
LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
/* check if this workspace is already attached to the tree */
Con *workspace = NULL, *out;
TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(out),
- !strcasecmp(child->name, assignment->name));
+ GREP_FIRST(workspace, output_get_content(out),
+ !strcasecmp(child->name, assignment->name));
if (workspace != NULL)
continue;
/* notify the IPC listeners */
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
}
+ DLOG("Detaching\n");
/* detach from the old output and attach to the new output */
- bool workspace_was_visible = workspace_is_visible(ws);
Con *old_content = ws->parent;
con_detach(ws);
if (workspace_was_visible) {
/* fix the coordinates of the floating containers */
Con *floating_con;
TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
- floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
+ floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}");
if (workspace_was_visible) {
/* Focus the moved workspace on the destination output. */
workspace_show(ws);
}
+
+ /* NB: We cannot simply work with previously_visible_ws since it might
+ * have been cleaned up by workspace_show() already, depending on the
+ * focus order/number of other workspaces on the output.
+ * Instead, we loop through the available workspaces and only work with
+ * previously_visible_ws if we still find it. */
+ TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
+ if (ws != previously_visible_ws)
+ continue;
+
+ /* Call the on_remove_child callback of the workspace which previously
+ * was visible on the destination output. Since it is no longer
+ * visible, it might need to get cleaned up. */
+ CALL(previously_visible_ws, on_remove_child);
+ break;
+ }
}
cmd_output->needs_tree_render = true;
*
*/
void cmd_split(I3_CMD, char *direction) {
+ owindow *current;
/* TODO: use matches */
LOG("splitting in direction %c\n", direction[0]);
- tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
+ if (match_is_empty(current_match))
+ tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
+ else {
+ TAILQ_FOREACH(current, &owindows, owindows) {
+ DLOG("matching: %p / %s\n", current->con, current->con->name);
+ tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
+ }
+ }
cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply
}
/*
- * Implementaiton of 'kill [window|client]'.
+ * Implementation of 'kill [window|client]'.
*
*/
void cmd_kill(I3_CMD, char *kill_mode_str) {
*
*/
void cmd_focus_direction(I3_CMD, char *direction) {
- if (focused &&
- focused->type != CT_WORKSPACE &&
- focused->fullscreen_mode != CF_NONE) {
- LOG("Cannot change focus while in fullscreen mode.\n");
- ysuccess(false);
- return;
- }
-
DLOG("direction = *%s*\n", direction);
if (strcmp(direction, "left") == 0)
*
*/
void cmd_focus_window_mode(I3_CMD, char *window_mode) {
- if (focused &&
- focused->type != CT_WORKSPACE &&
- focused->fullscreen_mode != CF_NONE) {
- LOG("Cannot change focus while in fullscreen mode.\n");
- ysuccess(false);
- return;
- }
-
DLOG("window_mode = %s\n", window_mode);
Con *ws = con_get_workspace(focused);
current = TAILQ_FIRST(&(ws->focus_head));
if (current != NULL && current->type == CT_FLOATING_CON)
window_mode = "tiling";
- else window_mode = "floating";
+ else
+ window_mode = "floating";
}
TAILQ_FOREACH(current, &(ws->focus_head), focused) {
if ((strcmp(window_mode, "floating") == 0 && current->type != CT_FLOATING_CON) ||
if (con_fullscreen_permits_focusing(focused->parent))
success = level_up();
else
- LOG("Currently in fullscreen, not going up\n");
+ ELOG("'focus parent': Currently in fullscreen, not going up\n");
}
}
/* Focusing a child should always be allowed. */
- else success = level_down();
+ else
+ success = level_down();
cmd_output->needs_tree_render = success;
// XXX: default reply for now, make this a better reply
ELOG("You have to specify which window/container should be focused.\n");
ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n");
- y(map_open);
- ystr("success");
- y(bool, false);
- ystr("error");
- ystr("You have to specify which window/container should be focused");
- y(map_close);
+ yerror("You have to specify which window/container should be focused");
return;
}
+ Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
int count = 0;
owindow *current;
TAILQ_FOREACH(current, &owindows, owindows) {
return;
}
+ /* In case this is a scratchpad window, call scratchpad_show(). */
+ if (ws == __i3_scratch) {
+ scratchpad_show(current->con);
+ count++;
+ /* While for the normal focus case we can change focus multiple
+ * times and only a single window ends up focused, we could show
+ * multiple scratchpad windows. So, rather break here. */
+ break;
+ }
+
/* If the container is not on the current workspace,
* workspace_show() will switch to a different workspace and (if
* enabled) trigger a mouse pointer warp to the currently focused
if (count > 1)
LOG("WARNING: Your criteria for the focus command matches %d containers, "
- "while only exactly one container can be focused at a time.\n", count);
+ "while only exactly one container can be focused at a time.\n",
+ count);
cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply
HANDLE_EMPTY_MATCH;
TAILQ_FOREACH(current, &owindows, owindows) {
- printf("matching: %p / %s\n", current->con, current->con->name);
+ DLOG("matching: %p / %s\n", current->con, current->con->name);
con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT));
}
// TODO: We could either handle this in the parser itself as a separate token (and make the stack typed) or we need a better way to convert a string to a number with error checking
int px = atoi(move_px);
- /* TODO: make 'move' work with criteria. */
- DLOG("moving in direction %s, px %s\n", direction, move_px);
- if (con_is_floating(focused)) {
- DLOG("floating move with %d pixels\n", px);
- Rect newrect = focused->parent->rect;
- if (strcmp(direction, "left") == 0) {
- newrect.x -= px;
- } else if (strcmp(direction, "right") == 0) {
- newrect.x += px;
- } else if (strcmp(direction, "up") == 0) {
- newrect.y -= px;
- } else if (strcmp(direction, "down") == 0) {
- newrect.y += px;
+ owindow *current;
+ HANDLE_EMPTY_MATCH;
+
+ Con *initially_focused = focused;
+
+ TAILQ_FOREACH (current, &owindows, owindows) {
+ DLOG("moving in direction %s, px %s\n", direction, move_px);
+ if (con_is_floating(current->con)) {
+ DLOG("floating move with %d pixels\n", px);
+ Rect newrect = current->con->parent->rect;
+ if (strcmp(direction, "left") == 0) {
+ newrect.x -= px;
+ } else if (strcmp(direction, "right") == 0) {
+ newrect.x += px;
+ } else if (strcmp(direction, "up") == 0) {
+ newrect.y -= px;
+ } else if (strcmp(direction, "down") == 0) {
+ newrect.y += px;
+ }
+ floating_reposition(current->con->parent, newrect);
+ } else {
+ tree_move(current->con, (strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN))));
+ cmd_output->needs_tree_render = true;
}
- floating_reposition(focused->parent, newrect);
- } else {
- tree_move((strcmp(direction, "right") == 0 ? D_RIGHT :
- (strcmp(direction, "left") == 0 ? D_LEFT :
- (strcmp(direction, "up") == 0 ? D_UP :
- D_DOWN))));
- cmd_output->needs_tree_render = true;
}
+ /* the move command should not disturb focus */
+ if (focused != initially_focused)
+ con_focus(initially_focused);
+
// XXX: default reply for now, make this a better reply
ysuccess(true);
}
/*
- * Implementation of 'layout default|stacked|stacking|tabbed'.
+ * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
*
*/
void cmd_layout(I3_CMD, char *layout_str) {
if (strcmp(layout_str, "stacking") == 0)
layout_str = "stacked";
- DLOG("changing layout to %s\n", layout_str);
owindow *current;
- int layout = (strcmp(layout_str, "default") == 0 ? L_DEFAULT :
- (strcmp(layout_str, "stacked") == 0 ? L_STACKED :
- L_TABBED));
+ layout_t layout;
+ /* default is a special case which will be handled in con_set_layout(). */
+ if (strcmp(layout_str, "default") == 0)
+ layout = L_DEFAULT;
+ else if (strcmp(layout_str, "stacked") == 0)
+ layout = L_STACKED;
+ else if (strcmp(layout_str, "tabbed") == 0)
+ layout = L_TABBED;
+ else if (strcmp(layout_str, "splitv") == 0)
+ layout = L_SPLITV;
+ else if (strcmp(layout_str, "splith") == 0)
+ layout = L_SPLITH;
+ else {
+ ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str);
+ return;
+ }
+
+ DLOG("changing layout to %s (%d)\n", layout_str, layout);
/* check if the match is empty, not if the result is empty */
if (match_is_empty(current_match))
- con_set_layout(focused->parent, layout);
+ con_set_layout(focused, layout);
else {
TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
}
/*
- * Implementaiton of 'exit'.
+ * Implementation of 'layout toggle [all|split]'.
+ *
+ */
+void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
+ owindow *current;
+
+ if (toggle_mode == NULL)
+ toggle_mode = "default";
+
+ DLOG("toggling layout (mode = %s)\n", toggle_mode);
+
+ /* check if the match is empty, not if the result is empty */
+ if (match_is_empty(current_match))
+ con_toggle_layout(focused, toggle_mode);
+ else {
+ TAILQ_FOREACH(current, &owindows, owindows) {
+ DLOG("matching: %p / %s\n", current->con, current->con->name);
+ con_toggle_layout(current->con, toggle_mode);
+ }
+ }
+
+ cmd_output->needs_tree_render = true;
+ // XXX: default reply for now, make this a better reply
+ ysuccess(true);
+}
+
+/*
+ * Implementation of 'exit'.
*
*/
void cmd_exit(I3_CMD) {
LOG("Exiting due to user command.\n");
+ ipc_shutdown();
+ unlink(config.ipc_socket_path);
+ xcb_disconnect(conn);
exit(0);
/* unreached */
}
/*
- * Implementaiton of 'reload'.
+ * Implementation of 'reload'.
*
*/
void cmd_reload(I3_CMD) {
LOG("reloading\n");
- kill_configerror_nagbar(false);
+ kill_nagbar(&config_error_nagbar_pid, false);
+ kill_nagbar(&command_error_nagbar_pid, false);
load_configuration(conn, NULL, true);
x_set_i3_atoms();
/* Send an IPC event just in case the ws names have changed */
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
+ /* Send an update event for the barconfig just in case it has changed */
+ update_barconfig();
// XXX: default reply for now, make this a better reply
ysuccess(true);
}
/*
- * Implementaiton of 'restart'.
+ * Implementation of 'restart'.
*
*/
void cmd_restart(I3_CMD) {
LOG("restarting i3\n");
+ ipc_shutdown();
+ /* We need to call this manually since atexit handlers don’t get called
+ * when exec()ing */
+ purge_zerobyte_logfile();
+ /* The unlink call is intentionally after the purge_zerobyte_logfile() so
+ * that the latter does not remove the directory yet. We need to store the
+ * restart layout state in there. */
+ unlink(config.ipc_socket_path);
i3_restart(false);
// XXX: default reply for now, make this a better reply
}
/*
- * Implementaiton of 'open'.
+ * Implementation of 'open'.
*
*/
void cmd_open(I3_CMD) {
LOG("opening new container\n");
Con *con = tree_open_con(NULL, NULL);
+ con->layout = L_SPLITH;
con_focus(con);
y(map_open);
Output *output;
TAILQ_FOREACH(current, &owindows, owindows)
- current_output = get_output_containing(current->con->rect.x, current->con->rect.y);
+ current_output = get_output_of_con(current->con);
assert(current_output != NULL);
output = get_output_from_string(current_output, name);
*
*/
void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) {
-
int x = atoi(cx);
int y = atoi(cy);
if (!con_is_floating(focused)) {
ELOG("Cannot change position. The window/container is not floating\n");
- y(map_open);
- ystr("success");
- y(bool, false);
- ystr("error");
- ystr("Cannot change position. The window/container is not floating.");
- y(map_close);
+ yerror("Cannot change position. The window/container is not floating.");
return;
}
*
*/
void cmd_move_window_to_center(I3_CMD, char *method) {
-
if (!con_is_floating(focused)) {
ELOG("Cannot change position. The window/container is not floating\n");
- y(map_open);
- ystr("success");
- y(bool, false);
- ystr("error");
- ystr("Cannot change position. The window/container is not floating.");
- y(map_close);
+ yerror("Cannot change position. The window/container is not floating.");
+ return;
}
if (strcmp(method, "absolute") == 0) {
Rect *rect = &focused->parent->rect;
DLOG("moving to absolute center\n");
- rect->x = croot->rect.width/2 - rect->width/2;
- rect->y = croot->rect.height/2 - rect->height/2;
+ rect->x = croot->rect.width / 2 - rect->width / 2;
+ rect->y = croot->rect.height / 2 - rect->height / 2;
floating_maybe_reassign_ws(focused->parent);
cmd_output->needs_tree_render = true;
Rect newrect = focused->parent->rect;
DLOG("moving to center\n");
- newrect.x = wsrect->width/2 - newrect.width/2;
- newrect.y = wsrect->height/2 - newrect.height/2;
+ newrect.x = wsrect->width / 2 - newrect.width / 2;
+ newrect.y = wsrect->height / 2 - newrect.height / 2;
floating_reposition(focused->parent, newrect);
}
}
/*
- * Implementation of 'rename workspace <name> to <name>'
+ * Implementation of 'rename workspace [<name>] to <name>'
*
*/
void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
- LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
+ if (strncasecmp(new_name, "__", strlen("__")) == 0) {
+ LOG("Cannot rename workspace to \"%s\": names starting with __ are i3-internal.", new_name);
+ ysuccess(false);
+ return;
+ }
+ if (old_name) {
+ LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
+ } else {
+ LOG("Renaming current workspace to \"%s\"\n", new_name);
+ }
Con *output, *workspace = NULL;
- TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+ if (old_name) {
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
GREP_FIRST(workspace, output_get_content(output),
- !strcasecmp(child->name, old_name));
+ !strcasecmp(child->name, old_name));
+ } else {
+ workspace = con_get_workspace(focused);
+ }
if (!workspace) {
// TODO: we should include the old workspace name here and use yajl for
// generating the reply.
- y(map_open);
- ystr("success");
- y(bool, false);
- ystr("error");
// TODO: better error message
- ystr("Old workspace not found");
- y(map_close);
+ yerror("Old workspace not found");
return;
}
Con *check_dest = NULL;
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
- GREP_FIRST(check_dest, output_get_content(output),
- !strcasecmp(child->name, new_name));
+ GREP_FIRST(check_dest, output_get_content(output),
+ !strcasecmp(child->name, new_name));
if (check_dest != NULL) {
// TODO: we should include the new workspace name here and use yajl for
// generating the reply.
- y(map_open);
- ystr("success");
- y(bool, false);
- ystr("error");
// TODO: better error message
- ystr("New workspace already exists");
- y(map_close);
+ yerror("New workspace already exists");
return;
}
/* Change the name and try to parse it as a number. */
FREE(workspace->name);
workspace->name = sstrdup(new_name);
- char *endptr = NULL;
- long parsed_num = strtol(new_name, &endptr, 10);
- if (parsed_num == LONG_MIN ||
- parsed_num == LONG_MAX ||
- parsed_num < 0 ||
- endptr == new_name)
- workspace->num = -1;
- else workspace->num = parsed_num;
+
+ workspace->num = ws_name_to_number(new_name);
LOG("num = %d\n", workspace->num);
/* By re-attaching, the sort order will be correct afterwards. */
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}");
}
+
+/*
+ * Implementation of 'bar mode dock|hide|invisible|toggle [<bar_id>]'
+ *
+ */
+bool cmd_bar_mode(char *bar_mode, char *bar_id) {
+ int mode = M_DOCK;
+ bool toggle = false;
+ if (strcmp(bar_mode, "dock") == 0)
+ mode = M_DOCK;
+ else if (strcmp(bar_mode, "hide") == 0)
+ mode = M_HIDE;
+ else if (strcmp(bar_mode, "invisible") == 0)
+ mode = M_INVISIBLE;
+ else if (strcmp(bar_mode, "toggle") == 0)
+ toggle = true;
+ else {
+ ELOG("Unknown bar mode \"%s\", this is a mismatch between code and parser spec.\n", bar_mode);
+ return false;
+ }
+
+ bool changed_sth = false;
+ Barconfig *current = NULL;
+ TAILQ_FOREACH(current, &barconfigs, configs) {
+ if (bar_id && strcmp(current->id, bar_id) != 0)
+ continue;
+
+ if (toggle)
+ mode = (current->mode + 1) % 2;
+
+ DLOG("Changing bar mode of bar_id '%s' to '%s (%d)'\n", current->id, bar_mode, mode);
+ current->mode = mode;
+ changed_sth = true;
+
+ if (bar_id)
+ break;
+ }
+
+ if (bar_id && !changed_sth) {
+ DLOG("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Implementation of 'bar hidden_state hide|show|toggle [<bar_id>]'
+ *
+ */
+bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id) {
+ int hidden_state = S_SHOW;
+ bool toggle = false;
+ if (strcmp(bar_hidden_state, "hide") == 0)
+ hidden_state = S_HIDE;
+ else if (strcmp(bar_hidden_state, "show") == 0)
+ hidden_state = S_SHOW;
+ else if (strcmp(bar_hidden_state, "toggle") == 0)
+ toggle = true;
+ else {
+ ELOG("Unknown bar state \"%s\", this is a mismatch between code and parser spec.\n", bar_hidden_state);
+ return false;
+ }
+
+ bool changed_sth = false;
+ Barconfig *current = NULL;
+ TAILQ_FOREACH(current, &barconfigs, configs) {
+ if (bar_id && strcmp(current->id, bar_id) != 0)
+ continue;
+
+ if (toggle)
+ hidden_state = (current->hidden_state + 1) % 2;
+
+ DLOG("Changing bar hidden_state of bar_id '%s' to '%s (%d)'\n", current->id, bar_hidden_state, hidden_state);
+ current->hidden_state = hidden_state;
+ changed_sth = true;
+
+ if (bar_id)
+ break;
+ }
+
+ if (bar_id && !changed_sth) {
+ DLOG("Changing bar hidden_state of bar_id %s failed, bar_id not found.\n", bar_id);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]'
+ *
+ */
+void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id) {
+ bool ret;
+ if (strcmp(bar_type, "mode") == 0)
+ ret = cmd_bar_mode(bar_value, bar_id);
+ else if (strcmp(bar_type, "hidden_state") == 0)
+ ret = cmd_bar_hidden_state(bar_value, bar_id);
+ else {
+ ELOG("Unknown bar option type \"%s\", this is a mismatch between code and parser spec.\n", bar_type);
+ ret = false;
+ }
+
+ ysuccess(ret);
+ if (!ret)
+ return;
+
+ update_barconfig();
+}
+
+/*
+ * Implementation of 'shmlog <size>|toggle|on|off'
+ *
+ */
+void cmd_shmlog(I3_CMD, char *argument) {
+ if (!strcmp(argument, "toggle"))
+ /* Toggle shm log, if size is not 0. If it is 0, set it to default. */
+ shmlog_size = shmlog_size ? -shmlog_size : default_shmlog_size;
+ else if (!strcmp(argument, "on"))
+ shmlog_size = default_shmlog_size;
+ else if (!strcmp(argument, "off"))
+ shmlog_size = 0;
+ else {
+ /* If shm logging now, restart logging with the new size. */
+ if (shmlog_size > 0) {
+ shmlog_size = 0;
+ LOG("Restarting shm logging...\n");
+ init_logging();
+ }
+ shmlog_size = atoi(argument);
+ /* Make a weakly attempt at ensuring the argument is valid. */
+ if (shmlog_size <= 0)
+ shmlog_size = default_shmlog_size;
+ }
+ LOG("%s shm logging\n", shmlog_size > 0 ? "Enabling" : "Disabling");
+ init_logging();
+ update_shmlog_atom();
+ // XXX: default reply for now, make this a better reply
+ ysuccess(true);
+}
+
+/*
+ * Implementation of 'debuglog toggle|on|off'
+ *
+ */
+void cmd_debuglog(I3_CMD, char *argument) {
+ bool logging = get_debug_logging();
+ if (!strcmp(argument, "toggle")) {
+ LOG("%s debug logging\n", logging ? "Disabling" : "Enabling");
+ set_debug_logging(!logging);
+ } else if (!strcmp(argument, "on") && !logging) {
+ LOG("Enabling debug logging\n");
+ set_debug_logging(true);
+ } else if (!strcmp(argument, "off") && logging) {
+ LOG("Disabling debug logging\n");
+ set_debug_logging(false);
+ }
+ // XXX: default reply for now, make this a better reply
+ ysuccess(true);
+}