Skip to content

Commit

Permalink
Merge pull request #1024 from willakat/master
Browse files Browse the repository at this point in the history
Add Awesome/Monad style automatic layouts to Sway
  • Loading branch information
ddevault authored Jan 14, 2017
2 parents 0001b00 + a2cf3be commit 81102e8
Show file tree
Hide file tree
Showing 15 changed files with 1,072 additions and 356 deletions.
2 changes: 1 addition & 1 deletion common/list.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ int list_seq_find(list_t *list, int compare(const void *item, const void *data),
return -1;
}

static void list_swap(list_t *list, int src, int dest) {
void list_swap(list_t *list, int src, int dest) {
void *tmp = list->items[src];
list->items[src] = list->items[dest];
list->items[dest] = tmp;
Expand Down
63 changes: 63 additions & 0 deletions contrib/awesome.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#
# Replicate some of Awesome's default layout manipulation configuration for Sway
#
# Differences:
# - Layout switching doesn't use the spacebar (i.e. i3/Sway behavior to switch to/from floating windows)
# and uses the 'A' key instead (as in auto)
# - Resizing windows uses i3/Sway's more versatile Mod4+r
# - no tags
# - no Maximize/Minize, alternatives to Maximize would be to switch to Stacked/Tabbed layouts
# via Mod4+w or Mod4+s.
# - kill focused client is available on Mod4+Shift+q (instead of Mod4+Shift+c, which maps to Sway's
# config reload)
# - probably many more ...

# Awesome-style container traversal using Vim-like binding
set $next j
set $prev k

#
# Moving around:
#
# Move your focus around
bindsym $mod+$next focus next
bindsym $mod+$prev focus prev

# _move_ the focused window with the same, but add Shift
bindsym $mod+Shift+$next move next
bindsym $mod+Shift+$prev move prev

#
# Layout:
#
workspace_layout auto left

# This is usually bound to $mod+space, but this works well in practice by keeping
# all the layout switching keys grouped together.
bindsym $mod+a layout auto next
bindsym $mod+Shift+a layout auto prev

# Promote a child to master position in an auto layout
bindsym $mod+Control+Return move first

# Increase/decrease number of master elements in auto layout
bindsym $mod+Shift+h layout auto master inc 1
bindsym $mod+Shift+l layout auto master inc -1

# Increase/decrease number of slave element groups in auto layout
bindsym $mod+Control+h layout auto ncol inc 1
bindsym $mod+Control+l layout auto ncol inc -1

#
# Resizing containers:
# Again, not really the way Awesome works well, but in spirit with i3/Sway and it works well.
#
mode "resize" {
bindsym Left resize shrink width 20 px
bindsym Down resize grow height 20 px
bindsym Up resize shrink height 20 px
bindsym Right resize grow width 20 px
}
bindsym $mod+r mode "resize"

new_window pixel 1
2 changes: 2 additions & 0 deletions include/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ void list_qsort(list_t *list, int compare(const void *left, const void *right));
int list_seq_find(list_t *list, int compare(const void *item, const void *cmp_to), const void *cmp_to);
// stable sort since qsort is not guaranteed to be stable
void list_stable_sort(list_t *list, int compare(const void *a, const void *b));
// swap two elements in a list
void list_swap(list_t *list, int src, int dest);
#endif
20 changes: 20 additions & 0 deletions include/sway/container.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ enum swayc_layouts {
L_STACKED,
L_TABBED,
L_FLOATING, /**< A psuedo-container, removed from the tree, to hold floating windows */

/* Awesome/Monad style auto layouts */
L_AUTO_LEFT,
L_AUTO_RIGHT,
L_AUTO_TOP,
L_AUTO_BOTTOM,

L_AUTO_FIRST = L_AUTO_LEFT,
L_AUTO_LAST = L_AUTO_BOTTOM,

// Keep last
L_LAYOUTS,
};
Expand Down Expand Up @@ -144,6 +154,16 @@ struct sway_container {
struct wlc_geometry title_bar_geometry;
struct wlc_geometry actual_geometry;
int border_thickness;

/**
* Number of master views in auto layouts.
*/
size_t nb_master;

/**
* Number of slave groups (e.g. columns) in auto layouts.
*/
size_t nb_slave_groups;
};

enum visibility_mask {
Expand Down
6 changes: 4 additions & 2 deletions include/sway/focus.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ enum movement_direction {
MOVE_UP,
MOVE_DOWN,
MOVE_PARENT,
MOVE_CHILD
MOVE_CHILD,
MOVE_NEXT,
MOVE_PREV,
MOVE_FIRST
};

#include "container.h"
Expand Down Expand Up @@ -40,4 +43,3 @@ extern bool suspend_workspace_cleanup;
bool move_focus(enum movement_direction direction);

#endif

7 changes: 7 additions & 0 deletions include/sway/layout.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,11 @@ void swayc_log(log_importance_t verbosity, swayc_t *cont, const char* format, ..
*/
enum swayc_layouts default_layout(swayc_t *output);

bool is_auto_layout(enum swayc_layouts layout);
int auto_group_start_index(const swayc_t *container, int index);
int auto_group_end_index(const swayc_t *container, int index);
size_t auto_group_count(const swayc_t *container);
size_t auto_group_index(const swayc_t *container, int index);
bool auto_group_bounds(const swayc_t *container, size_t group_index, int *start, int *end);

#endif
4 changes: 4 additions & 0 deletions sway/commands/focus.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
move_focus(MOVE_PARENT);
} else if (strcasecmp(argv[0], "child") == 0) {
move_focus(MOVE_CHILD);
} else if (strcasecmp(argv[0], "next") == 0) {
move_focus(MOVE_NEXT);
} else if (strcasecmp(argv[0], "prev") == 0) {
move_focus(MOVE_PREV);
} else if (strcasecmp(argv[0], "mode_toggle") == 0) {
int i;
swayc_t *workspace = swayc_active_workspace();
Expand Down
127 changes: 126 additions & 1 deletion sway/commands/layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
#include "sway/container.h"
#include "sway/layout.h"

/**
* handle "layout auto" command group
*/
static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv);

struct cmd_results *cmd_layout(int argc, char **argv) {
struct cmd_results *error = NULL;
if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file.");
Expand Down Expand Up @@ -49,11 +54,14 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
} else if (strcasecmp(argv[0], "splitv") == 0) {
swayc_change_layout(parent, L_VERT);
} else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE || parent->workspace_layout == L_HORIZ)) {
if (parent->layout == L_HORIZ && (parent->workspace_layout == L_NONE
|| parent->workspace_layout == L_HORIZ)) {
swayc_change_layout(parent, L_VERT);
} else {
swayc_change_layout(parent, L_HORIZ);
}
} else if (strcasecmp(argv[0], "auto") == 0) {
return cmd_layout_auto(parent, argc, argv);
}
}

