diff --git a/.gitignore b/.gitignore index b7b3266c..cf23d168 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ bin/ test/ build/ .lvimrc +config-debug diff --git a/CMakeLists.txt b/CMakeLists.txt index ba2f8be3..d190cd8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,6 @@ project(sway C) set(CMAKE_C_FLAGS "-g") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin/") add_definitions("-Wall") -set(CMAKE_BUILD_TYPE Debug) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMake) find_package(XKBCommon REQUIRED) diff --git a/include/config.h b/include/config.h index 9243bf35..c23c3509 100644 --- a/include/config.h +++ b/include/config.h @@ -41,9 +41,12 @@ struct sway_config { bool active; bool failed; bool reloading; + + int gaps_inner; + int gaps_outer; }; -bool load_config(void); +bool load_config(const char *file); bool read_config(FILE *file, bool is_active); char *do_var_replacement(struct sway_config *config, char *str); diff --git a/include/container.h b/include/container.h index 63529e44..79e023fe 100644 --- a/include/container.h +++ b/include/container.h @@ -11,7 +11,7 @@ enum swayc_types{ C_WORKSPACE, C_CONTAINER, C_VIEW, - //Keep last + // Keep last C_TYPES, }; @@ -23,7 +23,7 @@ enum swayc_layouts{ L_STACKED, L_TABBED, L_FLOATING, - //Keep last + // Keep last L_LAYOUTS, }; @@ -45,10 +45,10 @@ struct sway_container { bool is_floating; bool is_focused; - int weight; - char *name; + int gaps; + list_t *children; list_t *floating; @@ -56,6 +56,7 @@ struct sway_container { struct sway_container *focused; }; +// Container Creation swayc_t *new_output(wlc_handle handle); swayc_t *new_workspace(swayc_t *output, const char *name); @@ -66,18 +67,29 @@ 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 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 Lookup + +swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types); +swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts); + swayc_t *find_container(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data); void container_map(swayc_t *, void (*f)(swayc_t *, void *), void *); // Mappings void set_view_visibility(swayc_t *view, void *data); +void reset_gaps(swayc_t *view, void *data); #endif diff --git a/include/focus.h b/include/focus.h index 410ed134..383993fa 100644 --- a/include/focus.h +++ b/include/focus.h @@ -1,7 +1,5 @@ #ifndef _SWAY_FOCUS_H #define _SWAY_FOCUS_H -#include "container.h" - enum movement_direction { MOVE_LEFT, MOVE_RIGHT, @@ -10,6 +8,8 @@ enum movement_direction { MOVE_PARENT }; +#include "container.h" + // focused_container - the container found by following the `focused` pointer // from a given container to a container with `is_focused` boolean set // --- diff --git a/include/input_state.h b/include/input_state.h new file mode 100644 index 00000000..782b4b19 --- /dev/null +++ b/include/input_state.h @@ -0,0 +1,49 @@ +#ifndef _SWAY_KEY_STATE_H +#define _SWAY_KEY_STATE_H +#include +#include +#include "container.h" + +/* Keyboard state */ + +typedef uint32_t keycode; + +// returns true if key has been pressed, otherwise false +bool check_key(keycode key); + +// sets a key as pressed +void press_key(keycode key); + +// unsets a key as pressed +void release_key(keycode key); + +/* Pointer state */ + +enum pointer_values { + M_LEFT_CLICK = 272, + M_RIGHT_CLICK = 273, + M_SCROLL_CLICK = 274, + M_SCROLL_UP = 275, + M_SCROLL_DOWN = 276, +}; + +extern struct pointer_state { + bool l_held; + bool r_held; + struct pointer_floating { + bool drag; + bool resize; + } floating; + struct pointer_lock { + bool left; + bool right; + bool top; + bool bottom; + } lock; +} pointer_state; + +void start_floating(swayc_t *view); +void reset_floating(swayc_t *view); + +#endif + diff --git a/include/ipc.h b/include/ipc.h new file mode 100644 index 00000000..0b6441f6 --- /dev/null +++ b/include/ipc.h @@ -0,0 +1,18 @@ +#ifndef _SWAY_IPC_H +#define _SWAY_IPC_H + +enum ipc_command_type { + IPC_COMMAND = 0, + IPC_GET_WORKSPACES = 1, + IPC_SUBSCRIBE = 2, + IPC_GET_OUTPUTS = 3, + IPC_GET_TREE = 4, + IPC_GET_MARKS = 5, + IPC_GET_BAR_CONFIG = 6, + IPC_GET_VERSION = 7, +}; + +void ipc_init(void); +void ipc_terminate(void); + +#endif diff --git a/include/layout.h b/include/layout.h index 79241698..c1d7d8b4 100644 --- a/include/layout.h +++ b/include/layout.h @@ -4,12 +4,14 @@ #include #include "list.h" #include "container.h" +#include "focus.h" extern swayc_t root_container; void init_layout(void); void add_child(swayc_t *parent, swayc_t *child); +void add_floating(swayc_t *ws, swayc_t *child); // Returns parent container which needs to be rearranged. swayc_t *add_sibling(swayc_t *sibling, swayc_t *child); swayc_t *replace_child(swayc_t *child, swayc_t *new_child); @@ -28,5 +30,6 @@ void focus_view_for(swayc_t *ancestor, swayc_t *container); swayc_t *get_focused_container(swayc_t *parent); swayc_t *get_swayc_for_handle(wlc_handle handle, swayc_t *parent); +swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir); #endif diff --git a/include/log.h b/include/log.h index d35b2a54..47a83321 100644 --- a/include/log.h +++ b/include/log.h @@ -1,5 +1,7 @@ #ifndef _SWAY_LOG_H #define _SWAY_LOG_H +#include +#include "container.h" typedef enum { L_SILENT = 0, @@ -10,7 +12,10 @@ typedef enum { void init_log(int verbosity); void sway_log_colors(int mode); -void sway_log(int verbosity, char* format, ...) __attribute__((format(printf,2,3))); -void sway_abort(char* format, ...)__attribute__((format(printf,1,2))); +void sway_log(int verbosity, const char* format, ...) __attribute__((format(printf,2,3))); +void sway_log_errno(int verbosity, char* format, ...) __attribute__((format(printf,2,3))); +void sway_abort(const char* format, ...) __attribute__((format(printf,1,2))); +bool sway_assert(bool condition, const char* format, ...) __attribute__((format(printf,2,3))); +void layout_log(const swayc_t *c, int depth); #endif diff --git a/include/stringop.h b/include/stringop.h index a5346829..4300f9ed 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -10,5 +10,6 @@ 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(char **argv, int argc); +char *join_list(list_t *list, char *separator); #endif diff --git a/include/sway.h b/include/sway.h new file mode 100644 index 00000000..6499c81d --- /dev/null +++ b/include/sway.h @@ -0,0 +1,6 @@ +#ifndef _SWAY_SWAY_H +#define _SWAY_SWAY_H + +void sway_terminate(void); + +#endif diff --git a/include/workspace.h b/include/workspace.h index 8ce39bbd..042a15d9 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -15,6 +15,5 @@ void workspace_output_next(); void workspace_next(); void workspace_output_prev(); void workspace_prev(); -void layout_log(const swayc_t *c, int depth); #endif diff --git a/sway.5.txt b/sway.5.txt index 9f92dfe8..5bccbd12 100644 --- a/sway.5.txt +++ b/sway.5.txt @@ -22,11 +22,11 @@ Commands -------- **bindsym** :: - Binds _key combo_ to execute _command_ when pressed. You may use XKB key names - here (**xev**(1) is a good tool for discovering them). An example bindsym - command would be _bindsym Mod1+Shift+f exec firefox_, which would execute - Firefox if the alt, shift, and F keys are pressed together. Any valid sway - command is eligible to be bound to a key combo. + Binds _key combo_ to execute _command_ when pressed. You may use XKB key + names here (**xev**(1) is a good tool for discovering them). An example + bindsym command would be _bindsym Mod1+Shift+f exec firefox_, which would + execute Firefox if the alt, shift, and F keys are pressed together. Any + valid sway command is eligible to be bound to a key combo. **exec** :: Executes _shell command_ with sh. @@ -48,6 +48,9 @@ Commands container, which is useful, for example, to open a sibling of the parent container, or to move the entire container around. +**focus** mode_toggle:: + Toggles focus between floating view and tiled view. + **focus_follows_mouse** :: If set to _yes_, the currently focused view will change as you move your mouse around the screen to the view that ends up underneath your mouse. @@ -76,13 +79,24 @@ Commands **splitv**:: Equivalent to **split vertical**. -**fullscreen**: +**floating_modifier** :: + When the _modifier_ key is held down, you may use left click to drag floating + windows, and right click to resize them. + +**fullscreen**:: Toggles fullscreen status for the focused view. -**workspace** : +**gaps** :: + Adds _amount_ pixels between each view, and around each output. + +**gaps** :: + Adds _amount_ pixels as an _inner_ or _outer_ gap, where the former affects + spacing between views and the latter affects the space around each output. + +**workspace** :: Switches to the specified workspace. -**workspace** : +**workspace** :: Switches to the next workspace on the current output. **workspace** output :: diff --git a/sway/commands.c b/sway/commands.c index 1ca5c17f..e39b781a 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include "commands.h" #include "container.h" #include "handlers.h" +#include "sway.h" struct modifier_key { char *name; @@ -75,6 +77,18 @@ static bool checkarg(int argc, char *name, enum expected_args type, int val) { return false; } +static int bindsym_sort(const void *_lbind, const void *_rbind) { + const struct sway_binding *lbind = *(void **)_lbind; + const struct sway_binding *rbind = *(void **)_rbind; + unsigned int lmod = 0, rmod = 0, i; + + // Count how any modifiers are pressed + for (i = 0; i < 8 * sizeof(lbind->modifiers); ++i) { + lmod += lbind->modifiers & 1 << i; + rmod += rbind->modifiers & 1 << i; + } + return (rbind->keys->length + rmod) - (lbind->keys->length + lmod); +} static bool cmd_bindsym(struct sway_config *config, int argc, char **argv) { if (!checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1)) { @@ -104,6 +118,10 @@ static bool cmd_bindsym(struct sway_config *config, int argc, char **argv) { xkb_keysym_t sym = xkb_keysym_from_name(split->items[i], XKB_KEYSYM_CASE_INSENSITIVE); if (!sym) { sway_log(L_ERROR, "bindsym - unknown key %s", (char *)split->items[i]); + list_free(binding->keys); + free(binding->command); + free(binding); + list_free(split); return false; } xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t)); @@ -113,7 +131,10 @@ static bool cmd_bindsym(struct sway_config *config, int argc, char **argv) { list_free(split); // TODO: Check if there are other commands with this key binding - list_add(config->current_mode->bindings, binding); + struct sway_mode *mode = config->current_mode; + list_add(mode->bindings, binding); + qsort(mode->bindings->items, mode->bindings->length, + sizeof(mode->bindings->items[0]), bindsym_sort); sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command); return true; @@ -166,7 +187,7 @@ static bool cmd_exit(struct sway_config *config, int argc, char **argv) { } // Close all views container_map(&root_container, kill_views, NULL); - exit(0); + sway_terminate(); return true; } @@ -181,43 +202,28 @@ static bool cmd_floating(struct sway_config *config, int argc, char **argv) { if (view->type != C_VIEW) { return true; } - int i; // Change from nonfloating to floating if (!view->is_floating) { - view->is_floating = true; - for (i = 0; i < view->parent->children->length; i++) { - if (view->parent->children->items[i] == view) { - // Try to use desired geometry to set w/h - if (view->desired_width != -1) { - view->width = view->desired_width; - } - if (view->desired_height != -1) { - view->height = view->desired_height; - } - - // Swap from the list of whatever container the view was in - // to the workspace->floating list - list_del(view->parent->children, i); - list_add(active_workspace->floating, view); - destroy_container(view->parent); - - // Set the new position of the container and arrange windows - view->x = (active_workspace->width - view->width)/2; - view->y = (active_workspace->height - view->height)/2; - sway_log(L_INFO, "Setting container %p to floating at coordinates X:%d Y:%d, W:%d, H:%d", view, view->x, view->y, view->width, view->height); - // Change parent to active_workspace - view->parent = active_workspace; - arrange_windows(active_workspace, -1, -1); - return true; - } + // Remove view from its current location + destroy_container(remove_child(view)); + + // and move it into workspace floating + add_floating(active_workspace,view); + view->x = (active_workspace->width - view->width)/2; + view->y = (active_workspace->height - view->height)/2; + if (view->desired_width != -1) { + view->width = view->desired_width; } + if (view->desired_height != -1) { + view->height = view->desired_height; + } + arrange_windows(active_workspace, -1, -1); } else { // Delete the view from the floating list and unset its is_floating flag // Using length-1 as the index is safe because the view must be the currently // focused floating output - list_del(active_workspace->floating, active_workspace->floating->length - 1); + remove_child(view); view->is_floating = false; - active_workspace->focused = NULL; // Get the properly focused container, and add in the view there swayc_t *focused = container_under_pointer(); // If focused is null, it's because the currently focused container is a workspace @@ -228,21 +234,20 @@ static bool cmd_floating(struct sway_config *config, int argc, char **argv) { sway_log(L_DEBUG, "Non-floating focused container is %p", focused); - //Case of focused workspace, just create as child of it + // Case of focused workspace, just create as child of it if (focused->type == C_WORKSPACE) { add_child(focused, view); } - //Regular case, create as sibling of current container + // Regular case, create as sibling of current container else { add_sibling(focused, view); } // Refocus on the view once its been put back into the layout - set_focused_container(view); + view->width = view->height = 0; arrange_windows(active_workspace, -1, -1); - return true; } + set_focused_container(view); } - return true; } @@ -250,11 +255,29 @@ static bool cmd_floating_mod(struct sway_config *config, int argc, char **argv) if (!checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1)) { return false; } - config->floating_mod = xkb_keysym_from_name(argv[0], XKB_KEYSYM_CASE_INSENSITIVE); + int i, j; + list_t *split = split_string(argv[0], "+"); + config->floating_mod = 0; + + // set modifer keys + for (i = 0; i < split->length; ++i) { + for (j = 0; j < sizeof(modifiers) / sizeof(struct modifier_key); ++j) { + if (strcasecmp(modifiers[j].name, split->items[i]) == 0) { + config->floating_mod |= modifiers[j].mod; + } + } + } + list_free(split); + if (!config->floating_mod) { + sway_log(L_ERROR, "bindsym - unknown keys %s", argv[0]); + return false; + } return true; } static bool cmd_focus(struct sway_config *config, int argc, char **argv) { + static int floating_toggled_index = 0; + static int tiled_toggled_index = 0; if (!checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1)) { return false; } @@ -268,7 +291,44 @@ static bool cmd_focus(struct sway_config *config, int argc, char **argv) { return move_focus(MOVE_DOWN); } else if (strcasecmp(argv[0], "parent") == 0) { return move_focus(MOVE_PARENT); + } else if (strcasecmp(argv[0], "mode_toggle") == 0) { + int i; + swayc_t *focused = get_focused_view(active_workspace); + if (focused->is_floating) { + if (active_workspace->children->length > 0) { + for (i = 0;i < active_workspace->floating->length; i++) { + if (active_workspace->floating->items[i] == focused) { + floating_toggled_index = i; + break; + } + } + if (active_workspace->children->length > tiled_toggled_index) { + set_focused_container(get_focused_view(active_workspace->children->items[tiled_toggled_index])); + } else { + set_focused_container(get_focused_view(active_workspace->children->items[0])); + tiled_toggled_index = 0; + } + } + } else { + if (active_workspace->floating->length > 0) { + for (i = 0;i < active_workspace->children->length; i++) { + if (active_workspace->children->items[i] == focused) { + tiled_toggled_index = i; + break; + } + } + if (active_workspace->floating->length > floating_toggled_index) { + swayc_t *floating = active_workspace->floating->items[floating_toggled_index]; + set_focused_container(get_focused_view(floating)); + } else { + swayc_t *floating = active_workspace->floating->items[active_workspace->floating->length - 1]; + set_focused_container(get_focused_view(floating)); + tiled_toggled_index = active_workspace->floating->length - 1; + } + } + } } + return true; } @@ -303,6 +363,42 @@ static bool cmd_move(struct sway_config *config, int argc, char **argv) { return true; +static bool cmd_gaps(struct sway_config *config, int argc, char **argv) { + if (!checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1)) { + return false; + } + + if (argc == 1) { + char *end; + int amount = (int)strtol(argv[0], &end, 10); + if (errno == ERANGE || amount == 0) { + errno = 0; + return false; + } + if (config->gaps_inner == 0) { + config->gaps_inner = amount; + } + if (config->gaps_outer == 0) { + config->gaps_outer = amount; + } + } else if (argc == 2) { + char *end; + int amount = (int)strtol(argv[1], &end, 10); + if (errno == ERANGE || amount == 0) { + errno = 0; + return false; + } + if (strcasecmp(argv[0], "inner") == 0) { + config->gaps_inner = amount; + } else if (strcasecmp(argv[0], "outer") == 0) { + config->gaps_outer = amount; + } else { + return false; + } + } else { + return false; + } + return true; } static bool cmd_kill(struct sway_config *config, int argc, char **argv) { @@ -316,7 +412,6 @@ static bool cmd_layout(struct sway_config *config, int argc, char **argv) { return false; } swayc_t *parent = get_focused_container(&root_container); - while (parent->type == C_VIEW) { parent = parent->parent; } @@ -341,7 +436,7 @@ static bool cmd_reload(struct sway_config *config, int argc, char **argv) { if (!checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0)) { return false; } - if (!load_config()) { + if (!load_config(NULL)) { // TODO: Use config given from -c return false; } arrange_windows(&root_container, -1, -1); @@ -435,14 +530,12 @@ static bool cmd_fullscreen(struct sway_config *config, int argc, char **argv) { swayc_t *container = get_focused_view(&root_container); bool current = (wlc_view_get_state(container->handle) & WLC_BIT_FULLSCREEN) > 0; wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current); - //Resize workspace if going from fullscreen -> notfullscreen - //otherwise just resize container + // Resize workspace if going from fullscreen -> notfullscreen + // otherwise just resize container if (current) { - while (container->type != C_WORKSPACE) { - container = container->parent; - } + container = swayc_parent_by_type(container, C_WORKSPACE); } - //Only resize container when going into fullscreen + // Only resize container when going into fullscreen arrange_windows(container, -1, -1); return true; @@ -508,6 +601,7 @@ static struct cmd_handler handlers[] = { { "focus", cmd_focus }, { "focus_follows_mouse", cmd_focus_follows_mouse }, { "fullscreen", cmd_fullscreen }, + { "gaps", cmd_gaps }, { "kill", cmd_kill }, { "layout", cmd_layout }, { "log_colors", cmd_log_colors }, @@ -606,7 +700,7 @@ bool handle_command(struct sway_config *config, char *exec) { char **argv = split_directive(exec + strlen(handler->command), &argc); int i; - //Perform var subs on all parts of the command + // Perform var subs on all parts of the command for (i = 0; i < argc; ++i) { argv[i] = do_var_replacement(config, argv[i]); } diff --git a/sway/config.c b/sway/config.c index f1de6080..9f65e8a2 100644 --- a/sway/config.c +++ b/sway/config.c @@ -8,6 +8,7 @@ #include "log.h" #include "commands.h" #include "config.h" +#include "layout.h" struct sway_config *config; @@ -15,121 +16,143 @@ static bool exists(const char *path) { return access(path, R_OK) != -1; } +void config_defaults(struct sway_config *config) { + config->symbols = create_list(); + config->modes = create_list(); + config->cmd_queue = create_list(); + config->workspace_outputs = create_list(); + config->current_mode = malloc(sizeof(struct sway_mode)); + config->current_mode->name = NULL; + config->current_mode->bindings = create_list(); + list_add(config->modes, config->current_mode); + // Flags + config->focus_follows_mouse = true; + config->mouse_warping = true; + config->reloading = false; + config->active = false; + config->failed = false; + config->gaps_inner = 0; + config->gaps_outer = 0; +} + +void free_mode(struct sway_mode *mode) { + free(mode->name); + free_flat_list(mode->bindings); +} + +void free_config(struct sway_config *config) { + int i; + for (i = 0; i < config->modes->length; ++i) { + free_mode((struct sway_mode *)config->modes->items[i]); + } + free_flat_list(config->modes); + for (i = 0; i < config->workspace_outputs->length; ++i) { + struct workspace_output *wso = config->workspace_outputs->items[i]; + free(wso->output); + free(wso->workspace); + } + free_flat_list(config->workspace_outputs); + free_flat_list(config->cmd_queue); + for (i = 0; i < config->symbols->length; ++i) { + struct sway_variable *sym = config->symbols->items[i]; + free(sym->name); + free(sym->value); + } + free_flat_list(config->symbols); +} + +static const char *search_paths[] = { + "$home/.sway/config", + "$config/sway/config", + "/etc/sway/config", + "$home/.i3/config", + "$config/.i3/config", + "/etc/i3/config" +}; + static char *get_config_path() { - char *name = "/.sway/config"; - const char *home = getenv("HOME"); - - // Check home dir - sway_log(L_DEBUG, "Trying to find config in ~/.sway/config"); - char *temp = malloc(strlen(home) + strlen(name) + 1); - strcpy(temp, home); - strcat(temp, name); - if (exists(temp)) { - return temp; + char *home = getenv("HOME"); + if (home) { + home = strdup(getenv("HOME")); } - - // Check XDG_CONFIG_HOME with fallback to ~/.config/ - sway_log(L_DEBUG, "Trying to find config in XDG_CONFIG_HOME/sway/config"); - char *xdg_config_home = getenv("XDG_CONFIG_HOME"); - if (xdg_config_home == NULL) { - sway_log(L_DEBUG, "Falling back to ~/.config/sway/config"); - name = "/.config/sway/config"; - temp = malloc(strlen(home) + strlen(name) + 1); - strcpy(temp, home); - strcat(temp, name); + char *config = getenv("XDG_CONFIG_HOME"); + if (config) { + config = strdup(getenv("XDG_CONFIG_HOME")); + } else if (home) { + const char *def = "/.config"; + config = malloc(strlen(home) + strlen(def) + 1); + strcpy(config, home); + strcat(config, def); } else { - name = "/sway/config"; - temp = malloc(strlen(xdg_config_home) + strlen(name) + 1); - strcpy(xdg_config_home, home); - strcat(temp, name); - } - if (exists(temp)) { - return temp; + home = strdup(""); + config = strdup(""); } - // Check /etc/ - sway_log(L_DEBUG, "Trying to find config in /etc/sway/config"); - strcpy(temp, "/etc/sway/config"); - if (exists(temp)) { - return temp; + // Set up a temporary config for holding set variables + struct sway_config *temp_config = malloc(sizeof(struct sway_config)); + config_defaults(temp_config); + const char *set_home = "set $home "; + char *_home = malloc(strlen(home) + strlen(set_home) + 1); + strcpy(_home, set_home); + strcat(_home, home); + handle_command(temp_config, _home); + free(_home); + const char *set_config = "set $config "; + char *_config = malloc(strlen(config) + strlen(set_config) + 1); + strcpy(_config, set_config); + strcat(_config, config); + handle_command(temp_config, _config); + free(_config); + + char *test = NULL; + int i; + for (i = 0; i < sizeof(search_paths) / sizeof(char *); ++i) { + test = strdup(search_paths[i]); + test = do_var_replacement(temp_config, test); + sway_log(L_DEBUG, "Checking for config at %s", test); + if (exists(test)) { + goto _continue; + } + free(test); + test = NULL; } - // Check XDG_CONFIG_DIRS sway_log(L_DEBUG, "Trying to find config in XDG_CONFIG_DIRS"); char *xdg_config_dirs = getenv("XDG_CONFIG_DIRS"); if (xdg_config_dirs != NULL) { list_t *paths = split_string(xdg_config_dirs, ":"); - name = "/sway/config"; + char *name = "/sway/config"; int i; for (i = 0; i < paths->length; i++ ) { - temp = malloc(strlen(paths->items[i]) + strlen(name) + 1); - strcpy(temp, paths->items[i]); - strcat(temp, name); - if (exists(temp)) { + test = malloc(strlen(paths->items[i]) + strlen(name) + 1); + strcpy(test, paths->items[i]); + strcat(test, name); + if (exists(test)) { free_flat_list(paths); - return temp; + return test; } + free(test); + test = NULL; } free_flat_list(paths); } - //Now fall back to i3 paths and try the same thing - name = "/.i3/config"; - sway_log(L_DEBUG, "Trying to find config in ~/.i3/config"); - temp = malloc(strlen(home) + strlen(name) + 1); - strcpy(temp, home); - strcat(temp, name); - if (exists(temp)) { - return temp; - } - - sway_log(L_DEBUG, "Trying to find config in XDG_CONFIG_HOME/i3/config"); - if (xdg_config_home == NULL) { - sway_log(L_DEBUG, "Falling back to ~/.config/i3/config"); - name = "/.config/i3/config"; - temp = malloc(strlen(home) + strlen(name) + 1); - strcpy(temp, home); - strcat(temp, name); - } else { - name = "/i3/config"; - temp = malloc(strlen(xdg_config_home) + strlen(name) + 1); - strcpy(xdg_config_home, home); - strcat(temp, name); - } - if (exists(temp)) { - return temp; - } - - sway_log(L_DEBUG, "Trying to find config in /etc/i3/config"); - strcpy(temp, "/etc/i3/config"); - if (exists(temp)) { - return temp; - } - - sway_log(L_DEBUG, "Trying to find config in XDG_CONFIG_DIRS"); - if (xdg_config_dirs != NULL) { - list_t *paths = split_string(xdg_config_dirs, ":"); - name = "/i3/config"; - int i; - for (i = 0; i < paths->length; i++ ) { - temp = malloc(strlen(paths->items[i]) + strlen(name) + 1); - strcpy(temp, paths->items[i]); - strcat(temp, name); - if (exists(temp)) { - free_flat_list(paths); - return temp; - } - } - free_flat_list(paths); - } - - return NULL; +_continue: + free_config(temp_config); + free(home); + free(config); + return test; } -bool load_config(void) { +bool load_config(const char *file) { sway_log(L_INFO, "Loading config"); - char *path = get_config_path(); + char *path; + if (file != NULL) { + path = strdup(file); + } else { + path = get_config_path(); + } if (path == NULL) { sway_log(L_ERROR, "Unable to find a config file!"); @@ -155,23 +178,6 @@ bool load_config(void) { return config_load_success; } -void config_defaults(struct sway_config *config) { - config->symbols = create_list(); - config->modes = create_list(); - config->cmd_queue = create_list(); - config->workspace_outputs = create_list(); - config->current_mode = malloc(sizeof(struct sway_mode)); - config->current_mode->name = NULL; - config->current_mode->bindings = create_list(); - list_add(config->modes, config->current_mode); - // Flags - config->focus_follows_mouse = true; - config->mouse_warping = true; - config->reloading = false; - config->active = false; - config->failed = false; -} - bool read_config(FILE *file, bool is_active) { struct sway_config *temp_config = malloc(sizeof(struct sway_config)); config_defaults(temp_config); @@ -188,8 +194,8 @@ bool read_config(FILE *file, bool is_active) { while (!feof(file)) { int _; char *line = read_line(file); - line = strip_comments(line); line = strip_whitespace(line, &_); + line = strip_comments(line); if (!line[0]) { goto _continue; } @@ -225,6 +231,8 @@ _continue: if (is_active) { temp_config->reloading = false; + container_map(&root_container, reset_gaps, NULL); + arrange_windows(&root_container, -1, -1); } config = temp_config; diff --git a/sway/container.c b/sway/container.c index 62c5bda0..7ccc2e09 100644 --- a/sway/container.c +++ b/sway/container.c @@ -8,53 +8,57 @@ #include "layout.h" #include "log.h" +#define ASSERT_NONNULL(PTR) \ + sway_assert (PTR, "%s: " #PTR "must be non-null", __func__) static swayc_t *new_swayc(enum swayc_types type) { swayc_t *c = calloc(1, sizeof(swayc_t)); c->handle = -1; c->layout = L_NONE; c->type = type; - c->weight = 1; if (type != C_VIEW) { c->children = create_list(); } return c; } -static void free_swayc(swayc_t *c) { +static void free_swayc(swayc_t *cont) { + if (!ASSERT_NONNULL(cont)) { + return; + } // TODO does not properly handle containers with children, // TODO but functions that call this usually check for that - if (c->children) { - if (c->children->length) { + if (cont->children) { + if (cont->children->length) { int i; - for (i = 0; i < c->children->length; ++i) { - free_swayc(c->children->items[i]); + for (i = 0; i < cont->children->length; ++i) { + free_swayc(cont->children->items[i]); } } - list_free(c->children); + list_free(cont->children); } - if (c->floating) { - if (c->floating->length) { + if (cont->floating) { + if (cont->floating->length) { int i; - for (i = 0; i < c->floating->length; ++i) { - free_swayc(c->floating->items[i]); + for (i = 0; i < cont->floating->length; ++i) { + free_swayc(cont->floating->items[i]); } } - list_free(c->floating); + list_free(cont->floating); } - if (c->parent) { - remove_child(c); + if (cont->parent) { + remove_child(cont); } - if (c->name) { - free(c->name); + if (cont->name) { + free(cont->name); } - free(c); + free(cont); } -/* New containers */ +// New containers static bool workspace_test(swayc_t *view, void *name) { - return strcasecmp(view->name, (char *)name); + return strcasecmp(view->name, (char *)name) == 0; } swayc_t *new_output(wlc_handle handle) { @@ -67,6 +71,7 @@ swayc_t *new_output(wlc_handle handle) { output->height = size->h; output->handle = handle; output->name = name ? strdup(name) : NULL; + output->gaps = config->gaps_outer + config->gaps_inner / 2; add_child(&root_container, output); @@ -80,8 +85,10 @@ swayc_t *new_output(wlc_handle handle) { 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 (find_container(&root_container, workspace_test, 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; } @@ -101,10 +108,15 @@ swayc_t *new_output(wlc_handle handle) { } swayc_t *new_workspace(swayc_t *output, const char *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); workspace->layout = L_HORIZ; // TODO: default layout + workspace->x = output->x; + workspace->y = output->y; workspace->width = output->width; workspace->height = output->height; workspace->name = strdup(name); @@ -116,6 +128,9 @@ swayc_t *new_workspace(swayc_t *output, const char *name) { } swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) { + if (!ASSERT_NONNULL(child)) { + return NULL; + } swayc_t *cont = new_swayc(C_CONTAINER); sway_log(L_DEBUG, "creating container %p around %p", cont, child); @@ -147,6 +162,7 @@ swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) { // give them proper layouts cont->layout = workspace->layout; workspace->layout = layout; + set_focused_container_for(workspace, get_focused_view(workspace)); } else { // Or is built around container swayc_t *parent = replace_child(child, cont); if (parent) { @@ -157,6 +173,9 @@ swayc_t *new_container(swayc_t *child, enum swayc_layouts layout) { } swayc_t *new_view(swayc_t *sibling, wlc_handle handle) { + if (!ASSERT_NONNULL(sibling)) { + return NULL; + } const char *title = wlc_view_get_title(handle); swayc_t *view = new_swayc(C_VIEW); sway_log(L_DEBUG, "Adding new view %lu:%s to container %p %d", @@ -166,11 +185,15 @@ swayc_t *new_view(swayc_t *sibling, wlc_handle handle) { view->name = title ? strdup(title) : NULL; view->visible = true; view->is_focused = true; + // Setup geometry + const struct wlc_geometry* geometry = wlc_view_get_geometry(handle); + view->width = 0; + view->height = 0; + view->desired_width = geometry->size.w; + view->desired_height = geometry->size.h; - view->desired_width = -1; - view->desired_height = -1; + view->gaps = config->gaps_inner; - // TODO: properly set this view->is_floating = false; if (sibling->type == C_WORKSPACE) { @@ -196,8 +219,9 @@ swayc_t *new_floating_view(wlc_handle handle) { // Set the geometry of the floating view const struct wlc_geometry* geometry = wlc_view_get_geometry(handle); - view->x = geometry->origin.x; - view->y = geometry->origin.y; + // give it requested geometry, but place in center + view->x = (active_workspace->width - geometry->size.w) / 2; + view->y = (active_workspace->height- geometry->size.h) / 2; view->width = geometry->size.w; view->height = geometry->size.h; @@ -215,9 +239,12 @@ swayc_t *new_floating_view(wlc_handle handle) { return view; } -/* Destroy container */ +// Destroy container swayc_t *destroy_output(swayc_t *output) { + if (!ASSERT_NONNULL(output)) { + return NULL; + } if (output->children->length == 0) { // TODO move workspaces to other outputs } @@ -227,23 +254,21 @@ swayc_t *destroy_output(swayc_t *output) { } swayc_t *destroy_workspace(swayc_t *workspace) { + if (!ASSERT_NONNULL(workspace)) { + return NULL; + } // NOTE: This is called from elsewhere without checking children length // TODO move containers to other workspaces? // for now just dont delete // Do not destroy this if it's the last workspace on this output - swayc_t *output = workspace->parent; - while (output && output->type != C_OUTPUT) { - output = output->parent; - } - if (output) { - if (output->children->length == 1) { - return NULL; - } + swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT); + if (output && output->children->length == 1) { + return NULL; } if (workspace->children->length == 0) { - sway_log(L_DEBUG, "Workspace: Destroying workspace '%s'", workspace->name); + sway_log(L_DEBUG, "%s: '%s'", __func__, workspace->name); swayc_t *parent = workspace->parent; free_swayc(workspace); return parent; @@ -252,19 +277,20 @@ swayc_t *destroy_workspace(swayc_t *workspace) { } swayc_t *destroy_container(swayc_t *container) { + if (!ASSERT_NONNULL(container)) { + return NULL; + } while (container->children->length == 0 && container->type == C_CONTAINER) { sway_log(L_DEBUG, "Container: Destroying container '%p'", container); swayc_t *parent = container->parent; free_swayc(container); - container = parent; } return container; } swayc_t *destroy_view(swayc_t *view) { - if (view == NULL) { - sway_log(L_DEBUG, "Warning: NULL passed into destroy_view"); + if (!ASSERT_NONNULL(view)) { return NULL; } sway_log(L_DEBUG, "Destroying view '%p'", view); @@ -278,6 +304,34 @@ swayc_t *destroy_view(swayc_t *view) { return parent; } +// Container lookup + +swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) { + if (!ASSERT_NONNULL(container)) { + return NULL; + } + if (!sway_assert(type < C_TYPES && type >= C_ROOT, "%s: invalid type", __func__)) { + return NULL; + } + do { + container = container->parent; + } while(container && container->type != type); + return container; +} + +swayc_t *swayc_parent_by_layout(swayc_t *container, enum swayc_layouts layout) { + if (!ASSERT_NONNULL(container)) { + return NULL; + } + if (!sway_assert(layout < L_LAYOUTS && layout >= L_NONE, "%s: invalid layout", __func__)) { + return NULL; + } + do { + container = container->parent; + } while (container && container->layout != layout); + return container; +} + swayc_t *find_container(swayc_t *container, bool (*test)(swayc_t *view, void *data), void *data) { if (!container->children) { return NULL; @@ -307,25 +361,27 @@ swayc_t *find_container(swayc_t *container, bool (*test)(swayc_t *view, void *da } void container_map(swayc_t *container, void (*f)(swayc_t *view, void *data), void *data) { - if (!container || !container->children || !container->children->length) { - return; - } - int i; - for (i = 0; i < container->children->length; ++i) { - swayc_t *child = container->children->items[i]; - f(child, data); - container_map(child, f, data); - } - if (container->type == C_WORKSPACE) { - for (i = 0; i < container->floating->length; ++i) { - swayc_t *child = container->floating->items[i]; + if (container && container->children && container->children->length) { + int i; + for (i = 0; i < container->children->length; ++i) { + swayc_t *child = container->children->items[i]; f(child, data); container_map(child, f, data); } + if (container->type == C_WORKSPACE) { + for (i = 0; i < container->floating->length; ++i) { + swayc_t *child = container->floating->items[i]; + f(child, data); + container_map(child, f, data); + } + } } } void set_view_visibility(swayc_t *view, void *data) { + if (!ASSERT_NONNULL(view)) { + return; + } uint32_t *p = data; if (view->type == C_VIEW) { wlc_view_set_mask(view->handle, *p); @@ -337,3 +393,15 @@ void set_view_visibility(swayc_t *view, void *data) { } view->visible = (*p == 2); } + +void reset_gaps(swayc_t *view, void *data) { + if (!ASSERT_NONNULL(view)) { + return; + } + if (view->type == C_OUTPUT) { + view->gaps = config->gaps_outer; + } + if (view->type == C_VIEW) { + view->gaps = config->gaps_inner; + } +} diff --git a/sway/focus.c b/sway/focus.c index 628316dd..5008dbbf 100644 --- a/sway/focus.c +++ b/sway/focus.c @@ -3,6 +3,7 @@ #include "focus.h" #include "log.h" #include "workspace.h" +#include "layout.h" bool locked_container_focus = false; bool locked_view_focus = false; @@ -20,6 +21,8 @@ static void update_focus(swayc_t *c) { // Case where output changes case C_OUTPUT: wlc_output_focus(c->handle); + // Set new workspace to the outputs focused workspace + active_workspace = c->focused; break; // Case where workspace changes @@ -51,74 +54,17 @@ static void update_focus(swayc_t *c) { } bool move_focus(enum movement_direction direction) { - if (locked_container_focus) { - return false; - } - swayc_t *current = get_focused_container(&root_container); - if (current->type == C_VIEW - && wlc_view_get_state(current->handle) & WLC_BIT_FULLSCREEN) { - return false; - } - swayc_t *parent = current->parent; - - if (direction == MOVE_PARENT) { - if (parent->type == C_OUTPUT) { - sway_log(L_DEBUG, "Focus cannot move to parent"); - return false; + swayc_t *view = get_swayc_in_direction( + get_focused_container(&root_container), direction); + if (view) { + if (direction == MOVE_PARENT) { + set_focused_container(view); } else { - sway_log(L_DEBUG, "Moving focus from %p:%ld to %p:%ld", - current, current->handle, parent, parent->handle); - set_focused_container(parent); - return true; - } - } - - while (true) { - sway_log(L_DEBUG, "Moving focus away from %p", current); - - // Test if we can even make a difference here - bool can_move = false; - int diff = 0; - if (direction == MOVE_LEFT || direction == MOVE_RIGHT) { - if (parent->layout == L_HORIZ || parent->type == C_ROOT) { - can_move = true; - diff = direction == MOVE_LEFT ? -1 : 1; - } - } else { - if (parent->layout == L_VERT) { - can_move = true; - diff = direction == MOVE_UP ? -1 : 1; - } - } - sway_log(L_DEBUG, "Can move? %s", can_move ? "yes" : "no"); - if (can_move) { - int i; - for (i = 0; i < parent->children->length; ++i) { - swayc_t *child = parent->children->items[i]; - if (child == current) { - break; - } - } - int desired = i + diff; - sway_log(L_DEBUG, "Moving from %d to %d", i, desired); - if (desired < 0 || desired >= parent->children->length) { - can_move = false; - } else { - swayc_t *newview = parent->children->items[desired]; - set_focused_container(get_focused_view(newview)); - return true; - } - } - if (!can_move) { - sway_log(L_DEBUG, "Can't move at current level, moving up tree"); - current = parent; - parent = parent->parent; - if (!parent) { - // Nothing we can do - return false; - } + set_focused_container(get_focused_view(view)); } + return true; } + return false; } swayc_t *get_focused_container(swayc_t *parent) { @@ -142,13 +88,13 @@ void set_focused_container(swayc_t *c) { // Find previous focused view, and the new focused view, if they are the same return swayc_t *focused = get_focused_view(&root_container); swayc_t *workspace = active_workspace; - if (focused == get_focused_view(c)) { - return; - } // update container focus from here to root, making necessary changes along // the way swayc_t *p = c; + if (p->type != C_OUTPUT && p->type != C_ROOT) { + p->is_focused = true; + } while (p != &root_container) { update_focus(p); p = p->parent; @@ -171,8 +117,11 @@ void set_focused_container(swayc_t *c) { } // activate current focus if (p->type == C_VIEW) { - wlc_view_focus(p->handle); wlc_view_set_state(p->handle, WLC_BIT_ACTIVATED, true); + // set focus if view_focus is unlocked + if (!locked_view_focus) { + wlc_view_focus(p->handle); + } } } } diff --git a/sway/handlers.c b/sway/handlers.c index 534b4e4f..79628fe5 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -13,21 +13,14 @@ #include "workspace.h" #include "container.h" #include "focus.h" - -uint32_t keys_pressed[32]; +#include "input_state.h" static struct wlc_origin mouse_origin; -static bool m1_held = false; -static bool m2_held = false; - static bool pointer_test(swayc_t *view, void *_origin) { const struct wlc_origin *origin = _origin; // Determine the output that the view is under - swayc_t *parent = view; - while (parent->type != C_OUTPUT) { - parent = parent->parent; - } + swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT); if (origin->x >= view->x && origin->y >= view->y && origin->x < view->x + view->width && origin->y < view->y + view->height && view->visible && parent == root_container.focused) { @@ -86,10 +79,12 @@ swayc_t *container_under_pointer(void) { return lookup; } +/* Handles */ + static bool handle_output_created(wlc_handle output) { swayc_t *op = new_output(output); - //Switch to workspace if we need to + // Switch to workspace if we need to if (active_workspace == NULL) { swayc_t *ws = op->children->items[0]; workspace_switch(ws); @@ -111,7 +106,7 @@ static void handle_output_destroyed(wlc_handle output) { if (list->length == 0) { active_workspace = NULL; } else { - //switch to other outputs active workspace + // switch to other outputs active workspace workspace_switch(((swayc_t *)root_container.children->items[0])->focused); } } @@ -137,38 +132,64 @@ static void handle_output_focused(wlc_handle output, bool focus) { } static bool handle_view_created(wlc_handle handle) { - swayc_t *focused = get_focused_container(&root_container); + // if view is child of another view, the use that as focused container + wlc_handle parent = wlc_view_get_parent(handle); + swayc_t *focused = NULL; swayc_t *newview = NULL; + + // Get parent container, to add view in + if (parent) { + focused = get_swayc_for_handle(parent, &root_container); + } + if (!focused || focused->type == C_OUTPUT) { + focused = get_focused_container(&root_container); + } + sway_log(L_DEBUG, "handle:%ld type:%x state:%x parent:%ld " + "mask:%d (x:%d y:%d w:%d h:%d) title:%s " + "class:%s appid:%s", + handle, wlc_view_get_type(handle), wlc_view_get_state(handle), parent, + wlc_view_get_mask(handle), wlc_view_get_geometry(handle)->origin.x, + wlc_view_get_geometry(handle)->origin.y,wlc_view_get_geometry(handle)->size.w, + wlc_view_get_geometry(handle)->size.h, wlc_view_get_title(handle), + wlc_view_get_class(handle), wlc_view_get_app_id(handle)); + + // TODO properly figure out how each window should be handled. switch (wlc_view_get_type(handle)) { // regular view created regularly case 0: newview = new_view(focused, handle); wlc_view_set_state(handle, WLC_BIT_MAXIMIZED, true); break; - // takes keyboard focus + + // Dmenu keeps viewfocus, but others with this flag dont, for now simulate + // dmenu case WLC_BIT_OVERRIDE_REDIRECT: - sway_log(L_DEBUG, "view %ld with OVERRIDE_REDIRECT", handle); - locked_view_focus = true; +// locked_view_focus = true; wlc_view_focus(handle); wlc_view_set_state(handle, WLC_BIT_ACTIVATED, true); wlc_view_bring_to_front(handle); break; - // Takes container focus + + // Firefox popups have this flag set. case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED: - sway_log(L_DEBUG, "view %ld with OVERRIDE_REDIRECT|WLC_BIT_MANAGED", handle); wlc_view_bring_to_front(handle); locked_container_focus = true; break; - // set modals as floating containers + + // Modals, get focus, popups do not case WLC_BIT_MODAL: + wlc_view_focus(handle); wlc_view_bring_to_front(handle); newview = new_floating_view(handle); case WLC_BIT_POPUP: + wlc_view_bring_to_front(handle); break; } + if (newview) { set_focused_container(newview); - arrange_windows(newview->parent, -1, -1); + swayc_t *output = swayc_parent_by_type(newview, C_OUTPUT); + arrange_windows(output, -1, -1); } return true; } @@ -181,19 +202,19 @@ static void handle_view_destroyed(wlc_handle handle) { // regular view created regularly case 0: case WLC_BIT_MODAL: + case WLC_BIT_POPUP: if (view) { swayc_t *parent = destroy_view(view); arrange_windows(parent, -1, -1); } break; - // takes keyboard focus + // DMENU has this flag, and takes view_focus, but other things with this + // flag dont case WLC_BIT_OVERRIDE_REDIRECT: - locked_view_focus = false; +// locked_view_focus = false; break; - // Takes container focus case WLC_BIT_OVERRIDE_REDIRECT|WLC_BIT_UNMANAGED: locked_container_focus = false; - case WLC_BIT_POPUP: break; } set_focused_container(get_focused_view(&root_container)); @@ -205,7 +226,7 @@ static void handle_view_focus(wlc_handle view, bool focus) { static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geometry *geometry) { sway_log(L_DEBUG, "geometry request %d x %d : %d x %d", - geometry->origin.x, geometry->origin.y, geometry->size.w,geometry->size.h); + geometry->origin.x, geometry->origin.y, geometry->size.w, geometry->size.h); // 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 @@ -225,21 +246,17 @@ 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 = NULL; - switch(state) { + swayc_t *c = get_swayc_for_handle(view, &root_container); + switch (state) { case WLC_BIT_FULLSCREEN: // i3 just lets it become fullscreen wlc_view_set_state(view, state, toggle); - c = get_swayc_for_handle(view, &root_container); - sway_log(L_DEBUG, "setting view %ld %s, fullscreen %d",view,c->name,toggle); if (c) { + sway_log(L_DEBUG, "setting view %ld %s, fullscreen %d", view, c->name, toggle); arrange_windows(c->parent, -1, -1); // Set it as focused window for that workspace if its going fullscreen if (toggle) { - swayc_t *ws = c; - while (ws->type != C_WORKSPACE) { - ws = ws->parent; - } + swayc_t *ws = swayc_parent_by_type(c, C_WORKSPACE); // Set ws focus to c set_focused_container_for(ws, c); } @@ -248,7 +265,9 @@ static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit s case WLC_BIT_MAXIMIZED: case WLC_BIT_RESIZING: case WLC_BIT_MOVING: + break; case WLC_BIT_ACTIVATED: + sway_log(L_DEBUG, "View %p requested to be activated", c); break; } return; @@ -257,29 +276,38 @@ static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit s static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers, uint32_t key, uint32_t sym, enum wlc_key_state state) { - enum { QSIZE = 32 }; + if (locked_view_focus && state == WLC_KEY_STATE_PRESSED) { return false; } - static uint8_t head = 0; - bool cmd_success = false; + + // Revert floating container back to original position on keypress + if (state == WLC_KEY_STATE_PRESSED && + (pointer_state.floating.drag || pointer_state.floating.resize)) { + reset_floating(get_focused_view(&root_container)); + } struct sway_mode *mode = config->current_mode; + + if (sym < 70000 /* bullshit made up number */) { + if (!isalnum(sym) && sym != ' ' && sym != XKB_KEY_Escape && sym != XKB_KEY_Tab) { + // God fucking dammit + return false; + } + } + // Lowercase if necessary sym = tolower(sym); - // Find key, if it has been pressed - int mid = 0; - while (mid < head && keys_pressed[mid] != sym) { - ++mid; - } - if (state == WLC_KEY_STATE_PRESSED && mid == head && head + 1 < QSIZE) { - keys_pressed[head++] = sym; - } else if (state == WLC_KEY_STATE_RELEASED && mid < head) { - memmove(keys_pressed + mid, keys_pressed + mid + 1, sizeof*keys_pressed * (--head - mid)); - } - // TODO: reminder to check conflicts with mod+q+a versus mod+q int i; + + if (state == WLC_KEY_STATE_PRESSED) { + press_key(sym); + } else { // WLC_KEY_STATE_RELEASED + release_key(sym); + } + + // TODO: reminder to check conflicts with mod+q+a versus mod+q for (i = 0; i < mode->bindings->length; ++i) { struct sway_binding *binding = mode->bindings->items[i]; @@ -287,39 +315,22 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier bool match; int j; for (j = 0; j < binding->keys->length; ++j) { - match = false; xkb_keysym_t *key = binding->keys->items[j]; - uint8_t k; - for (k = 0; k < head; ++k) { - if (keys_pressed[k] == *key) { - match = true; - break; - } - } - if (match == false) { + if ((match = check_key(*key)) == false) { break; } } - if (match) { - // Remove matched keys from keys_pressed - int j; - for (j = 0; j < binding->keys->length; ++j) { - uint8_t k; - for (k = 0; k < head; ++k) { - memmove(keys_pressed + k, keys_pressed + k + 1, sizeof*keys_pressed * (--head - k)); - break; - } - } if (state == WLC_KEY_STATE_PRESSED) { - cmd_success = handle_command(config, binding->command); + handle_command(config, binding->command); + return true; } else if (state == WLC_KEY_STATE_RELEASED) { // TODO: --released } } } } - return cmd_success; + return false; } static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct wlc_origin *origin) { @@ -327,119 +338,129 @@ static bool handle_pointer_motion(wlc_handle handle, uint32_t time, const struct static wlc_handle prev_handle = 0; mouse_origin = *origin; bool changed_floating = false; - int i = 0; if (!active_workspace) { return false; } // Do checks to determine if proper keys are being held - swayc_t *view = active_workspace->focused; - if (m1_held && view) { + swayc_t *view = get_focused_view(active_workspace); + uint32_t edge = 0; + if (pointer_state.floating.drag && view) { if (view->is_floating) { - while (keys_pressed[i++]) { - if (keys_pressed[i] == config->floating_mod) { - int dx = mouse_origin.x - prev_pos.x; - int dy = mouse_origin.y - prev_pos.y; - sway_log(L_DEBUG, "Moving from px: %d to cx: %d and from py: %d to cy: %d", prev_pos.x, mouse_origin.x, prev_pos.y, mouse_origin.y); - sway_log(L_DEBUG, "Moving: dx: %d, dy: %d", dx, dy); + int dx = mouse_origin.x - prev_pos.x; + int dy = mouse_origin.y - prev_pos.y; + view->x += dx; + view->y += dy; + changed_floating = true; + } + } else if (pointer_state.floating.resize && view) { + if (view->is_floating) { + int dx = mouse_origin.x - prev_pos.x; + int dy = mouse_origin.y - prev_pos.y; + int min_sane_w = 100; + int min_sane_h = 60; - view->x += dx; - view->y += dy; + // Move and resize the view based on the dx/dy and mouse position + int midway_x = view->x + view->width/2; + int midway_y = view->y + view->height/2; + if (dx < 0) { + if (!pointer_state.lock.right) { + if (view->width > min_sane_w) { + changed_floating = true; + view->width += dx; + edge += WLC_RESIZE_EDGE_RIGHT; + } + } else if (mouse_origin.x < midway_x && !pointer_state.lock.left) { changed_floating = true; - break; + view->x += dx; + view->width -= dx; + edge += WLC_RESIZE_EDGE_LEFT; + } + } else if (dx > 0) { + if (mouse_origin.x > midway_x && !pointer_state.lock.right) { + changed_floating = true; + view->width += dx; + edge += WLC_RESIZE_EDGE_RIGHT; + } else if (!pointer_state.lock.left) { + if (view->width > min_sane_w) { + changed_floating = true; + view->x += dx; + view->width -= dx; + edge += WLC_RESIZE_EDGE_LEFT; + } } } - } - } else if (m2_held && view) { - if (view->is_floating) { - while (keys_pressed[i++]) { - if (keys_pressed[i] == config->floating_mod) { - int dx = mouse_origin.x - prev_pos.x; - int dy = mouse_origin.y - prev_pos.y; - sway_log(L_DEBUG, "Moving from px: %d to cx: %d and from py: %d to cy: %d", prev_pos.x, mouse_origin.x, prev_pos.y, mouse_origin.y); - sway_log(L_INFO, "Moving: dx: %d, dy: %d", dx, dy); - // Move and resize the view based on the dx/dy and mouse position - int midway_x = view->x + view->width/2; - int midway_y = view->y + view->height/2; - - if (dx < 0) { + if (dy < 0) { + if (!pointer_state.lock.bottom) { + if (view->height > min_sane_h) { changed_floating = true; - if (mouse_origin.x > midway_x) { - sway_log(L_INFO, "Downsizing view to the left"); - view->width += dx; - } else { - sway_log(L_INFO, "Upsizing view to the left"); - view->x += dx; - view->width -= dx; - } - } else if (dx > 0){ - changed_floating = true; - if (mouse_origin.x > midway_x) { - sway_log(L_INFO, "Upsizing to the right"); - view->width += dx; - } else { - sway_log(L_INFO, "Downsizing to the right"); - view->x += dx; - view->width -= dx; - } + view->height += dy; + edge += WLC_RESIZE_EDGE_BOTTOM; } - - if (dy < 0) { + } else if (mouse_origin.y < midway_y && !pointer_state.lock.top) { + changed_floating = true; + view->y += dy; + view->height -= dy; + edge += WLC_RESIZE_EDGE_TOP; + } + } else if (dy > 0) { + if (mouse_origin.y > midway_y && !pointer_state.lock.bottom) { + changed_floating = true; + view->height += dy; + edge += WLC_RESIZE_EDGE_BOTTOM; + } else if (!pointer_state.lock.top) { + if (view->height > min_sane_h) { changed_floating = true; - if (mouse_origin.y > midway_y) { - sway_log(L_INFO, "Downsizing view to the top"); - view->height += dy; - } else { - sway_log(L_INFO, "Upsizing the view to the top"); - view->y += dy; - view->height -= dy; - } - } else if (dy > 0) { - changed_floating = true; - if (mouse_origin.y > midway_y) { - sway_log(L_INFO, "Upsizing to the bottom"); - view->height += dy; - } else { - sway_log(L_INFO, "Downsizing to the bottom"); - view->y += dy; - view->height -= dy; - } + view->y += dy; + view->height -= dy; + edge += WLC_RESIZE_EDGE_TOP; } - break; } } } } if (config->focus_follows_mouse && prev_handle != handle) { - //Dont change focus if fullscreen + // Dont change focus if fullscreen swayc_t *focused = get_focused_view(view); - if (!(focused->type == C_VIEW && wlc_view_get_state(focused->handle) & WLC_BIT_FULLSCREEN)) { + if (!(focused->type == C_VIEW && wlc_view_get_state(focused->handle) & WLC_BIT_FULLSCREEN) + && !(pointer_state.l_held || pointer_state.r_held)) { set_focused_container(container_under_pointer()); } } prev_handle = handle; prev_pos = mouse_origin; if (changed_floating) { - arrange_windows(view, -1, -1); + struct wlc_geometry geometry = { + .origin = { + .x = view->x, + .y = view->y + }, + .size = { + .w = view->width, + .h = view->height + } + }; + wlc_view_set_geometry(view->handle, edge, &geometry); return true; } return false; } + static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers, - uint32_t button, enum wlc_button_state state) { + uint32_t button, enum wlc_button_state state, const struct wlc_origin *origin) { swayc_t *focused = get_focused_container(&root_container); - //dont change focus if fullscreen + // dont change focus if fullscreen if (focused->type == C_VIEW && wlc_view_get_state(focused->handle) & WLC_BIT_FULLSCREEN) { return false; } if (state == WLC_BUTTON_STATE_PRESSED) { sway_log(L_DEBUG, "Mouse button %u pressed", button); - if (button == 272) { - m1_held = true; + if (button == M_LEFT_CLICK) { + pointer_state.l_held = true; } - if (button == 273) { - m2_held = true; + if (button == M_RIGHT_CLICK) { + pointer_state.r_held = true; } swayc_t *pointer = container_under_pointer(); set_focused_container(pointer); @@ -453,15 +474,32 @@ static bool handle_pointer_button(wlc_handle view, uint32_t time, const struct w } } arrange_windows(pointer->parent, -1, -1); + if (modifiers->mods & config->floating_mod) { + int midway_x = pointer->x + pointer->width/2; + int midway_y = pointer->y + pointer->height/2; + + pointer_state.floating.drag = pointer_state.l_held; + pointer_state.floating.resize = pointer_state.r_held; + pointer_state.lock.bottom = origin->y < midway_y; + pointer_state.lock.top = !pointer_state.lock.bottom; + pointer_state.lock.right = origin->x < midway_x; + pointer_state.lock.left = !pointer_state.lock.right; + start_floating(pointer); + } + // Dont want pointer sent to window while dragging or resizing + return (pointer_state.floating.drag || pointer_state.floating.resize); } return (pointer && pointer != focused); } else { sway_log(L_DEBUG, "Mouse button %u released", button); - if (button == 272) { - m1_held = false; + if (button == M_LEFT_CLICK) { + pointer_state.l_held = false; + pointer_state.floating.drag = false; } - if (button == 273) { - m2_held = false; + if (button == M_RIGHT_CLICK) { + pointer_state.r_held = false; + pointer_state.floating.resize = false; + pointer_state.lock = (struct pointer_lock){false ,false ,false ,false}; } } return false; diff --git a/sway/input_state.c b/sway/input_state.c new file mode 100644 index 00000000..a7f88d4a --- /dev/null +++ b/sway/input_state.c @@ -0,0 +1,68 @@ +#include +#include +#include + +#include "input_state.h" + +#define KEY_STATE_MAX_LENGTH 64 + +static keycode key_state_array[KEY_STATE_MAX_LENGTH]; + +static uint8_t find_key(keycode key) { + int i; + for (i = 0; i < KEY_STATE_MAX_LENGTH; ++i) { + if (key_state_array[i] == key) { + break; + } + } + return i; +} + +bool check_key(keycode key) { + return find_key(key) < KEY_STATE_MAX_LENGTH; +} + +void press_key(keycode key) { + // Check if key exists + if (!check_key(key)) { + // Check that we dont exceed buffer length + int insert = find_key(0); + if (insert < KEY_STATE_MAX_LENGTH) { + key_state_array[insert] = key; + } + } +} + +void release_key(keycode key) { + uint8_t index = find_key(key); + if (index < KEY_STATE_MAX_LENGTH) { + // shift it over and remove key + key_state_array[index] = 0; + } +} + +struct pointer_state pointer_state = {0, 0, {0, 0}, {0, 0, 0, 0}}; + +static struct wlc_geometry saved_floating; + +void start_floating(swayc_t *view) { + if (view->is_floating) { + saved_floating.origin.x = view->x; + saved_floating.origin.y = view->y; + saved_floating.size.w = view->width; + saved_floating.size.h = view->height; + } +} + +void reset_floating(swayc_t *view) { + if (view->is_floating) { + view->x = saved_floating.origin.x; + view->y = saved_floating.origin.y; + view->width = saved_floating.size.w; + view->height = saved_floating.size.h; + arrange_windows(view->parent, -1, -1); + } + pointer_state.floating = (struct pointer_floating){0,0}; + pointer_state.lock = (struct pointer_lock){0,0,0,0}; +} + diff --git a/sway/ipc.c b/sway/ipc.c new file mode 100644 index 00000000..39e580cd --- /dev/null +++ b/sway/ipc.c @@ -0,0 +1,321 @@ +// See https://i3wm.org/docs/ipc.html for protocol information + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipc.h" +#include "log.h" +#include "config.h" +#include "commands.h" +#include "list.h" +#include "stringop.h" + +static int ipc_socket = -1; +static struct wlc_event_source *ipc_event_source = NULL; +static struct sockaddr_un ipc_sockaddr = { + .sun_family = AF_UNIX, + .sun_path = "/tmp/sway-ipc.sock" +}; + +static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; + +struct ipc_client { + struct wlc_event_source *event_source; + int fd; + uint32_t payload_length; + enum ipc_command_type current_command; +}; + +int ipc_handle_connection(int fd, uint32_t mask, void *data); +int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); +void ipc_client_disconnect(struct ipc_client *client); +void ipc_client_handle_command(struct ipc_client *client); +bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); +void ipc_get_workspaces_callback(swayc_t *container, void *data); +void ipc_get_outputs_callback(swayc_t *container, void *data); + +char *json_list(list_t *items); + +void ipc_init(void) { + ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + if (ipc_socket == -1) { + sway_abort("Unable to create IPC socket"); + } + + if (getenv("SWAYSOCK") != NULL) { + strncpy(ipc_sockaddr.sun_path, getenv("SWAYSOCK"), sizeof(ipc_sockaddr.sun_path)); + } + + unlink(ipc_sockaddr.sun_path); + if (bind(ipc_socket, (struct sockaddr *)&ipc_sockaddr, sizeof(ipc_sockaddr)) == -1) { + sway_abort("Unable to bind IPC socket"); + } + + if (listen(ipc_socket, 3) == -1) { + sway_abort("Unable to listen on IPC socket"); + } + + // Set i3 IPC socket path so that i3-msg works out of the box + setenv("I3SOCK", ipc_sockaddr.sun_path, 1); + + ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); +} + +void ipc_terminate(void) { + if (ipc_event_source) { + wlc_event_source_remove(ipc_event_source); + } + close(ipc_socket); + unlink(ipc_sockaddr.sun_path); +} + +int ipc_handle_connection(int fd, uint32_t mask, void *data) { + (void) fd; (void) data; + sway_log(L_DEBUG, "Event on IPC listening socket"); + assert(mask == WLC_EVENT_READABLE); + + int client_fd = accept(ipc_socket, NULL, NULL); + if (client_fd == -1) { + sway_log_errno(L_INFO, "Unable to accept IPC client connection"); + return 0; + } + + int flags; + if ((flags=fcntl(client_fd, F_GETFD)) == -1 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { + sway_log_errno(L_INFO, "Unable to set CLOEXEC on IPC client socket"); + return 0; + } + + struct ipc_client* client = malloc(sizeof(struct ipc_client)); + client->payload_length = 0; + client->fd = client_fd; + client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); + + return 0; +} + +static const int ipc_header_size = sizeof(ipc_magic)+8; + +int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { + struct ipc_client *client = data; + sway_log(L_DEBUG, "Event on IPC client socket %d", client_fd); + + if (mask & WLC_EVENT_ERROR) { + sway_log(L_INFO, "IPC Client socket error, removing client"); + ipc_client_disconnect(client); + return 0; + } + + if (mask & WLC_EVENT_HANGUP) { + ipc_client_disconnect(client); + return 0; + } + + int read_available; + ioctl(client_fd, FIONREAD, &read_available); + + // Wait for the rest of the command payload in case the header has already been read + if (client->payload_length > 0) { + if (read_available >= client->payload_length) { + ipc_client_handle_command(client); + } + else { + sway_log(L_DEBUG, "Too little data to read payload on IPC Client socket, waiting for more (%d < %d)", read_available, client->payload_length); + } + return 0; + } + + if (read_available < ipc_header_size) { + sway_log(L_DEBUG, "Too little data to read header on IPC Client socket, waiting for more (%d < %d)", read_available, ipc_header_size); + return 0; + } + + char buf[ipc_header_size]; + ssize_t received = recv(client_fd, buf, ipc_header_size, 0); + if (received == -1) { + sway_log_errno(L_INFO, "Unable to receive header from IPC client"); + ipc_client_disconnect(client); + return 0; + } + + if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { + sway_log(L_DEBUG, "IPC header check failed"); + ipc_client_disconnect(client); + return 0; + } + + client->payload_length = *(uint32_t *)&buf[sizeof(ipc_magic)]; + client->current_command = (enum ipc_command_type) *(uint32_t *)&buf[sizeof(ipc_magic)+4]; + + if (read_available - received >= client->payload_length) { + ipc_client_handle_command(client); + } + + return 0; +} + +void ipc_client_disconnect(struct ipc_client *client) +{ + if (!sway_assert(client != NULL, "client != NULL")) { + return; + } + + sway_log(L_INFO, "IPC Client %d disconnected", client->fd); + wlc_event_source_remove(client->event_source); + close(client->fd); + free(client); +} + +void ipc_client_handle_command(struct ipc_client *client) { + if (!sway_assert(client != NULL, "client != NULL")) { + return; + } + + char buf[client->payload_length + 1]; + if (client->payload_length > 0) + { + ssize_t received = recv(client->fd, buf, client->payload_length, 0); + if (received == -1) + { + sway_log_errno(L_INFO, "Unable to receive payload from IPC client"); + ipc_client_disconnect(client); + return; + } + } + + switch (client->current_command) { + case IPC_COMMAND: + { + buf[client->payload_length] = '\0'; + bool success = handle_command(config, buf); + char reply[64]; + int length = snprintf(reply, sizeof(reply), "{\"success\":%s}", success ? "true" : "false"); + ipc_send_reply(client, reply, (uint32_t) length); + break; + } + case IPC_GET_WORKSPACES: + { + list_t *workspaces = create_list(); + container_map(&root_container, ipc_get_workspaces_callback, workspaces); + char *json = json_list(workspaces); + free_flat_list(workspaces); + ipc_send_reply(client, json, strlen(json)); + free(json); + break; + } + case IPC_GET_OUTPUTS: + { + list_t *outputs = create_list(); + container_map(&root_container, ipc_get_outputs_callback, outputs); + char *json = json_list(outputs); + free_flat_list(outputs); + ipc_send_reply(client, json, strlen(json)); + free(json); + break; + } + default: + sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); + ipc_client_disconnect(client); + break; + } + + client->payload_length = 0; +} + +bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length) { + assert(payload); + + char data[ipc_header_size]; + + memcpy(data, ipc_magic, sizeof(ipc_magic)); + *(uint32_t *)&(data[sizeof(ipc_magic)]) = payload_length; + *(uint32_t *)&(data[sizeof(ipc_magic)+4]) = client->current_command; + + if (write(client->fd, data, ipc_header_size) == -1) { + sway_log_errno(L_INFO, "Unable to send header to IPC client"); + ipc_client_disconnect(client); + return false; + } + + if (write(client->fd, payload, payload_length) == -1) { + sway_log_errno(L_INFO, "Unable to send payload to IPC client"); + ipc_client_disconnect(client); + return false; + } + + return true; +} + +char *json_list(list_t *items) { + char *json_elements = join_list(items, ","); + size_t len = strlen(json_elements); + char *json = malloc(len + 3); + json[0] = '['; + memcpy(json + 1, json_elements, len); + json[len+1] = ']'; + json[len+2] = '\0'; + free(json_elements); + return json; +} + +void ipc_get_workspaces_callback(swayc_t *container, void *data) { + if (container->type == C_WORKSPACE) { + char *json = malloc(512); // Output should usually be around 180 chars + int num = isdigit(container->name[0]) ? atoi(container->name) : -1; + // TODO: escape the name (quotation marks, unicode) + sprintf(json, + "{" + "\"num\":%d," + "\"name\":\"%s\"," + "\"visible\":%s," + "\"focused\":%s," + "\"rect\":{" + "\"x\":%d," + "\"y\":%d," + "\"width\":%d," + "\"height\":%d" + "}," + "\"output\":\"%s\"," + "\"urgent\":%s" + "}", + num, container->name, container->visible ? "true" : "false", container->is_focused ? "true" : "false", + container->x, container->y, container->width, container->height, + container->parent->name, "false" // TODO: urgent hint + ); + list_add((list_t *)data, json); + } +} + +void ipc_get_outputs_callback(swayc_t *container, void *data) { + if (container->type == C_OUTPUT) { + char *json = malloc(512); // Output should usually be around 130 chars + // TODO: escape the name (quotation marks, unicode) + sprintf(json, + "{" + "\"name\":\"%s\"," + "\"active\":%s," + "\"primary\":%s," + "\"rect\":{" + "\"x\":%d," + "\"y\":%d," + "\"width\":%d," + "\"height\":%d" + "}," + "\"current_workspace\":\"%s\"" + "}", + container->name, "true", "false", // TODO: active, primary + container->x, container->y, container->width, container->height, + container->focused ? container->focused->name : "" + ); + list_add((list_t *)data, json); + } +} diff --git a/sway/layout.c b/sway/layout.c index 9fdfd62a..a48f15c4 100644 --- a/sway/layout.c +++ b/sway/layout.c @@ -4,6 +4,7 @@ #include "layout.h" #include "log.h" #include "list.h" +#include "config.h" #include "container.h" #include "workspace.h" #include "focus.h" @@ -32,6 +33,21 @@ void add_child(swayc_t *parent, swayc_t *child) { child->width, child->height, parent, parent->type, parent->width, parent->height); list_add(parent->children, child); child->parent = parent; + // set focus for this container + if (parent->children->length == 1) { + set_focused_container_for(parent, child); + } +} + +void add_floating(swayc_t *ws, swayc_t *child) { + sway_log(L_DEBUG, "Adding %p (%d, %dx%d) to %p (%d, %dx%d)", child, child->type, + child->width, child->height, ws, ws->type, ws->width, ws->height); + list_add(ws->floating, child); + child->parent = ws; + child->is_floating = true; + if (!ws->focused) { + set_focused_container_for(ws, child); + } } swayc_t *add_sibling(swayc_t *sibling, swayc_t *child) { @@ -55,7 +71,7 @@ swayc_t *replace_child(swayc_t *child, swayc_t *new_child) { new_child->parent = child->parent; if (child->parent->focused == child) { - child->parent->focused = new_child; + set_focused_container_for(child->parent, new_child); } child->parent = NULL; return parent; @@ -72,6 +88,7 @@ swayc_t *remove_child(swayc_t *child) { break; } } + i = 0; } else { for (i = 0; i < parent->children->length; ++i) { if (parent->children->items[i] == child) { @@ -80,7 +97,7 @@ swayc_t *remove_child(swayc_t *child) { } } } - //Set focused to new container + // Set focused to new container if (parent->focused == child) { if (parent->children->length > 0) { set_focused_container_for(parent, parent->children->items[i?i-1:0]); @@ -104,7 +121,7 @@ void move_container(swayc_t *container,swayc_t* root,int direction){ //Only one container, meh. break; } - + //TODO: Implement horizontal movement. //TODO: Implement move to a different workspace. if(direction == MOVE_LEFT && i > 0){ temp = root->children->items[i-1]; @@ -167,11 +184,11 @@ void arrange_windows(swayc_t *container, int width, int height) { // y -= container->y; for (i = 0; i < container->children->length; ++i) { swayc_t *child = container->children->items[i]; - sway_log(L_DEBUG, "Arranging workspace #%d", i); - child->x = x; - child->y = y; - child->width = width; - child->height = height; + child->x = x + container->gaps; + child->y = y + container->gaps; + child->width = width - container->gaps * 2; + child->height = height - container->gaps * 2; + sway_log(L_DEBUG, "Arranging workspace #%d at %d, %d", i, child->x, child->y); arrange_windows(child, -1, -1); } return; @@ -179,27 +196,24 @@ void arrange_windows(swayc_t *container, int width, int height) { { struct wlc_geometry geometry = { .origin = { - .x = container->x, - .y = container->y + .x = container->x + container->gaps / 2, + .y = container->y + container->gaps / 2 }, .size = { - .w = width, - .h = height + .w = width - container->gaps, + .h = height - container->gaps } }; if (wlc_view_get_state(container->handle) & WLC_BIT_FULLSCREEN) { - swayc_t *parent = container; - while (parent->type != C_OUTPUT) { - parent = parent->parent; - } + swayc_t *parent = swayc_parent_by_type(container, C_OUTPUT); geometry.origin.x = 0; geometry.origin.y = 0; geometry.size.w = parent->width; geometry.size.h = parent->height; - wlc_view_set_geometry(container->handle, &geometry); + wlc_view_set_geometry(container->handle, 0, &geometry); wlc_view_bring_to_front(container->handle); } else { - wlc_view_set_geometry(container->handle, &geometry); + wlc_view_set_geometry(container->handle, 0, &geometry); container->width = width; container->height = height; } @@ -213,40 +227,62 @@ void arrange_windows(swayc_t *container, int width, int height) { break; } - double total_weight = 0; - for (i = 0; i < container->children->length; ++i) { - swayc_t *child = container->children->items[i]; - total_weight += child->weight; - } - + x = y = 0; + double scale = 0; switch (container->layout) { case L_HORIZ: default: - sway_log(L_DEBUG, "Arranging %p horizontally", container); + // Calculate total width for (i = 0; i < container->children->length; ++i) { - swayc_t *child = container->children->items[i]; - double percent = child->weight / total_weight; - sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will receive %.2f of %d)", child, child->type, percent, width); - child->x = x + container->x; - child->y = y + container->y; - int w = width * percent; - int h = height; - arrange_windows(child, w, h); - x += w; + int *old_width = &((swayc_t *)container->children->items[i])->width; + if (*old_width <= 0) { + if (container->children->length > 1) { + *old_width = width / (container->children->length - 1); + } else { + *old_width = width; + } + } + scale += *old_width; + } + // Resize windows + if (scale > 0.1) { + scale = width / scale; + sway_log(L_DEBUG, "Arranging %p horizontally", container); + for (i = 0; i < container->children->length; ++i) { + swayc_t *child = container->children->items[i]; + sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %d by %f)", child, child->type, width, scale); + child->x = x + container->x; + child->y = y + container->y; + arrange_windows(child, child->width * scale, height); + x += child->width; + } } break; case L_VERT: - sway_log(L_DEBUG, "Arranging %p vertically", container); + // Calculate total height for (i = 0; i < container->children->length; ++i) { - swayc_t *child = container->children->items[i]; - double percent = child->weight / total_weight; - sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will receive %.2f of %d)", child, child->type, percent, width); - child->x = x + container->x; - child->y = y + container->y; - int w = width; - int h = height * percent; - arrange_windows(child, w, h); - y += h; + int *old_height = &((swayc_t *)container->children->items[i])->height; + if (*old_height <= 0) { + if (container->children->length > 1) { + *old_height = height / (container->children->length - 1); + } else { + *old_height = height; + } + } + scale += *old_height; + } + // Resize + if (scale > 0.1) { + scale = height / scale; + sway_log(L_DEBUG, "Arranging %p vertically", container); + for (i = 0; i < container->children->length; ++i) { + swayc_t *child = container->children->items[i]; + sway_log(L_DEBUG, "Calculating arrangement for %p:%d (will scale %d by %f)", child, child->type, height, scale); + child->x = x + container->x; + child->y = y + container->y; + arrange_windows(child, width, child->height * scale); + y += child->height; + } } break; } @@ -268,20 +304,15 @@ void arrange_windows(swayc_t *container, int width, int height) { } }; if (wlc_view_get_state(view->handle) & WLC_BIT_FULLSCREEN) { - swayc_t *parent = view; - while (parent->type != C_OUTPUT) { - parent = parent->parent; - } + swayc_t *parent = swayc_parent_by_type(view, C_OUTPUT); geometry.origin.x = 0; geometry.origin.y = 0; geometry.size.w = parent->width; geometry.size.h = parent->height; - wlc_view_set_geometry(view->handle, &geometry); + wlc_view_set_geometry(view->handle, 0, &geometry); wlc_view_bring_to_front(view->handle); } else { - wlc_view_set_geometry(view->handle, &geometry); - view->width = width; - view->height = height; + wlc_view_set_geometry(view->handle, 0, &geometry); // Bring the views to the front in order of the list, the list // will be kept up to date so that more recently focused views // have higher indexes @@ -326,3 +357,54 @@ swayc_t *get_swayc_for_handle(wlc_handle handle, swayc_t *parent) { } return NULL; } + +swayc_t *get_swayc_in_direction(swayc_t *container, enum movement_direction dir) { + swayc_t *parent = container->parent; + + if (dir == MOVE_PARENT) { + if (parent->type == C_OUTPUT) { + return NULL; + } else { + return parent; + } + } + while (true) { + // Test if we can even make a difference here + bool can_move = false; + int diff = 0; + if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { + if (parent->layout == L_HORIZ || parent->type == C_ROOT) { + can_move = true; + diff = dir == MOVE_LEFT ? -1 : 1; + } + } else { + if (parent->layout == L_VERT) { + can_move = true; + diff = dir == MOVE_UP ? -1 : 1; + } + } + if (can_move) { + int i; + for (i = 0; i < parent->children->length; ++i) { + swayc_t *child = parent->children->items[i]; + if (child == container) { + break; + } + } + int desired = i + diff; + if (desired < 0 || desired >= parent->children->length) { + can_move = false; + } else { + return parent->children->items[desired]; + } + } + if (!can_move) { + container = parent; + parent = parent->parent; + if (!parent) { + // Nothing we can do + return NULL; + } + } + } +} diff --git a/sway/log.c b/sway/log.c index 8e380ffe..6e01421b 100644 --- a/sway/log.c +++ b/sway/log.c @@ -1,9 +1,13 @@ #include "log.h" +#include "sway.h" #include #include #include #include #include +#include +#include +#include int colored = 1; int v = 0; @@ -18,10 +22,10 @@ static const char *verbosity_colors[] = { void init_log(int verbosity) { v = verbosity; /* set FD_CLOEXEC flag to prevent programs called with exec to write into logs */ - int i, flag; + int i; int fd[] = { STDOUT_FILENO, STDIN_FILENO, STDERR_FILENO }; for (i = 0; i < 3; ++i) { - flag = fcntl(fd[i], F_GETFD); + int flag = fcntl(fd[i], F_GETFD); if (flag != -1) { fcntl(fd[i], F_SETFD, flag | FD_CLOEXEC); } @@ -32,17 +36,17 @@ void sway_log_colors(int mode) { colored = (mode == 1) ? 1 : 0; } -void sway_abort(char *format, ...) { +void sway_abort(const char *format, ...) { fprintf(stderr, "ERROR: "); va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); - exit(1); + sway_terminate(); } -void sway_log(int verbosity, char* format, ...) { +void sway_log(int verbosity, const char* format, ...) { if (verbosity <= v) { int c = verbosity; if (c > sizeof(verbosity_colors) / sizeof(char *)) { @@ -64,3 +68,106 @@ void sway_log(int verbosity, char* format, ...) { fprintf(stderr, "\n"); } } + +void sway_log_errno(int verbosity, char* format, ...) { + if (verbosity <= v) { + int c = verbosity; + if (c > sizeof(verbosity_colors) / sizeof(char *)) { + c = sizeof(verbosity_colors) / sizeof(char *) - 1; + } + + if (colored) { + fprintf(stderr, verbosity_colors[c]); + } + + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + fprintf(stderr, ": "); + char error[256]; + strerror_r(errno, error, sizeof(error)); + fprintf(stderr, error); + + if (colored) { + fprintf(stderr, "\x1B[0m"); + } + fprintf(stderr, "\n"); + } +} + +bool sway_assert(bool condition, const char* format, ...) { + if (condition) { + return true; + } + +#ifndef NDEBUG + raise(SIGABRT); +#endif + + va_list args; + va_start(args, format); + sway_log(L_ERROR, format, args); + va_end(args); + + return false; +} + +#include "workspace.h" + +/* XXX:DEBUG:XXX */ +static void container_log(const swayc_t *c) { + fprintf(stderr, "focus:%c|", + c->is_focused ? 'F' : // Focused + c == active_workspace ? 'W' : // active workspace + c == &root_container ? 'R' : // root + 'X');// not any others + fprintf(stderr,"(%p)",c); + fprintf(stderr,"(p:%p)",c->parent); + fprintf(stderr,"(f:%p)",c->focused); + fprintf(stderr,"(h:%ld)",c->handle); + fprintf(stderr,"Type:"); + fprintf(stderr, + c->type == C_ROOT ? "Root|" : + c->type == C_OUTPUT ? "Output|" : + c->type == C_WORKSPACE ? "Workspace|" : + c->type == C_CONTAINER ? "Container|" : + c->type == C_VIEW ? "View|" : "Unknown|"); + fprintf(stderr,"layout:"); + fprintf(stderr, + c->layout == L_NONE ? "NONE|" : + c->layout == L_HORIZ ? "Horiz|": + c->layout == L_VERT ? "Vert|": + c->layout == L_STACKED ? "Stacked|": + c->layout == L_FLOATING ? "Floating|": + "Unknown|"); + fprintf(stderr, "w:%d|h:%d|", c->width, c->height); + fprintf(stderr, "x:%d|y:%d|", c->x, c->y); + fprintf(stderr, "vis:%c|", c->visible?'t':'f'); + fprintf(stderr, "name:%.16s|", c->name); + fprintf(stderr, "children:%d\n",c->children?c->children->length:0); +} +void layout_log(const swayc_t *c, int depth) { + int i, d; + int e = c->children ? c->children->length : 0; + container_log(c); + if (e) { + for (i = 0; i < e; ++i) { + fputc('|',stderr); + for (d = 0; d < depth; ++d) fputc('-', stderr); + layout_log(c->children->items[i], depth + 1); + } + } + if (c->type == C_WORKSPACE) { + e = c->floating?c->floating->length:0; + if (e) { + for (i = 0; i < e; ++i) { + fputc('|',stderr); + for (d = 0; d < depth; ++d) fputc('=', stderr); + layout_log(c->floating->items[i], depth + 1); + } + } + } +} +/* XXX:DEBUG:XXX */ diff --git a/sway/main.c b/sway/main.c index 2db4604c..f37f086d 100644 --- a/sway/main.c +++ b/sway/main.c @@ -4,31 +4,121 @@ #include #include #include +#include #include "layout.h" #include "config.h" #include "log.h" #include "handlers.h" +#include "ipc.h" +#include "sway.h" + +static bool terminate_request = false; + +void sway_terminate(void) { + terminate_request = true; + wlc_terminate(); +} static void sigchld_handle(int signal); int main(int argc, char **argv) { + static int verbose = 0, debug = 0, validate = 0; + + static struct option long_options[] = { + {"config", required_argument, NULL, 'c'}, + {"validate", no_argument, &validate, 1}, + {"debug", no_argument, &debug, 1}, + {"version", no_argument, NULL, 'v'}, + {"verbose", no_argument, &verbose, 1}, + {"get-socketpath", no_argument, NULL, 'p'}, + }; + /* Signal handling */ signal(SIGCHLD, sigchld_handle); setenv("WLC_DIM", "0", 0); + + FILE *devnull = fopen("/dev/null", "w"); + if (devnull) { + // NOTE: Does not work, see wlc issue #54 + wlc_set_log_file(devnull); + } + /* Changing code earlier than this point requires detailed review */ if (!wlc_init(&interface, argc, argv)) { return 1; } - init_log(L_DEBUG); // TODO: Control this with command line arg - init_layout(); + char *config_path = NULL; - if (!load_config()) { - sway_log(L_ERROR, "Error(s) loading config!"); + int c; + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "CdvVpc:", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 0: // Flag + break; + case 'c': // config + config_path = strdup(optarg); + break; + case 'C': // validate + validate = 1; + break; + case 'd': // debug + debug = 1; + break; + case 'v': // version + // todo + exit(0); + break; + case 'V': // verbose + verbose = 1; + break; + case 'p': // --get-socketpath + // TODO + break; + } } - wlc_run(); + if (debug) { + init_log(L_DEBUG); + wlc_set_log_file(stderr); + fclose(devnull); + devnull = NULL; + } else if (verbose || validate) { + init_log(L_INFO); + } else { + init_log(L_ERROR); + } + + if (validate) { + bool valid = load_config(config_path); + return valid ? 0 : 1; + } + + init_layout(); + + if (!load_config(config_path)) { + sway_log(L_ERROR, "Error(s) loading config!"); + } + if (config_path) { + free(config_path); + } + + ipc_init(); + + if (!terminate_request) { + wlc_run(); + } + + if (devnull) { + fclose(devnull); + } + + ipc_terminate(); return 0; } diff --git a/sway/readline.c b/sway/readline.c index dfdc3fe8..e75b183f 100644 --- a/sway/readline.c +++ b/sway/readline.c @@ -17,18 +17,22 @@ char *read_line(FILE *file) { continue; } if (length == size) { - string = realloc(string, size *= 2); - if (!string) { + char *new_string = realloc(string, size *= 2); + if (!new_string) { + free(string); return NULL; } + string = new_string; } string[length++] = c; } if (length + 1 == size) { - string = realloc(string, length + 1); - if (!string) { + char *new_string = realloc(string, length + 1); + if (!new_string) { + free(string); return NULL; } + string = new_string; } string[length] = '\0'; return string; diff --git a/sway/stringop.c b/sway/stringop.c index 1dff97bf..c39e2c34 100644 --- a/sway/stringop.c +++ b/sway/stringop.c @@ -4,6 +4,7 @@ #include "string.h" #include "list.h" #include +#include /* Note: This returns 8 characters for trimmed_start per tab character. */ char *strip_whitespace(char *_str, int *trimmed_start) { @@ -197,3 +198,41 @@ char *join_args(char **argv, int argc) { res[len - 1] = '\0'; return res; } + +/* + * Join a list of strings, adding separator in between. Separator can be NULL. + */ +char *join_list(list_t *list, char *separator) { + if (!sway_assert(list != NULL, "list != NULL") || list->length == 0) { + return NULL; + } + + size_t len = 1; // NULL terminator + size_t sep_len = 0; + if (separator != NULL) { + sep_len = strlen(separator); + len += (list->length - 1) * sep_len; + } + + for (int i = 0; i < list->length; i++) { + len += strlen(list->items[i]); + } + + char *res = malloc(len); + + char *p = res + strlen(list->items[0]); + strcpy(res, list->items[0]); + + for (int i = 1; i < list->length; i++) { + if (sep_len) { + memcpy(p, separator, sep_len); + p += sep_len; + } + strcpy(p, list->items[i]); + p += strlen(list->items[i]); + } + + *p = '\0'; + + return res; +} diff --git a/sway/workspace.c b/sway/workspace.c index 05a669fe..d436da8e 100644 --- a/sway/workspace.c +++ b/sway/workspace.c @@ -31,7 +31,7 @@ char *workspace_next_name(void) { char* target = malloc(strlen(args->items[1]) + 1); strcpy(target, args->items[1]); while (*target == ' ' || *target == '\t') - target++; + target++; // Make sure that the command references an actual workspace // not a command about workspaces @@ -42,11 +42,15 @@ char *workspace_next_name(void) { strcmp(target, "number") == 0 || strcmp(target, "back_and_forth") == 0 || strcmp(target, "current") == 0) + { + list_free(args); continue; - - //Make sure that the workspace doesn't already exist + } + + // Make sure that the workspace doesn't already exist if (workspace_find_by_name(target)) { - continue; + list_free(args); + continue; } list_free(args); @@ -54,6 +58,7 @@ char *workspace_next_name(void) { sway_log(L_DEBUG, "Workspace: Found free name %s", target); return target; } + list_free(args); } // As a fall back, get the current number of active workspaces // and return that + 1 for the next workspace's name @@ -70,14 +75,12 @@ char *workspace_next_name(void) { swayc_t *workspace_create(const char* name) { swayc_t *parent = get_focused_container(&root_container); - while (parent->type != C_OUTPUT) { - parent = parent->parent; - } + parent = swayc_parent_by_type(parent, C_OUTPUT); return new_workspace(parent, name); } bool workspace_by_name(swayc_t *view, void *data) { - return (view->type == C_WORKSPACE) && + return (view->type == C_WORKSPACE) && (strcasecmp(view->name, (char *) data) == 0); } @@ -180,62 +183,4 @@ void workspace_switch(swayc_t *workspace) { sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name); set_focused_container(get_focused_view(workspace)); arrange_windows(workspace, -1, -1); - active_workspace = workspace; } - -/* XXX:DEBUG:XXX */ -static void container_log(const swayc_t *c) { - fprintf(stderr, "focus:%c|", - c->is_focused ? 'F' : //Focused - c == active_workspace ? 'W' : //active workspace - c == &root_container ? 'R' : //root - 'X');//not any others - fprintf(stderr,"(%p)",c); - fprintf(stderr,"(p:%p)",c->parent); - fprintf(stderr,"(f:%p)",c->focused); - fprintf(stderr,"(h:%ld)",c->handle); - fprintf(stderr,"Type:"); - fprintf(stderr, - c->type == C_ROOT ? "Root|" : - c->type == C_OUTPUT ? "Output|" : - c->type == C_WORKSPACE ? "Workspace|" : - c->type == C_CONTAINER ? "Container|" : - c->type == C_VIEW ? "View|" : "Unknown|"); - fprintf(stderr,"layout:"); - fprintf(stderr, - c->layout == L_NONE ? "NONE|" : - c->layout == L_HORIZ ? "Horiz|": - c->layout == L_VERT ? "Vert|": - c->layout == L_STACKED ? "Stacked|": - c->layout == L_FLOATING ? "Floating|": - "Unknown|"); - fprintf(stderr, "w:%d|h:%d|", c->width, c->height); - fprintf(stderr, "x:%d|y:%d|", c->x, c->y); - fprintf(stderr, "vis:%c|", c->visible?'t':'f'); - fprintf(stderr, "wgt:%d|", c->weight); - fprintf(stderr, "name:%.16s|", c->name); - fprintf(stderr, "children:%d\n",c->children?c->children->length:0); -} -void layout_log(const swayc_t *c, int depth) { - int i, d; - int e = c->children ? c->children->length : 0; - container_log(c); - if (e) { - for (i = 0; i < e; ++i) { - fputc('|',stderr); - for (d = 0; d < depth; ++d) fputc('-', stderr); - layout_log(c->children->items[i], depth + 1); - } - } - if (c->type == C_WORKSPACE) { - e = c->floating?c->floating->length:0; - if (e) { - for (i = 0; i < e; ++i) { - fputc('|',stderr); - for (d = 0; d < depth; ++d) fputc('-', stderr); - layout_log(c->floating->items[i], depth + 1); - } - } - } -} -/* XXX:DEBUG:XXX */