X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcommands.c;h=62adcc6536ab87b36dd02c45ce50d555e1292200;hb=6be1b28813c15f0ba02eff50d114271eda956347;hp=49005ba7d5421c98f80041cbb1fdf1088bdcd63a;hpb=eb73059c610752fc18e0fcc5c5402bb9b7770574;p=i3%2Fi3 diff --git a/src/commands.c b/src/commands.c index 49005ba7..62adcc65 100644 --- a/src/commands.c +++ b/src/commands.c @@ -4,7 +4,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * * commands.c: all command functions (see commands_parser.c) * @@ -27,16 +27,19 @@ 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); \ - } \ +#define yerror(format, ...) \ + do { \ + if (cmd_output->json_gen != NULL) { \ + char *message; \ + sasprintf(&message, format, ##__VA_ARGS__); \ + y(map_open); \ + ystr("success"); \ + y(bool, false); \ + ystr("error"); \ + ystr(message); \ + y(map_close); \ + free(message); \ + } \ } while (0) /** When the command did not include match criteria (!), we use the currently @@ -332,7 +335,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) { if (strcmp(ctype, "con_id") == 0) { char *end; - long parsed = strtol(cvalue, &end, 10); + long parsed = strtol(cvalue, &end, 0); if (parsed == LONG_MIN || parsed == LONG_MAX || parsed < 0 || @@ -347,7 +350,7 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) { if (strcmp(ctype, "id") == 0) { char *end; - long parsed = strtol(cvalue, &end, 10); + long parsed = strtol(cvalue, &end, 0); if (parsed == LONG_MIN || parsed == LONG_MAX || parsed < 0 || @@ -360,6 +363,31 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) { return; } + if (strcmp(ctype, "window_type") == 0) { + if (strcasecmp(cvalue, "normal") == 0) + current_match->window_type = A__NET_WM_WINDOW_TYPE_NORMAL; + else if (strcasecmp(cvalue, "dialog") == 0) + current_match->window_type = A__NET_WM_WINDOW_TYPE_DIALOG; + else if (strcasecmp(cvalue, "utility") == 0) + current_match->window_type = A__NET_WM_WINDOW_TYPE_UTILITY; + else if (strcasecmp(cvalue, "toolbar") == 0) + current_match->window_type = A__NET_WM_WINDOW_TYPE_TOOLBAR; + else if (strcasecmp(cvalue, "splash") == 0) + current_match->window_type = A__NET_WM_WINDOW_TYPE_SPLASH; + else if (strcasecmp(cvalue, "menu") == 0) + current_match->window_type = A__NET_WM_WINDOW_TYPE_MENU; + else if (strcasecmp(cvalue, "dropdown_menu") == 0) + current_match->window_type = A__NET_WM_WINDOW_TYPE_DROPDOWN_MENU; + else if (strcasecmp(cvalue, "popup_menu") == 0) + current_match->window_type = A__NET_WM_WINDOW_TYPE_POPUP_MENU; + else if (strcasecmp(cvalue, "tooltip") == 0) + current_match->window_type = A__NET_WM_WINDOW_TYPE_TOOLTIP; + else + ELOG("unknown window_type value \"%s\"\n", cvalue); + + return; + } + if (strcmp(ctype, "con_mark") == 0) { current_match->mark = regex_new(cvalue); return; @@ -383,6 +411,11 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) { return; } + if (strcmp(ctype, "workspace") == 0) { + current_match->workspace = regex_new(cvalue); + return; + } + ELOG("Unknown criterion: %s\n", ctype); } @@ -491,27 +524,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { LOG("should move window to workspace %s\n", name); /* get the workspace */ - Con *ws = NULL; - Con *output = NULL; - - /* first look for a workspace with this name */ - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { - GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name)); - } - - /* if the name is plain digits, we interpret this as a "workspace number" - * command */ - if (!ws && name_is_digits(name)) { - long parsed_num = ws_name_to_number(name); - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { - GREP_FIRST(ws, output_get_content(output), - child->num == parsed_num); - } - } - - /* if no workspace was found, make a new one */ - if (!ws) - ws = workspace_get(name, NULL); + Con *ws = workspace_get(name, NULL); ws = maybe_auto_back_and_forth_workspace(ws); @@ -552,8 +565,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *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"); + yerror("Could not parse number \"%s\"", which); return; } @@ -809,7 +821,7 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz } /* - * Implementation of 'border normal|none|1pixel|toggle|pixel'. + * Implementation of 'border normal|pixel []', 'border none|1pixel|toggle'. * */ void cmd_border(I3_CMD, char *border_style_str, char *border_width) { @@ -878,11 +890,15 @@ void cmd_nop(I3_CMD, char *comment) { void cmd_append_layout(I3_CMD, char *path) { LOG("Appending layout \"%s\"\n", path); + /* Make sure we allow paths like '~/.i3/layout.json' */ + path = resolve_tilde(path); + json_content_t content = json_determine_content(path); LOG("JSON content = %d\n", content); if (content == JSON_CONTENT_UNKNOWN) { ELOG("Could not determine the contents of \"%s\", not loading.\n", path); - ysuccess(false); + yerror("Could not determine the contents of \"%s\".", path); + free(path); return; } @@ -924,6 +940,7 @@ void cmd_append_layout(I3_CMD, char *path) { if (content == JSON_CONTENT_WORKSPACE) ipc_send_workspace_event("restored", parent, NULL); + free(path); cmd_output->needs_tree_render = true; } @@ -980,8 +997,7 @@ void cmd_workspace_number(I3_CMD, char *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"); + yerror("Could not parse number \"%s\"", which); return; } @@ -1043,30 +1059,7 @@ void cmd_workspace_name(I3_CMD, char *name) { DLOG("should switch to workspace %s\n", name); if (maybe_back_and_forth(cmd_output, name)) return; - - Con *ws = NULL; - Con *output = NULL; - - /* first look for a workspace with this name */ - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { - GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name)); - } - - /* if the name is only digits, we interpret this as a "workspace number" - * command */ - if (!ws && name_is_digits(name)) { - long parsed_num = ws_name_to_number(name); - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { - GREP_FIRST(ws, output_get_content(output), - child->num == parsed_num); - } - } - - /* if no workspace was found, make a new one */ - if (!ws) - ws = workspace_get(name, NULL); - - workspace_show(ws); + workspace_show_by_name(name); cmd_output->needs_tree_render = true; // XXX: default reply for now, make this a better reply @@ -1074,28 +1067,48 @@ void cmd_workspace_name(I3_CMD, char *name) { } /* - * Implementation of 'mark ' + * Implementation of 'mark [--toggle] ' * */ -void cmd_mark(I3_CMD, char *mark) { - DLOG("Clearing all windows which have that mark first\n"); +void cmd_mark(I3_CMD, char *mark, char *toggle) { + HANDLE_EMPTY_MATCH; - Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) { - if (con->mark && strcmp(con->mark, mark) == 0) - FREE(con->mark); + owindow *current = TAILQ_FIRST(&owindows); + if (current == NULL) { + ysuccess(false); + return; } - DLOG("marking window with str %s\n", mark); - owindow *current; - - HANDLE_EMPTY_MATCH; + /* Marks must be unique, i.e., no two windows must have the same mark. */ + if (current != TAILQ_LAST(&owindows, owindows_head)) { + yerror("A mark must not be put onto more than one window"); + return; + } - TAILQ_FOREACH(current, &owindows, owindows) { - DLOG("matching: %p / %s\n", current->con, current->con->name); + DLOG("matching: %p / %s\n", current->con, current->con->name); + current->con->mark_changed = true; + if (toggle != NULL && current->con->mark && strcmp(current->con->mark, mark) == 0) { + DLOG("removing window mark %s\n", mark); + FREE(current->con->mark); + } else { + DLOG("marking window with str %s\n", mark); + FREE(current->con->mark); current->con->mark = sstrdup(mark); } + DLOG("Clearing all non-matched windows with this mark\n"); + Con *con; + TAILQ_FOREACH(con, &all_cons, all_cons) { + /* Skip matched window, we took care of it already. */ + if (current->con == con) + continue; + + if (con->mark && strcmp(con->mark, mark) == 0) { + FREE(con->mark); + con->mark_changed = true; + } + } + cmd_output->needs_tree_render = true; // XXX: default reply for now, make this a better reply ysuccess(true); @@ -1109,16 +1122,20 @@ void cmd_unmark(I3_CMD, char *mark) { if (mark == NULL) { Con *con; TAILQ_FOREACH(con, &all_cons, all_cons) { + if (con->mark == NULL) + continue; + FREE(con->mark); + con->mark_changed = true; } - DLOG("removed all window marks"); + DLOG("Removed all window marks.\n"); } else { - Con *con; - TAILQ_FOREACH(con, &all_cons, all_cons) { - if (con->mark && strcmp(con->mark, mark) == 0) - FREE(con->mark); + Con *con = con_by_mark(mark); + if (con != NULL) { + FREE(con->mark); + con->mark_changed = true; } - DLOG("removed window mark %s\n", mark); + DLOG("Removed window mark \"%s\".\n", mark); } cmd_output->needs_tree_render = true; @@ -1149,28 +1166,13 @@ void cmd_move_con_to_output(I3_CMD, char *name) { HANDLE_EMPTY_MATCH; - /* get the output */ Output *current_output = NULL; - Output *output; - // TODO: fix the handling of criteria TAILQ_FOREACH(current, &owindows, owindows) 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_wrap(D_UP, current_output); - else if (strcasecmp(name, "down") == 0) - output = get_output_next_wrap(D_DOWN, current_output); - else if (strcasecmp(name, "left") == 0) - output = get_output_next_wrap(D_LEFT, current_output); - else if (strcasecmp(name, "right") == 0) - output = get_output_next_wrap(D_RIGHT, current_output); - else - output = get_output_by_name(name); - + Output *output = get_output_from_string(current_output, name); if (!output) { LOG("No such output found.\n"); ysuccess(false); @@ -1195,6 +1197,26 @@ void cmd_move_con_to_output(I3_CMD, char *name) { ysuccess(true); } +/* + * Implementation of 'move [container|window] [to] mark '. + * + */ +void cmd_move_con_to_mark(I3_CMD, char *mark) { + DLOG("moving window to mark \"%s\"\n", mark); + + HANDLE_EMPTY_MATCH; + + bool result = true; + owindow *current; + 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); + } + + cmd_output->needs_tree_render = true; + ysuccess(result); +} + /* * Implementation of 'floating enable|disable|toggle' * @@ -1798,31 +1820,48 @@ void cmd_move_window_to_center(I3_CMD, char *method) { } 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; + floating_center(focused->parent, croot->rect); floating_maybe_reassign_ws(focused->parent); cmd_output->needs_tree_render = true; } if (strcmp(method, "position") == 0) { - Rect *wsrect = &con_get_workspace(focused)->rect; - 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; + floating_center(focused->parent, con_get_workspace(focused)->rect); - floating_reposition(focused->parent, newrect); + cmd_output->needs_tree_render = true; } // XXX: default reply for now, make this a better reply ysuccess(true); } +/* + * Implementation of 'move [window|container] [to] position mouse' + * + */ +void cmd_move_window_to_mouse(I3_CMD) { + HANDLE_EMPTY_MATCH; + + owindow *current; + 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", + current->con, current->con->name); + continue; + } + + DLOG("moving floating container %p / %s to cursor position\n", floating_con, floating_con->name); + floating_move_to_pointer(floating_con); + } + + cmd_output->needs_tree_render = true; + ysuccess(true); +} + /* * Implementation of 'move scratchpad'. * @@ -1865,13 +1904,42 @@ void cmd_scratchpad_show(I3_CMD) { ysuccess(true); } +/* + * Implementation of 'title_format ' + * + */ +void cmd_title_format(I3_CMD, char *format) { + DLOG("setting title_format to \"%s\"\n", format); + HANDLE_EMPTY_MATCH; + + owindow *current; + TAILQ_FOREACH(current, &owindows, owindows) { + if (current->con->window == NULL) + continue; + + DLOG("setting title_format for %p / %s\n", current->con, current->con->name); + FREE(current->con->window->title_format); + + /* If we only display the title without anything else, we can skip the parsing step, + * so we remove the title format altogether. */ + if (strcasecmp(format, "%title") != 0) + current->con->window->title_format = sstrdup(format); + + /* Make sure the window title is redrawn immediately. */ + current->con->window->name_x_changed = true; + } + + cmd_output->needs_tree_render = true; + ysuccess(true); +} + /* * Implementation of 'rename workspace [] to ' * */ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { if (strncasecmp(new_name, "__", strlen("__")) == 0) { - LOG("Cannot rename workspace to \"%s\": names starting with __ are i3-internal.", new_name); + LOG("Cannot rename workspace to \"%s\": names starting with __ are i3-internal.\n", new_name); ysuccess(false); return; } @@ -1888,13 +1956,11 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { !strcasecmp(child->name, old_name)); } else { workspace = con_get_workspace(focused); + old_name = workspace->name; } if (!workspace) { - // TODO: we should include the old workspace name here and use yajl for - // generating the reply. - // TODO: better error message - yerror("Old workspace not found"); + yerror("Old workspace \"%s\" not found", old_name); return; } @@ -1904,10 +1970,7 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { !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. - // TODO: better error message - yerror("New workspace already exists"); + yerror("New workspace \"%s\" already exists", new_name); return; } @@ -1929,13 +1992,15 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { if (assignment->output == NULL) continue; - if (strcmp(assignment->name, workspace->name) != 0 - && (!name_is_digits(assignment->name) || ws_name_to_number(assignment->name) != workspace->num)) { - + if (strcmp(assignment->name, workspace->name) != 0 && (!name_is_digits(assignment->name) || ws_name_to_number(assignment->name) != workspace->num)) { continue; } workspace_move_to_output(workspace, assignment->output); + + if (previously_focused) + workspace_show(con_get_workspace(previously_focused)); + break; } @@ -1949,6 +2014,8 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); ewmh_update_current_desktop(); + + startup_sequence_rename_workspace(old_name, new_name); } /*