Expand All @@ -64,3 +72,120 @@ struct cmd_results *cmd_layout(int argc, char **argv) {

return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

static struct cmd_results *cmd_layout_auto(swayc_t *container, int argc, char **argv) {
// called after checking that argv[0] is auto, so just continue parsing from there
struct cmd_results *error = NULL;
const char *cmd_name = "layout auto";
const char *set_inc_cmd_name = "layout auto [master|ncol] [set|inc]";
const char *err_msg = "Allowed arguments are <right|left|top|bottom|next|prev|master|ncol>";

bool need_layout_update = false;
enum swayc_layouts old_layout = container->layout;
enum swayc_layouts layout = old_layout;

if (strcasecmp(argv[1], "left") == 0) {
layout = L_AUTO_LEFT;
} else if (strcasecmp(argv[1], "right") == 0) {
layout = L_AUTO_RIGHT;
} else if (strcasecmp(argv[1], "top") == 0) {
layout = L_AUTO_TOP;
} else if (strcasecmp(argv[1], "bottom") == 0) {
layout = L_AUTO_BOTTOM;
} else if (strcasecmp(argv[1], "next") == 0) {
if (is_auto_layout(container->layout) && container->layout < L_AUTO_LAST) {
layout = container->layout + 1;
} else {
layout = L_AUTO_FIRST;
}
} else if (strcasecmp(argv[1], "prev") == 0) {
if (is_auto_layout(container->layout) && container->layout > L_AUTO_FIRST) {
layout = container->layout - 1;
} else {
layout = L_AUTO_LAST;
}
} else {
bool is_nmaster;
bool is_set;
if (strcasecmp(argv[1], "master") == 0) {
is_nmaster = true;
} else if (strcasecmp(argv[1], "ncol") == 0) {
is_nmaster = false;
} else {
return cmd_results_new(CMD_INVALID, cmd_name, "Invalid %s command. %s",
cmd_name, err_msg);
}
if ((error = checkarg(argc, "auto <master|ncol>", EXPECTED_EQUAL_TO, 4))) {
return error;
}
if (strcasecmp(argv[2], "set") == 0) {
is_set = true;
} else if (strcasecmp(argv[2], "inc") == 0) {
is_set = false;
} else {
return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command. %s, "
"Argument must be on of <set|inc>",
set_inc_cmd_name);
}
char *end;
int n = (int)strtol(argv[3], &end, 10);
if (*end) {
return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
"(argument must be an integer)", set_inc_cmd_name);
}
if (is_auto_layout(container->layout)) {
int inc = 0; /* difference between current master/ncol and requested value */
if (is_nmaster) {
if (is_set) {
if (n < 0) {
return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
"(master must be >= 0)", set_inc_cmd_name);
}
inc = n - (int)container->nb_master;
} else { /* inc command */
if ((int)container->nb_master + n >= 0) {
inc = n;
}
}
if (inc) {
for (int i = container->nb_master;
i >= 0 && i < container->children->length
&& i != (int)container->nb_master + inc;) {
((swayc_t *)container->children->items[i])->height = -1;
((swayc_t *)container->children->items[i])->width = -1;
i += inc > 0 ? 1 : -1;
}
container->nb_master += inc;
need_layout_update = true;
}
} else { /* ncol modification */
if (is_set) {
if (n <= 0) {
return cmd_results_new(CMD_INVALID, set_inc_cmd_name, "Invalid %s command "
"(ncol must be > 0)", set_inc_cmd_name);
}
inc = n - (int)container->nb_slave_groups;
} else { /* inc command */
if ((int)container->nb_slave_groups + n > 0) {
inc = n;
}
}
if (inc) {
container->nb_slave_groups += inc;
need_layout_update = true;
}
}
}
}

