diff --git a/include/commands.h b/include/commands.h index 69ab1380..5c87be51 100644 --- a/include/commands.h +++ b/include/commands.h @@ -3,21 +3,19 @@ #include #include "config.h" -typedef enum cmd_status { - CMD_SUCCESS, - CMD_FAILURE, - CMD_DEFER, -} sway_cmd(char *criteria, int argc, char **argv); - struct cmd_handler { - const char*command; - sway_cmd *handle; + char *command; + enum cmd_status { + CMD_SUCCESS, + CMD_FAILURE, + CMD_DEFER, + } (*handle)(int argc, char **argv); }; enum cmd_status handle_command(char *command); // Handles commands during config enum cmd_status config_command(char *command); -void remove_view_from_scratchpad(swayc_t *view); +void remove_view_from_scratchpad(); #endif diff --git a/include/config.h b/include/config.h index 04db3e5c..676218c8 100644 --- a/include/config.h +++ b/include/config.h @@ -63,10 +63,6 @@ bool load_config(const char *file); bool read_config(FILE *file, bool is_active); char *do_var_replacement(char *str); -// Find workspace_output from config by workspace or output name -struct workspace_output *wsop_find_workspace(const char *); -struct workspace_output *wsop_find_output(const char *); - extern struct sway_config *config; #endif diff --git a/include/container.h b/include/container.h index b164af95..ae9a9fc5 100644 --- a/include/container.h +++ b/include/container.h @@ -2,25 +2,29 @@ #define _SWAY_CONTAINER_H #include typedef struct sway_container swayc_t; + #include "layout.h" -enum swayc_types { - C_ROOT = 1 << 0, - C_OUTPUT = 1 << 1, - C_WORKSPACE = 1 << 2, - C_CONTAINER = 1 << 3, - C_VIEW = 1 << 4, - C_TYPES = 5, +enum swayc_types{ + C_ROOT, + C_OUTPUT, + C_WORKSPACE, + C_CONTAINER, + C_VIEW, + // Keep last + C_TYPES, }; -enum swayc_layouts { - L_NONE = 1 << 0, - L_HORIZ = 1 << 1, - L_VERT = 1 << 2, - L_STACKED = 1 << 3, - L_TABBED = 1 << 4, - L_FLOATING = 1 << 5, - L_LAYOUTS = 6, + +enum swayc_layouts{ + L_NONE, + L_HORIZ, + L_VERT, + L_STACKED, + L_TABBED, + L_FLOATING, + // Keep last + L_LAYOUTS, }; struct sway_container { @@ -31,16 +35,13 @@ struct sway_container { // Not including borders or margins double width, height; - double x, y; // Used for setting floating geometry int desired_width, desired_height; - enum visibility_mask { - INVISIBLE = false, - VISIBLE = true, - } visible; + double x, y; + bool visible; bool is_floating; bool is_focused; @@ -55,120 +56,70 @@ struct sway_container { struct sway_container *focused; }; -// swayc Creation +enum visibility_mask { + VISIBLE = true +} visible; -/* Creates and returns new, or an already created output. - * If it creates a new output, it also creates a workspace using - * `new_workspace(outputname, NULL);` */ -swayc_t *new_output(wlc_handle handle); +// Container Creation -/* Creates workspace with given name, under given output. - * If workspace with that name already exists, returns that workspace - * If name is NULL, it will choose a name automatically. - * If output is NULL, it will choose an output automatically. */ +swayc_t *new_output(wlc_handle handle); swayc_t *new_workspace(swayc_t *output, const char *name); - // Creates container Around child (parent child) -> (parent (container child)) swayc_t *new_container(swayc_t *child, enum swayc_layouts layout); - // Creates view as a sibling of current focused container, or as child of a workspace swayc_t *new_view(swayc_t *sibling, wlc_handle handle); - // Creates view as a new floating view which is in the active workspace swayc_t *new_floating_view(wlc_handle handle); // Container Destroying -// Destroys output and moves workspaces to another output -swayc_t *destroy_output(swayc_t *output); +swayc_t *destroy_output(swayc_t *output); // Destroys workspace if empty and returns parent pointer, else returns NULL swayc_t *destroy_workspace(swayc_t *workspace); - // Destroyes container and all parent container if they are empty, returns // topmost non-empty parent. returns NULL otherwise swayc_t *destroy_container(swayc_t *container); - // Destroys view and all empty parent containers. return topmost non-empty // parent swayc_t *destroy_view(swayc_t *view); -// Container Mapping and testing functions -typedef bool swayc_test_func(swayc_t *view, void *data); -typedef void swayc_map_func(swayc_t *view, void *data); - -// Returns the first swayc that matches test() -swayc_t *swayc_by_test_r(swayc_t *root, swayc_test_func test, void *data); -swayc_t *swayc_by_test(swayc_test_func test, void *data); - -// Calls func for all children. -void swayc_map_r(swayc_t *root, swayc_map_func func, void *data); -void swayc_map(swayc_map_func func, void *data); - - -// Call func on container if test passes -void swayc_map_by_test_r(swayc_t *root, - swayc_map_func func, swayc_test_func test, - void *funcdata, void *testdata); -void swayc_map_by_test( - swayc_map_func func, swayc_test_func test, - void *funcdata, void *testdata); - -// Map functions -swayc_map_func set_gaps; -swayc_map_func add_gaps; - -// Test functions -// generic swayc tests -swayc_test_func test_name; -swayc_test_func test_name_regex; -swayc_test_func test_layout; -swayc_test_func test_type; -swayc_test_func test_visibility; -swayc_test_func test_handle; - -// C_VIEW tests -// See wlc_view_*_bit enums -swayc_test_func test_view_state; -swayc_test_func test_view_type; -swayc_test_func test_view_title; -swayc_test_func test_view_class; -swayc_test_func test_view_appid; -swayc_test_func test_view_title_regex; -swayc_test_func test_view_class_regex; -swayc_test_func test_view_appid_regex; - -// functions for test_*_regex -void *compile_regex(const char *regex); -void free_regex(void *); - -// these take a NULL terminated array of test_list struct. -struct test_list { swayc_test_func *test; void *data ; }; -swayc_test_func test_and; -swayc_test_func test_or; +// Container Lookup +swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data); swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types); swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts); // Follow focused until type/layout swayc_t *swayc_focus_by_type(swayc_t *container, enum swayc_types); swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts); + +swayc_t *swayc_by_handle(wlc_handle handle); +swayc_t *swayc_by_name(const char *name); swayc_t *swayc_active_output(void); swayc_t *swayc_active_workspace(void); swayc_t *swayc_active_workspace_for(swayc_t *view); // Container information -// if `parent` is the parent of `child` + +bool swayc_is_fullscreen(swayc_t *view); +bool swayc_is_active(swayc_t *view); +// Is `parent` the parent of `child` bool swayc_is_parent_of(swayc_t *parent, swayc_t *child); -// If `child` is a child of `parent` +// Is `child` a child of `parent` bool swayc_is_child_of(swayc_t *child, swayc_t *parent); // Return gap of specified container int swayc_gap(swayc_t *container); -bool swayc_is_fullscreen(swayc_t *view); -bool swayc_is_active(swayc_t *view); +// Mapping functions + +void container_map(swayc_t *, void (*f)(swayc_t *, void *), void *); +// Mappings +void set_view_visibility(swayc_t *view, void *data); +// Set or add to gaps +void set_gaps(swayc_t *view, void *amount); +void add_gaps(swayc_t *view, void *amount); -// Specialized mapping functions void update_visibility(swayc_t *container); #endif diff --git a/include/stringop.h b/include/stringop.h index 6e80e729..dde50f13 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -19,7 +19,7 @@ void free_argv(int argc, char **argv); char *code_strchr(const char *string, char delimiter); char *code_strstr(const char *haystack, const char *needle); int unescape_string(char *string); -char *join_args(int argc, char **argv); +char *join_args(char **argv, int argc); char *join_list(list_t *list, char *separator); char *strdup(const char *); diff --git a/include/workspace.h b/include/workspace.h index 3a63ea38..7343b055 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -7,17 +7,13 @@ extern char *prev_workspace_name; -// Search for available workspace name on output from config -const char *workspace_output_open_name(swayc_t *output); -// Search for any available workspace name -const char *workspace_next_name(void); - - +char *workspace_next_name(void); +swayc_t *workspace_create(const char*); swayc_t *workspace_by_name(const char*); void workspace_switch(swayc_t*); -swayc_t *workspace_output_next(void); -swayc_t *workspace_next(void); -swayc_t *workspace_output_prev(void); -swayc_t *workspace_prev(void); +swayc_t *workspace_output_next(); +swayc_t *workspace_next(); +swayc_t *workspace_output_prev(); +swayc_t *workspace_prev(); #endif diff --git a/sway/commands.c b/sway/commands.c index 3ed0c8db..e79746ae 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -21,35 +21,6 @@ swayc_t *sp_view; int sp_index = 0; -// Commands -static sway_cmd cmd_bindsym; -static sway_cmd cmd_orientation; -static sway_cmd cmd_exec; -static sway_cmd cmd_exec_always; -static sway_cmd cmd_exit; -static sway_cmd cmd_floating; -static sway_cmd cmd_floating_mod; -static sway_cmd cmd_focus; -static sway_cmd cmd_focus_follows_mouse; -static sway_cmd cmd_for_window; -static sway_cmd cmd_fullscreen; -static sway_cmd cmd_gaps; -static sway_cmd cmd_kill; -static sway_cmd cmd_layout; -static sway_cmd cmd_log_colors; -static sway_cmd cmd_mode; -static sway_cmd cmd_move; -static sway_cmd cmd_output; -static sway_cmd cmd_reload; -static sway_cmd cmd_resize; -static sway_cmd cmd_scratchpad; -static sway_cmd cmd_set; -static sway_cmd cmd_split; -static sway_cmd cmd_splith; -static sway_cmd cmd_splitv; -static sway_cmd cmd_workspace; -static sway_cmd cmd_ws_auto_back_and_forth; - static struct modifier_key { char *name; uint32_t mod; @@ -123,7 +94,7 @@ static int bindsym_sort(const void *_lbind, const void *_rbind) { return (rbind->keys->length + rmod) - (lbind->keys->length + lmod); } -enum cmd_status cmd_bindsym(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_bindsym(int argc, char **argv) { if (!checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1) || !config->reading) { return CMD_FAILURE; @@ -132,7 +103,7 @@ enum cmd_status cmd_bindsym(char *criteria, int argc, char **argv) { struct sway_binding *binding = malloc(sizeof(struct sway_binding)); binding->keys = create_list(); binding->modifiers = 0; - binding->command = join_args(argc - 1, argv + 1); + binding->command = join_args(argv + 1, argc - 1); list_t *split = split_string(argv[0], "+"); int i; @@ -173,7 +144,7 @@ enum cmd_status cmd_bindsym(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_exec_always(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_exec_always(int argc, char **argv) { if (!checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0)) { return CMD_FAILURE; } @@ -189,7 +160,7 @@ enum cmd_status cmd_exec_always(char *criteria, int argc, char **argv) { } /* Child process */ if (pid == 0) { - char *args = join_args(argc, argv); + char *args = join_args(argv, argc); sway_log(L_DEBUG, "Executing %s", args); execl("/bin/sh", "sh", "-c", args, (char *)NULL); /* Execl doesnt return unless failure */ @@ -201,17 +172,17 @@ enum cmd_status cmd_exec_always(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_exec(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_exec(int argc, char **argv) { if (!config->active) { return CMD_DEFER; } if (config->reloading) { - char *args = join_args(argc, argv); + char *args = join_args(argv, argc); sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); free(args); return CMD_SUCCESS; } - return cmd_exec_always(criteria, argc, argv); + return cmd_exec_always(argc, argv); } static void kill_views(swayc_t *container, void *data) { @@ -220,19 +191,18 @@ static void kill_views(swayc_t *container, void *data) { } } -enum cmd_status cmd_exit(char *criteria, int argc, char **argv) { - (void) argv; +static enum cmd_status cmd_exit(int argc, char **argv) { if (!checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0) || config->reading || !config->active) { return CMD_FAILURE; } // Close all views - swayc_map(kill_views, NULL); + container_map(&root_container, kill_views, NULL); sway_terminate(); return CMD_SUCCESS; } -enum cmd_status cmd_floating(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_floating(int argc, char **argv) { if (!checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1) || config->reading || !config->active) { return CMD_FAILURE; @@ -294,7 +264,7 @@ enum cmd_status cmd_floating(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_floating_mod(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_floating_mod(int argc, char **argv) { if (!checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1) || !config->reading) { return CMD_FAILURE; @@ -319,7 +289,7 @@ enum cmd_status cmd_floating_mod(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_focus(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_focus(int argc, char **argv) { static int floating_toggled_index = 0; static int tiled_toggled_index = 0; if (!checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1) @@ -377,7 +347,7 @@ enum cmd_status cmd_focus(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_focus_follows_mouse(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_focus_follows_mouse(int argc, char **argv) { if (!checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1)) { return CMD_FAILURE; } @@ -386,21 +356,6 @@ enum cmd_status cmd_focus_follows_mouse(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -static void debug_for_window(swayc_t *view, void *data) { - layout_log(view, 0); -} - -enum cmd_status cmd_for_window(char *criteria, int argc, char **argv) { - if (!checkarg(argc, "for_window", EXPECTED_AT_LEAST, 1)) { - return CMD_FAILURE; - } - //TODO - void *re = compile_regex(argv[0]); - swayc_map_by_test(debug_for_window, test_view_title_regex, NULL, re); - free_regex(re); - return CMD_FAILURE; -} - static void hide_view_in_scratchpad(swayc_t *sp_view) { if(sp_view == NULL) { return; @@ -417,7 +372,7 @@ static void hide_view_in_scratchpad(swayc_t *sp_view) { set_focused_container(container_under_pointer()); } -enum cmd_status cmd_mode(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_mode(int argc, char **argv) { if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) { return CMD_FAILURE; } @@ -426,7 +381,7 @@ enum cmd_status cmd_mode(char *criteria, int argc, char **argv) { return CMD_FAILURE; } - char *mode_name = join_args(argc - mode_make, argv); + char *mode_name = join_args(argv, argc - mode_make); struct sway_mode *mode = NULL; // Find mode int i, len = config->modes->length; @@ -456,7 +411,7 @@ enum cmd_status cmd_mode(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_move(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_move(int argc, char **argv) { if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1) || config->reading || !config->active) { return CMD_FAILURE; @@ -489,7 +444,11 @@ enum cmd_status cmd_move(char *criteria, int argc, char **argv) { // move "container to workspace number x" ws_name = argv[4]; } - swayc_t *ws = new_workspace(NULL, ws_name); + + swayc_t *ws = workspace_by_name(ws_name); + if (ws == NULL) { + ws = workspace_create(ws_name); + } move_container_to(view, get_focused_container(ws)); } else if (strcasecmp(argv[0], "scratchpad") == 0) { if (view->type != C_CONTAINER && view->type != C_VIEW) { @@ -523,7 +482,7 @@ enum cmd_status cmd_move(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_orientation(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_orientation(int argc, char **argv) { if (!checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1) || !config->reading) { return CMD_FAILURE; @@ -540,7 +499,7 @@ enum cmd_status cmd_orientation(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_output(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_output(int argc, char **argv) { if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) { return CMD_FAILURE; } @@ -605,7 +564,7 @@ enum cmd_status cmd_output(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_gaps(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_gaps(int argc, char **argv) { if (!checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1)) { return CMD_FAILURE; } @@ -739,14 +698,15 @@ enum cmd_status cmd_gaps(char *criteria, int argc, char **argv) { top = &root_container; } int top_gap = top->gaps; - swayc_map_r(top, method == SET ? set_gaps : add_gaps, &amount); + container_map(top, method == SET ? set_gaps : add_gaps, &amount); top->gaps = top_gap; arrange_windows(top, -1, -1); } + return CMD_SUCCESS; } -enum cmd_status cmd_kill(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_kill(int argc, char **argv) { if (config->reading || !config->active) { return CMD_FAILURE; } @@ -755,7 +715,7 @@ enum cmd_status cmd_kill(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_layout(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_layout(int argc, char **argv) { if (!checkarg(argc, "layout", EXPECTED_MORE_THAN, 0) || config->reading || !config->active) { return CMD_FAILURE; @@ -781,7 +741,7 @@ enum cmd_status cmd_layout(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_reload(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_reload(int argc, char **argv) { if (!checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0) || config->reading || !load_config(NULL)) { @@ -791,7 +751,7 @@ enum cmd_status cmd_reload(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_resize(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_resize(int argc, char **argv) { if (!checkarg(argc, "resize", EXPECTED_AT_LEAST, 3) || config->reading || !config->active) { return CMD_FAILURE; @@ -821,7 +781,7 @@ enum cmd_status cmd_resize(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -static swayc_t *fetch_view_from_scratchpad(void) { +static swayc_t *fetch_view_from_scratchpad() { if (sp_index >= scratchpad->length) { sp_index = 0; } @@ -866,7 +826,7 @@ void remove_view_from_scratchpad(swayc_t *view) { } } -enum cmd_status cmd_scratchpad(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_scratchpad(int argc, char **argv) { if (!checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1) || config->reading || !config->active) { return CMD_FAILURE; @@ -900,7 +860,7 @@ static int compare_set(const void *_l, const void *_r) { return strlen((*r)->name) - strlen((*l)->name); } -enum cmd_status cmd_set(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_set(int argc, char **argv) { if (!checkarg(argc, "set", EXPECTED_AT_LEAST, 2) || !config->reading) { return CMD_FAILURE; @@ -923,11 +883,11 @@ enum cmd_status cmd_set(char *criteria, int argc, char **argv) { list_add(config->symbols, var); list_sort(config->symbols, compare_set); } - var->value = join_args(argc - 1, argv + 1); + var->value = join_args(argv + 1, argc - 1); return CMD_SUCCESS; } -static enum cmd_status _do_split(char *criteria, int argc, char **argv, int layout) { +static enum cmd_status _do_split(int argc, char **argv, int layout) { char *name = layout == L_VERT ? "splitv" : layout == L_HORIZ ? "splith" : "split"; if (!checkarg(argc, name, EXPECTED_EQUAL_TO, 0) @@ -959,16 +919,16 @@ static enum cmd_status _do_split(char *criteria, int argc, char **argv, int layo return CMD_SUCCESS; } -enum cmd_status cmd_split(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_split(int argc, char **argv) { if (!checkarg(argc, "split", EXPECTED_EQUAL_TO, 1) || config->reading || !config->active) { return CMD_FAILURE; } if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) { - _do_split(criteria, argc - 1, argv + 1, L_VERT); + _do_split(argc - 1, argv + 1, L_VERT); } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) { - _do_split(criteria, argc - 1, argv + 1, L_HORIZ); + _do_split(argc - 1, argv + 1, L_HORIZ); } else { sway_log(L_ERROR, "Invalid split command (expected either horiziontal or vertical)."); return CMD_FAILURE; @@ -976,15 +936,15 @@ enum cmd_status cmd_split(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_splitv(char *criteria, int argc, char **argv) { - return _do_split(criteria, argc, argv, L_VERT); +static enum cmd_status cmd_splitv(int argc, char **argv) { + return _do_split(argc, argv, L_VERT); } -enum cmd_status cmd_splith(char *criteria, int argc, char **argv) { - return _do_split(criteria, argc, argv, L_HORIZ); +static enum cmd_status cmd_splith(int argc, char **argv) { + return _do_split(argc, argv, L_HORIZ); } -enum cmd_status cmd_log_colors(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_log_colors(int argc, char **argv) { if (!checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1) || !config->reading) { return CMD_FAILURE; @@ -1000,7 +960,7 @@ enum cmd_status cmd_log_colors(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_fullscreen(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_fullscreen(int argc, char **argv) { if (!checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0) || config->reading || !config->active) { return CMD_FAILURE; @@ -1020,7 +980,7 @@ enum cmd_status cmd_fullscreen(char *criteria, int argc, char **argv) { return CMD_SUCCESS; } -enum cmd_status cmd_workspace(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_workspace(int argc, char **argv) { if (!checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1)) { return CMD_FAILURE; } @@ -1029,7 +989,28 @@ enum cmd_status cmd_workspace(char *criteria, int argc, char **argv) { if (config->reading || !config->active) { return CMD_DEFER; } - workspace_switch(new_workspace(NULL, argv[0])); + // Handle workspace next/prev + swayc_t *ws = NULL; + if (strcasecmp(argv[0], "next") == 0) { + ws = workspace_next(); + } else if (strcasecmp(argv[0], "prev") == 0) { + ws = workspace_prev(); + } else if (strcasecmp(argv[0], "next_on_output") == 0) { + ws = workspace_output_next(); + } else if (strcasecmp(argv[0], "prev_on_output") == 0) { + ws = workspace_output_prev(); + } else if (strcasecmp(argv[0], "back_and_forth") == 0) { + if (prev_workspace_name) { + if (!(ws = workspace_by_name(prev_workspace_name))) { + ws = workspace_create(prev_workspace_name); + } + } + } else { + if (!(ws= workspace_by_name(argv[0]))) { + ws = workspace_create(argv[0]); + } + } + workspace_switch(ws); } else { if (strcasecmp(argv[1], "output") == 0) { if (!checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 3)) { @@ -1040,12 +1021,15 @@ enum cmd_status cmd_workspace(char *criteria, int argc, char **argv) { wso->workspace = strdup(argv[0]); wso->output = strdup(argv[2]); list_add(config->workspace_outputs, wso); + if (!config->reading) { + // TODO: Move workspace to output. (dont do so when reloading) + } } } return CMD_SUCCESS; } -enum cmd_status cmd_ws_auto_back_and_forth(char *criteria, int argc, char **argv) { +static enum cmd_status cmd_ws_auto_back_and_forth(int argc, char **argv) { if (!checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1)) { return CMD_FAILURE; } @@ -1060,7 +1044,7 @@ enum cmd_status cmd_ws_auto_back_and_forth(char *criteria, int argc, char **argv } /* Keep alphabetized */ -static const struct cmd_handler handlers[] = { +static struct cmd_handler handlers[] = { { "bindsym", cmd_bindsym }, { "default_orientation", cmd_orientation }, { "exec", cmd_exec }, @@ -1070,7 +1054,6 @@ static const struct cmd_handler handlers[] = { { "floating_modifier", cmd_floating_mod }, { "focus", cmd_focus }, { "focus_follows_mouse", cmd_focus_follows_mouse }, - { "for_window", cmd_for_window }, { "fullscreen", cmd_fullscreen }, { "gaps", cmd_gaps }, { "kill", cmd_kill }, @@ -1107,7 +1090,6 @@ static struct cmd_handler *find_handler(char *line) { enum cmd_status handle_command(char *exec) { sway_log(L_INFO, "Handling command '%s'", exec); int argc; - char *criteria = NULL; char **argv = split_args(exec, &argc); enum cmd_status status = CMD_FAILURE; struct cmd_handler *handler; @@ -1115,7 +1097,7 @@ enum cmd_status handle_command(char *exec) { return status; } if ((handler = find_handler(argv[0])) == NULL - || (status = handler->handle(criteria, argc - 1, argv + 1)) != CMD_SUCCESS) { + || (status = handler->handle(argc - 1, argv + 1)) != CMD_SUCCESS) { sway_log(L_ERROR, "Command failed: %s", argv[0]); } free_argv(argc, argv); @@ -1126,7 +1108,6 @@ enum cmd_status config_command(char *exec) { sway_log(L_INFO, "handling config command '%s'", exec); int argc; char **argv = split_args(exec, &argc); - char *criteria = NULL; enum cmd_status status = CMD_FAILURE; struct cmd_handler *handler; if (!argc) { @@ -1146,7 +1127,7 @@ enum cmd_status config_command(char *exec) { for (; i < e; ++i) { argv[i] = do_var_replacement(argv[i]); } - status = handler->handle(criteria, argc - 1, argv + 1); + status = handler->handle(argc - 1, argv + 1); if (status == CMD_FAILURE) { sway_log(L_ERROR, "Config load failed for line `%s'", exec); } else if (status == CMD_DEFER) { diff --git a/sway/config.c b/sway/config.c index da92030e..23d6ac0d 100644 --- a/sway/config.c +++ b/sway/config.c @@ -286,28 +286,3 @@ char *do_var_replacement(char *str) { } return str; } - -struct workspace_output *wsop_find_workspace(const char *name) { - int i, len = config->workspace_outputs->length; - struct workspace_output *wsop; - for (i = 0; i < len; ++i) { - wsop = config->workspace_outputs->items[i]; - if (strcasecmp(wsop->workspace, name) == 0) { - return wsop; - } - } - return NULL; -} - -struct workspace_output *wsop_find_output(const char *name) { - int i, len = config->workspace_outputs->length; - struct workspace_output *wsop; - for (i = 0; i < len; ++i) { - wsop = config->workspace_outputs->items[i]; - if (strcasecmp(wsop->output, name) == 0) { - return wsop; - } - } - return NULL; -} - diff --git a/sway/container.c b/sway/container.c index baf84378..ef0e6c55 100644 --- a/sway/container.c +++ b/sway/container.c @@ -1,7 +1,6 @@ #include #include #include -#include #include "config.h" #include "container.h" #include "workspace.h" @@ -54,30 +53,26 @@ static void free_swayc(swayc_t *cont) { // New containers swayc_t *new_output(wlc_handle handle) { + const struct wlc_size *size = wlc_output_get_resolution(handle); const char *name = wlc_output_get_name(handle); - int i, len; - // Find current outputs to see if this already exists - if (name) { - len = root_container.children->length; + { + int i, len = root_container.children->length; for (i = 0; i < len; ++i) { swayc_t *op = root_container.children->items[i]; - if (strcmp(op->name, name) == 0) { - sway_log(L_DEBUG, "restoring output %lu:%s", handle, op->name); + const char *op_name = op->name; + if (op_name && name && strcmp(op_name, name) == 0) { + sway_log(L_DEBUG, "restoring output %lu:%s", handle, op_name); return op; } } - } else { - sway_log(L_ERROR, "Output has no given name"); - return NULL; } - sway_log(L_DEBUG, "Adding output %lu:%s", handle, name); + sway_log(L_DEBUG, "Added output %lu:%s", handle, name); - // Find output config struct output_config *oc = NULL; - len = config->output_configs->length; - for (i = 0; i < len; ++i) { + int i; + for (i = 0; i < config->output_configs->length; ++i) { oc = config->output_configs->items[i]; if (strcasecmp(name, oc->name) == 0) { sway_log(L_DEBUG, "Matched output config for %s", name); @@ -91,88 +86,77 @@ swayc_t *new_output(wlc_handle handle) { } swayc_t *output = new_swayc(C_OUTPUT); + if (oc && oc->width != -1 && oc->height != -1) { + output->width = oc->width; + output->height = oc->height; + struct wlc_size new_size = { .w = oc->width, .h = oc->height }; + wlc_output_set_resolution(handle, &new_size); + } else { + output->width = size->w; + output->height = size->h; + } output->handle = handle; output->name = name ? strdup(name) : NULL; - - if (oc) { - // Set output width/height - if (oc->width > 0 && oc->height > 0) { - output->width = oc->width; - output->height = oc->height; - struct wlc_size geo = { .w = oc->width, .h = oc->height}; - wlc_output_set_resolution(handle, &geo); - } else { - struct wlc_size geo = *wlc_output_get_resolution(handle); - output->width = geo.w; - output->height = geo.h; - } - // find position in config or find where it should go - // TODO more intelligent method - if (oc->x > 0 && oc->y > 0) { - output->x = oc->x; - output->y = oc->y; - } else { - unsigned int x = 0; - len = root_container.children->length; - for (i = 0; i < len; ++i) { - swayc_t *c = root_container.children->items[i]; - if (c->type == C_OUTPUT) { - unsigned int cx = c->width + c->x; - if (cx > x) { - x = cx; - } + + // Find position for it + if (oc && oc->x != -1 && oc->y != -1) { + sway_log(L_DEBUG, "Set %s position to %d, %d", name, oc->x, oc->y); + output->x = oc->x; + output->y = oc->y; + } else { + int x = 0; + for (i = 0; i < root_container.children->length; ++i) { + swayc_t *c = root_container.children->items[i]; + if (c->type == C_OUTPUT) { + if (c->width + c->x > x) { + x = c->width + c->x; } } - output->x = x; } + output->x = x; } - // Add as child to root + add_child(&root_container, output); + // Create workspace + char *ws_name = NULL; + if (name) { + for (i = 0; i < config->workspace_outputs->length; ++i) { + struct workspace_output *wso = config->workspace_outputs->items[i]; + if (strcasecmp(wso->output, name) == 0) { + sway_log(L_DEBUG, "Matched workspace to output: %s for %s", wso->workspace, wso->output); + // Check if any other workspaces are using this name + if (workspace_by_name(wso->workspace)) { + sway_log(L_DEBUG, "But it's already taken"); + break; + } + sway_log(L_DEBUG, "So we're going to use it"); + ws_name = strdup(wso->workspace); + break; + } + } + } + if (!ws_name) { + ws_name = workspace_next_name(); + } + // create and initilize default workspace - swayc_t *ws = new_workspace(output, NULL); + swayc_t *ws = new_workspace(output, ws_name); ws->is_focused = true; + free(ws_name); + return output; } swayc_t *new_workspace(swayc_t *output, const char *name) { - swayc_t *ws = NULL; - struct workspace_output *wsop; - if (name) { - // Find existing workspace with same name. - // or workspace found by special name - if ((ws = workspace_by_name(name))) { - return ws; - } - // Find matching output from config - if (!output) { - if ((wsop = wsop_find_workspace(name))) { - int i, len = root_container.children->length; - for (i = 0; i < len; ++i) { - swayc_t *op = root_container.children->items[i]; - if (strcasecmp(op->name, wsop->output) == 0) { - output = op; - goto find_wsop_end; - } - } - } - // Set output to active_output if there is no output. - output = swayc_active_output(); - find_wsop_end:; - } - } else { - // No name or output, use active_output - if (!output) { - output = swayc_active_output(); - } - // search for available output name - if (!(name = workspace_output_open_name(output))) { - // otherwise just use simple next name - name = workspace_next_name(); - } + if (!ASSERT_NONNULL(output)) { + return NULL; } + sway_log(L_DEBUG, "Added workspace %s for output %u", name, (unsigned int)output->handle); swayc_t *workspace = new_swayc(C_WORKSPACE); + + // TODO: default_layout if (config->default_layout != L_NONE) { workspace->layout = config->default_layout; } else if (config->default_orientation != L_NONE) { @@ -390,7 +374,7 @@ swayc_t *destroy_view(swayc_t *view) { // Container lookup -swayc_t *swayc_by_test_r(swayc_t *container, swayc_test_func test, void *data) { +swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data) { if (!container->children) { return NULL; } @@ -409,7 +393,7 @@ swayc_t *swayc_by_test_r(swayc_t *container, swayc_test_func test, void *data) { if (test(child, data)) { return child; } else { - swayc_t *res = swayc_by_test_r(child, test, data); + swayc_t *res = swayc_by_test(child, test, data); if (res) { return res; } @@ -417,193 +401,18 @@ swayc_t *swayc_by_test_r(swayc_t *container, swayc_test_func test, void *data) { } return NULL; } -swayc_t *swayc_by_test(swayc_test_func test, void *data) { - return swayc_by_test_r(&root_container, test, data); -} - -void swayc_map_r(swayc_t *container, swayc_map_func f, void *data) { - if (container) { - f(container, data); - int i; - if (container->children) { - for (i = 0; i < container->children->length; ++i) { - swayc_t *child = container->children->items[i]; - swayc_map_r(child, f, data); - } - } - if (container->floating) { - for (i = 0; i < container->floating->length; ++i) { - swayc_t *child = container->floating->items[i]; - swayc_map_r(child, f, data); - } - } - } -} -void swayc_map(swayc_map_func f, void *data) { - swayc_map_r(&root_container, f, data); -} - -void swayc_map_by_test_r(swayc_t *container, - swayc_map_func func, swayc_test_func test, - void *funcdata, void *testdata) { - if (container) { - if (test(container, testdata)) { - func(container, funcdata); - } - int i; - if (container->children) { - for (i = 0; i < container->children->length; ++i) { - swayc_t *child = container->children->items[i]; - swayc_map_by_test_r(child, func, test, funcdata, testdata); - } - } - if (container->floating) { - for (i = 0; i < container->floating->length; ++i) { - swayc_t *child = container->floating->items[i]; - swayc_map_by_test_r(child, func, test, funcdata, testdata); - } - } - } -} -void swayc_map_by_test( - swayc_map_func func, swayc_test_func test, - void *funcdata, void *testdata) { - swayc_map_by_test_r(&root_container, func, test, funcdata, testdata); -} - - - -// Map functions -void set_gaps(swayc_t *view, void *_data) { - int *data = _data; - if (view->type == C_WORKSPACE || view->type == C_VIEW) { - view->gaps = *data; - } -} -void add_gaps(swayc_t *view, void *_data) { - int *data = _data; - if (view->type == C_WORKSPACE || view->type == C_VIEW) { - if ((view->gaps += *data) < 0) { - view->gaps = 0; - } +static bool test_name(swayc_t *view, void *data) { + if (!view && !view->name) { + return false; } + return strcmp(view->name, data) == 0; } -// Test functions -bool test_name(swayc_t *view, void *data) { - return view->name && strcmp(view->name, data) == 0; +swayc_t *swayc_by_name(const char *name) { + return swayc_by_test(&root_container, test_name, (void *)name); } -// test_name_regex -struct test_name_regex { - pcre *reg; - pcre_extra *regext; -}; - -void *compile_regex(const char *pattern) { - struct test_name_regex *regex = malloc(sizeof *regex); - const char *error; - int erroffset; - if (!(regex->reg = pcre_compile(pattern, 0, &error, &erroffset, NULL))) { - sway_log(L_ERROR, "Regex compilation failed:%s:%s", pattern, error); - free(regex); - return NULL; - } - regex->regext = pcre_study(regex->reg, 0, &error); - if (error) { - sway_log(L_DEBUG, "Regex study failed:%s:%s", pattern, error); - } - return regex; -} - -void free_regex(void *_regex) { - struct test_name_regex *regex = _regex; - pcre_free(regex->reg); - pcre_free_study(regex->regext); - free(regex); -} - -static bool exec_regex(const char *pattern, struct test_name_regex *regex) { - int ovector[300]; - return 0 < pcre_exec(regex->reg, regex->regext, pattern, - strlen(pattern), 0, 0, ovector, 300); -} - -bool test_name_regex(swayc_t *view, void *data) { - return view->name && exec_regex(view->name, data); -} -bool test_layout(swayc_t *view, void *data) { - return view->layout & *(enum swayc_layouts *)data; -} -bool test_type(swayc_t *view, void *data) { - return view->layout & *(enum swayc_types *)data; -} -bool test_visibility(swayc_t *view, void *data) { - return view->visible == *(bool *)data; -} -bool test_handle(swayc_t *view, void *data) { - return view->handle == *(wlc_handle *)data; -} - -// C_VIEW tests -bool test_view_state(swayc_t *view, void *data) { - return view->type == C_VIEW - && wlc_view_get_state(view->handle) & *(int *)data; -} -bool test_view_type(swayc_t *view, void *data) { - return view->type == C_VIEW - && wlc_view_get_type(view->handle) & *(int *)data; -} -bool test_view_title(swayc_t *view, void *data) { - return view->type == C_VIEW - && strcmp(view->name, data) == 0; -} -bool test_view_class(swayc_t *view, void *data) { - return view->type == C_VIEW - && strcmp(wlc_view_get_class(view->handle), data) == 0; -} -bool test_view_appid(swayc_t *view, void *data) { - return view->type == C_VIEW - && strcmp(wlc_view_get_app_id(view->handle), data) == 0; -} -bool test_view_title_regex(swayc_t *view, void *data) { - return view->type == C_VIEW - && exec_regex(view->name, data); -} -bool test_view_class_regex(swayc_t *view, void *data) { - return view->type == C_VIEW - && exec_regex(wlc_view_get_class(view->handle), data); -} -bool test_view_appid_regex(swayc_t *view, void *data) { - return view->type == C_VIEW - && exec_regex(wlc_view_get_app_id(view->handle), data); -} - -// Fancy test combiners -bool test_and(swayc_t *view, void *data) { - struct test_list *list = data; - while (list->test) { - if (!list->test(view, list->data)) { - return false; - } - ++list; - } - return true; -} -bool test_or(swayc_t *view, void *data) { - struct test_list *list = data; - while (list->test) { - if (list->test(view, list->data)) { - return true; - } - ++list; - } - return false; -} - -// Focus|parent lookup - swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) { if (!ASSERT_NONNULL(container)) { return NULL; @@ -656,6 +465,42 @@ swayc_t *swayc_focus_by_layout(swayc_t *container, enum swayc_layouts layout) { return container; } + +static swayc_t *_swayc_by_handle_helper(wlc_handle handle, swayc_t *parent) { + if (!parent || !parent->children) { + return NULL; + } + int i, len; + swayc_t **child; + if (parent->type == C_WORKSPACE) { + len = parent->floating->length; + child = (swayc_t **)parent->floating->items; + for (i = 0; i < len; ++i, ++child) { + if ((*child)->handle == handle) { + return *child; + } + } + } + + len = parent->children->length; + child = (swayc_t**)parent->children->items; + for (i = 0; i < len; ++i, ++child) { + if ((*child)->handle == handle) { + return *child; + } else { + swayc_t *res; + if ((res = _swayc_by_handle_helper(handle, *child))) { + return res; + } + } + } + return NULL; +} + +swayc_t *swayc_by_handle(wlc_handle handle) { + return _swayc_by_handle_helper(handle, &root_container); +} + swayc_t *swayc_active_output(void) { return root_container.focused; } @@ -688,13 +533,11 @@ swayc_t *swayc_active_workspace_for(swayc_t *cont) { // Container information bool swayc_is_fullscreen(swayc_t *view) { - return view && view->type == C_VIEW - && wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN; + return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN); } bool swayc_is_active(swayc_t *view) { - return view && view->type == C_VIEW - && wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED; + return view && view->type == C_VIEW && (wlc_view_get_state(view->handle) & WLC_BIT_ACTIVATED); } bool swayc_is_parent_of(swayc_t *parent, swayc_t *child) { @@ -723,6 +566,25 @@ int swayc_gap(swayc_t *container) { // Mapping +void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) { + if (container) { + f(container, data); + int i; + if (container->children) { + for (i = 0; i < container->children->length; ++i) { + swayc_t *child = container->children->items[i]; + container_map(child, f, data); + } + } + if (container->floating) { + for (i = 0; i < container->floating->length; ++i) { + swayc_t *child = container->floating->items[i]; + container_map(child, f, data); + } + } + } +} + void update_visibility_output(swayc_t *container, wlc_handle output) { // Inherit visibility swayc_t *parent = container->parent; @@ -791,3 +653,24 @@ void update_visibility(swayc_t *container) { } } +void set_gaps(swayc_t *view, void *_data) { + int *data = _data; + if (!ASSERT_NONNULL(view)) { + return; + } + if (view->type == C_WORKSPACE || view->type == C_VIEW) { + view->gaps = *data; + } +} + +void add_gaps(swayc_t *view, void *_data) { + int *data = _data; + if (!ASSERT_NONNULL(view)) { + return; + } + if (view->type == C_WORKSPACE || view->type == C_VIEW) { + if ((view->gaps += *data) < 0) { + view->gaps = 0; + } + } +} diff --git a/sway/handlers.c b/sway/handlers.c index 1a8138f8..096df53c 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -126,7 +126,7 @@ static void handle_output_destroyed(wlc_handle output) { static void handle_output_resolution_change(wlc_handle output, const struct wlc_size *from, const struct wlc_size *to) { sway_log(L_DEBUG, "Output %u resolution changed to %d x %d", (unsigned int)output, to->w, to->h); - swayc_t *c = swayc_by_test(test_handle, &output); + swayc_t *c = swayc_by_handle(output); if (!c) return; c->width = to->w; c->height = to->h; @@ -134,7 +134,7 @@ static void handle_output_resolution_change(wlc_handle output, const struct wlc_ } static void handle_output_focused(wlc_handle output, bool focus) { - swayc_t *c = swayc_by_test(test_handle, &output); + swayc_t *c = swayc_by_handle(output); // if for some reason this output doesnt exist, create it. if (!c) { handle_output_created(output); @@ -152,7 +152,7 @@ static bool handle_view_created(wlc_handle handle) { // Get parent container, to add view in if (parent) { - focused = swayc_by_test(test_handle, &parent); + focused = swayc_by_handle(parent); } if (!focused || focused->type == C_OUTPUT) { focused = get_focused_container(&root_container); @@ -221,7 +221,7 @@ static bool handle_view_created(wlc_handle handle) { static void handle_view_destroyed(wlc_handle handle) { sway_log(L_DEBUG, "Destroying window %lu", handle); - swayc_t *view = swayc_by_test(test_handle, &handle); + swayc_t *view = swayc_by_handle(handle); // destroy views by type switch (wlc_view_get_type(handle)) { @@ -258,7 +258,7 @@ static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geo // If the view is floating, then apply the geometry. // Otherwise save the desired width/height for the view. // This will not do anything for the time being as WLC improperly sends geometry requests - swayc_t *view = swayc_by_test(test_handle, &handle); + swayc_t *view = swayc_by_handle(handle); if (view) { view->desired_width = geometry->size.w; view->desired_height = geometry->size.h; @@ -274,7 +274,7 @@ static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geo } static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) { - swayc_t *c = swayc_by_test(test_handle, &view); + swayc_t *c = swayc_by_handle(view); switch (state) { case WLC_BIT_FULLSCREEN: // i3 just lets it become fullscreen diff --git a/sway/ipc.c b/sway/ipc.c index 86518bc6..abf2ed0c 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -207,7 +207,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_WORKSPACES: { json_object *workspaces = json_object_new_array(); - swayc_map(ipc_get_workspaces_callback, workspaces); + container_map(&root_container, ipc_get_workspaces_callback, workspaces); const char *json_string = json_object_to_json_string(workspaces); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(workspaces); // free @@ -216,7 +216,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_OUTPUTS: { json_object *outputs = json_object_new_array(); - swayc_map(ipc_get_outputs_callback, outputs); + container_map(&root_container, ipc_get_outputs_callback, outputs); const char *json_string = json_object_to_json_string(outputs); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); json_object_put(outputs); // free diff --git a/sway/stringop.c b/sway/stringop.c index 54faf74c..191e40c8 100644 --- a/sway/stringop.c +++ b/sway/stringop.c @@ -258,7 +258,7 @@ int unescape_string(char *string) { return len - shift; } -char *join_args(int argc, char **argv) { +char *join_args(char **argv, int argc) { int len = 0, i; for (i = 0; i < argc; ++i) { len += strlen(argv[i]) + 1; diff --git a/sway/workspace.c b/sway/workspace.c index 80141f71..658f79bc 100644 --- a/sway/workspace.c +++ b/sway/workspace.c @@ -15,26 +15,7 @@ char *prev_workspace_name = NULL; -static swayc_t *workspace_by_name_only(const char *name); - -const char *workspace_output_open_name(swayc_t *output) { - struct workspace_output *wsop; - int i, len = config->workspace_outputs->length; - // Search config for output - for (i = 0; i < len; ++i) { - wsop = config->workspace_outputs->items[i]; - // Find matching outputs - if (strcasecmp(wsop->output, output->name)) { - // Check if workspace is available and use that name - if (!workspace_by_name(wsop->workspace)) { - return wsop->workspace; - } - } - } - return NULL; -} - -const char *workspace_next_name(void) { +char *workspace_next_name(void) { sway_log(L_DEBUG, "Workspace: Generating new name"); int i; int l = 1; @@ -44,29 +25,42 @@ const char *workspace_next_name(void) { for (i = 0; i < mode->bindings->length; ++i) { struct sway_binding *binding = mode->bindings->items[i]; - const char *command = binding->command; - const char *ws = "workspace"; - const int wslen = sizeof("workspace") - 1; - if (strncmp(ws, command, wslen) == 0) { - command += wslen; - // Skip whitespace - command += strspn(command, whitespace); - // make sure its not a special command - if (strcmp(command, "next") == 0 - || strcmp(command, "prev") == 0 - || strcmp(command, "next_on_output") == 0 - || strcmp(command, "prev_on_output") == 0 - || strcmp(command, "number") == 0 - || strcmp(command, "back_and_forth") == 0 - || strcmp(command, "current") == 0 - // Or if it already exists - || workspace_by_name_only(command)) { + const char* command = binding->command; + list_t *args = split_string(command, " "); + + if (strcmp("workspace", args->items[0]) == 0 && args->length > 1) { + sway_log(L_DEBUG, "Got valid workspace command for target: '%s'", (char *)args->items[1]); + char* target = malloc(strlen(args->items[1]) + 1); + strcpy(target, args->items[1]); + while (*target == ' ' || *target == '\t') + target++; + + // Make sure that the command references an actual workspace + // not a command about workspaces + if (strcmp(target, "next") == 0 || + strcmp(target, "prev") == 0 || + strcmp(target, "next_on_output") == 0 || + strcmp(target, "prev_on_output") == 0 || + strcmp(target, "number") == 0 || + strcmp(target, "back_and_forth") == 0 || + strcmp(target, "current") == 0) + { + free_flat_list(args); continue; - } else { - // otherwise we found it - return command; } + + // Make sure that the workspace doesn't already exist + if (workspace_by_name(target)) { + free_flat_list(args); + continue; + } + + free_flat_list(args); + + sway_log(L_DEBUG, "Workspace: Found free name %s", target); + return target; } + free_flat_list(args); } // As a fall back, get the current number of active workspaces // and return that + 1 for the next workspace's name @@ -81,40 +75,55 @@ const char *workspace_next_name(void) { return name; } -swayc_t *workspace_by_name_only(const char *name) { - int i, len = root_container.children->length; - for (i = 0; i < len; ++i) { - swayc_t *op = root_container.children->items[i]; - int i, len = op->children->length; - for (i = 0; i < len; ++i) { - swayc_t *ws = op->children->items[i]; - if (strcasecmp(ws->name, name) == 0) { - return ws; +swayc_t *workspace_create(const char* name) { + swayc_t *parent; + // Search for workspace<->output pair + int i, e = config->workspace_outputs->length; + for (i = 0; i < e; ++i) { + struct workspace_output *wso = config->workspace_outputs->items[i]; + if (strcasecmp(wso->workspace, name) == 0) + { + // Find output to use if it exists + e = root_container.children->length; + for (i = 0; i < e; ++i) { + parent = root_container.children->items[i]; + if (strcmp(parent->name, wso->output) == 0) { + return new_workspace(parent, name); + } } + break; } } - return NULL; + // Otherwise create a new one + parent = get_focused_container(&root_container); + parent = swayc_parent_by_type(parent, C_OUTPUT); + return new_workspace(parent, name); +} + +static bool _workspace_by_name(swayc_t *view, void *data) { + return (view->type == C_WORKSPACE) && + (strcasecmp(view->name, (char *) data) == 0); } swayc_t *workspace_by_name(const char* name) { if (strcmp(name, "prev") == 0) { return workspace_prev(); - } else if (!strcmp(name, "prev_on_output")) { + } + else if (strcmp(name, "prev_on_output") == 0) { return workspace_output_prev(); - } else if (!strcmp(name, "next")) { + } + else if (strcmp(name, "next") == 0) { return workspace_next(); - } else if (!strcmp(name, "next_on_output")) { + } + else if (strcmp(name, "next_on_output") == 0) { return workspace_output_next(); - } else if (!strcmp(name, "current")) { + } + else if (strcmp(name, "current") == 0) { return swayc_active_workspace(); - } else if (!strcmp(name, "back_and_forth")) { - if (prev_workspace_name) { - name = prev_workspace_name; - } else { // If there is no prev workspace name. just return current - return swayc_active_workspace(); - } } - return workspace_by_name_only(name); + else { + return swayc_by_test(&root_container, _workspace_by_name, (void *) name); + } } /** @@ -172,19 +181,19 @@ swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) { return NULL; } -swayc_t *workspace_output_next(void) { +swayc_t *workspace_output_next() { return workspace_output_prev_next_impl(swayc_active_output(), true); } -swayc_t *workspace_next(void) { +swayc_t *workspace_next() { return workspace_prev_next_impl(swayc_active_workspace(), true); } -swayc_t *workspace_output_prev(void) { +swayc_t *workspace_output_prev() { return workspace_output_prev_next_impl(swayc_active_output(), false); } -swayc_t *workspace_prev(void) { +swayc_t *workspace_prev() { return workspace_prev_next_impl(swayc_active_workspace(), false); } @@ -193,17 +202,17 @@ void workspace_switch(swayc_t *workspace) { return; } swayc_t *active_ws = swayc_active_workspace(); - // set workspace to prev_workspace - if (config->auto_back_and_forth && active_ws == workspace) { - workspace = new_workspace(NULL, "back_and_forth"); + if (config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) { + swayc_t *new_ws = workspace_by_name(prev_workspace_name); + workspace = new_ws ? new_ws : workspace_create(prev_workspace_name); } - // set prev workspace name if (!prev_workspace_name || (strcmp(prev_workspace_name, active_ws->name) && active_ws != workspace)) { free(prev_workspace_name); - prev_workspace_name = strdup(active_ws->name); + prev_workspace_name = malloc(strlen(active_ws->name)+1); + strcpy(prev_workspace_name, active_ws->name); } sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name);