if (layout != old_layout) {
swayc_change_layout(container, layout);
update_layout_geometry(container, old_layout);
need_layout_update = true;
}
if (need_layout_update) {
update_geometry(container);
arrange_windows(container, container->width, container->height);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
8 changes: 7 additions & 1 deletion sway/commands/move.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct cmd_results *cmd_move(int argc, char **argv) {
if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
return error;
}
const char* expected_syntax = "Expected 'move <left|right|up|down>' or "
const char* expected_syntax = "Expected 'move <left|right|up|down|next|prev|first>' or "
"'move <container|window> to workspace <name>' or "
"'move <container|window|workspace> to output <name|direction>' or "
"'move position mouse'";
Expand All @@ -27,6 +27,12 @@ struct cmd_results *cmd_move(int argc, char **argv) {
move_container(view, MOVE_UP);
} else if (strcasecmp(argv[0], "down") == 0) {
move_container(view, MOVE_DOWN);
} else if (strcasecmp(argv[0], "next") == 0) {
move_container(view, MOVE_NEXT);
} else if (strcasecmp(argv[0], "prev") == 0) {
move_container(view, MOVE_PREV);
} else if (strcasecmp(argv[0], "first") == 0) {
move_container(view, MOVE_FIRST);
} else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
// "move container ...
if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 4))) {
Expand Down
Loading

0 comments on commit 81102e8

Please sign in to comment.