diff --git a/README.md b/README.md index 4b41650..9f835bb 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Tiling window manager for the [river](https://github.com/riverwm/river) wayland - Monocle with alternate borders - Layout toggling - Dwindling, diminishing and even stacks +- Inner, outer and smart gaps | Layout | Symbol | Master | Stack | Directions | |---------|-----------|--------|--------|-------------| @@ -34,6 +35,7 @@ Tiling window manager for the [river](https://github.com/riverwm/river) wayland * [Even](#even) * [Diminish](#diminish) * [Dwindle](#dwindle) +- [GAPS](#gaps) - [INSTALL](#install) * [Package Manager](#package-manager) * [From Source](#from-source) @@ -45,6 +47,7 @@ Tiling window manager for the [river](https://github.com/riverwm/river) wayland - [FAQ](#faq) * [Name Does Not Always Update](#name-does-not-always-update) * [Borders Are Not Shown](#borders-are-not-shown) + * [Random Pixels In Gaps When Using Fractional Scaling](#random-pixels-in-gaps-when-using-fractional-scaling) - [ISSUES](#issues) * [Problems](#problems) * [Ideas](#ideas) @@ -96,6 +99,9 @@ wideriver \ --ratio-master 0.50 \ --count-wide-left 0 \ --ratio-wide 0.35 \ + --no-smart-gaps \ + --inner-gaps 0 \ + --outer-gaps 0 \ --border-width 2 \ --border-width-monocle 0 \ --border-color-focused "0x93a1a1" \ @@ -306,6 +312,32 @@ Arranged in a dwindling manner alternating in both stack directions. Each view occupies half the available / remaining area. +## GAPS + +Gaps, in pixels, between windows may be injected. They are off by default. + +`--inner-gaps` (`i`) are between windows. + +`--outer-gaps` (`o`) are between the edge of the screen and windows. + +`--smart-gaps` automatically hides the gaps when there is only one view or monocle layout. + + ---------------------------------------------------------- + | o o o o o o o o o o o o | + | ---------------------- ----------------------- | + | o | | i | | o | + | | | | | | + | o | | i | | o | + | | | ----------------------- | + | o | | i i i i i i i o | + | | | ----------------------- | + | o | | i | | o | + | | | | | | + | o | | i | | o | + | ---------------------- ----------------------- | + | o o o o o o o o o o o o | + ---------------------------------------------------------- + ## INSTALL ### Package Manager @@ -344,6 +376,10 @@ Should install under `/usr/local` --count-wide-left count 0 0 <= count --ratio-wide ratio 0.35 0.1 <= ratio <= 0.9 + --(no-)smart-gaps + --inner-gaps pixels 0 0 <= gap size + --outer-gaps pixels 0 0 <= gap size + --border-width pixels 2 0 <= width --border-width-monocle pixels 0 0 <= width @@ -388,6 +424,15 @@ Initial number of views in the wide layout’s left stack area, default `1`, min `--ratio-wide` *ratio* Initial proportion of the width the wide layout’s master area occupies, default `0.35`, minimum `0.1`, maximum `0.9`. The default value is best suited to ultrawide monitors, a value of `0.5` may be more useful for 16:9 monitors. +`--(no-)smart-gaps` +Automatically hides the gaps when there is only one view or monocle layout. + +`--inner-gaps` *pixels* +Inner gaps width, default `0`, minimum `0`. + +`--outer-gaps` *pixels* +Outer gaps width, default `0`, minimum `0`. + `--border-width` *pixels* Border width for all layouts except monocle, default `2`, minimum `0`. @@ -457,6 +502,14 @@ You can still use client side decorations for specific applications e.g.: riverctl rule-add -app-id audacity csd ``` +### Random Pixels In Gaps When Using Fractional Scaling + +This is a known river and wlroots issue: https://codeberg.org/river/river/issues/816 + +Workaround: set a river background colour other than default black `0x000000` + +`riverctl background-color "0x010101"` is sufficiently close. + ## ISSUES ### Problems diff --git a/doc/templ/20.man.readme.md b/doc/templ/20.man.readme.md index d4dfbcd..25a4223 100644 --- a/doc/templ/20.man.readme.md +++ b/doc/templ/20.man.readme.md @@ -5,6 +5,7 @@ Tiling window manager for the [river](https://github.com/riverwm/river) wayland * Monocle with alternate borders * Layout toggling * Dwindling, diminishing and even stacks +* Inner, outer and smart gaps | Layout | Symbol | Master | Stack | Directions | |---------|-----------|--------|--------|--------------| @@ -260,4 +261,32 @@ Arranged in a dwindling manner alternating in both stack directions. Each view occupies half the available / remaining area. +## GAPS + +Gaps, in pixels, between windows may be injected. They are off by default. + +`--inner-gaps` (`i`) are between windows. + +`--outer-gaps` (`o`) are between the edge of the screen and windows. + +`--smart-gaps` automatically hides the gaps when there is only one view or monocle layout. + +``` + ---------------------------------------------------------- +| o o o o o o o o o o o o | +| ---------------------- ----------------------- | +| o | | i | | o | +| | | | | | +| o | | i | | o | +| | | ----------------------- | +| o | | i i i i i i i o | +| | | ----------------------- | +| o | | i | | o | +| | | | | | +| o | | i | | o | +| ---------------------- ----------------------- | +| o o o o o o o o o o o o | + ---------------------------------------------------------- +``` + [//]: # vim: set filetype=markdown ts=4 sw=4 et : diff --git a/doc/templ/40.man.readme.md b/doc/templ/40.man.readme.md index 1741910..d8c0e66 100644 --- a/doc/templ/40.man.readme.md +++ b/doc/templ/40.man.readme.md @@ -26,6 +26,15 @@ You may wish to set this to 0 for a more natural or intuitive feel when launchin : Initial proportion of the width the wide layout's master area occupies, default `0.35`, minimum `0.1`, maximum `0.9`. The default value is best suited to ultrawide monitors, a value of `0.5` may be more useful for 16:9 monitors. +`--(no-)smart-gaps` +: Automatically hides the gaps when there is only one view or monocle layout. + +`--inner-gaps` *pixels* +: Inner gaps width, default `0`, minimum `0`. + +`--outer-gaps` *pixels* +: Outer gaps width, default `0`, minimum `0`. + `--border-width` *pixels* : Border width for all layouts except monocle, default `2`, minimum `0`. @@ -103,6 +112,14 @@ You can still use client side decorations for specific applications e.g.: riverctl rule-add -app-id audacity csd ``` +### Random Pixels In Gaps When Using Fractional Scaling + +This is a known river and wlroots issue: https://codeberg.org/river/river/issues/816 + +Workaround: set a river background colour other than default black `0x000000` + +`riverctl background-color "0x010101"` is sufficiently close. + ## ISSUES ### Problems diff --git a/inc/arrange.h b/inc/arrange.h index e99f6f8..8bcc9a5 100644 --- a/inc/arrange.h +++ b/inc/arrange.h @@ -49,6 +49,7 @@ void arrange_wide(const struct Demand *demand, // append many new Box to views with full usable area void arrange_monocle(const struct Demand *demand, + const struct Tag* const tag, struct SList **views); // recursively append new Box to views as per stack @@ -58,6 +59,7 @@ void arrange_views(const struct Demand *demand, const enum Cardinal dir_next, const uint32_t num_total, const uint32_t num_remaining, + const uint32_t inner_gap, const struct Box box_total, const struct Box box_remaining, struct SList **views); diff --git a/inc/cfg.h b/inc/cfg.h index 38e1206..ebc386d 100644 --- a/inc/cfg.h +++ b/inc/cfg.h @@ -18,6 +18,14 @@ #define RATIO_MASTER_DEFAULT 0.5 #define RATIO_WIDE_DEFAULT 0.35 +#define SMART_GAPS_DEFAULT false + +#define INNER_GAPS_MIN 0 +#define INNER_GAPS_DEFAULT 0 + +#define OUTER_GAPS_MIN 0 +#define OUTER_GAPS_DEFAULT 0 + #define BORDER_WIDTH_MIN 0 #define BORDER_WIDTH_DEFAULT 2 @@ -41,6 +49,9 @@ struct Cfg { double ratio_master; uint32_t count_wide_left; double ratio_wide; + bool smart_gaps; + uint32_t inner_gaps; + uint32_t outer_gaps; size_t border_width; size_t border_width_monocle; char border_color_focused[11]; @@ -56,6 +67,9 @@ bool cfg_set_count_master(const char *s); bool cfg_set_ratio_master(const char *s); bool cfg_set_count_wide_left(const char *s); bool cfg_set_ratio_wide(const char *s); +void cfg_set_smart_gaps(bool smart_gaps); +bool cfg_set_inner_gaps(const char *s); +bool cfg_set_outer_gaps(const char *s); bool cfg_set_border_width(const char *s); bool cfg_set_border_width_monocle(const char *s); diff --git a/inc/tag.h b/inc/tag.h index 5d31df2..1978fae 100644 --- a/inc/tag.h +++ b/inc/tag.h @@ -1,6 +1,7 @@ #ifndef TAG_H #define TAG_H +#include #include #include "enum.h" @@ -15,6 +16,9 @@ struct Tag { double ratio_master; uint32_t count_wide_left; double ratio_wide; + bool smart_gaps; + uint32_t inner_gaps; + uint32_t outer_gaps; }; struct Tag *tag_init(const uint32_t mask); diff --git a/man/wideriver.1 b/man/wideriver.1 index ea990a4..777041e 100644 --- a/man/wideriver.1 +++ b/man/wideriver.1 @@ -1,30 +1,12 @@ '\" t -.\" Automatically generated by Pandoc 3.1.1 +.\" Automatically generated by Pandoc 3.1.8 .\" -.\" Define V font for inline verbatim, using C font in formats -.\" that render this, and otherwise B font. -.ie "\f[CB]x\f[]"x" \{\ -. ftr V B -. ftr VI BI -. ftr VB B -. ftr VBI BI -.\} -.el \{\ -. ftr V CR -. ftr VI CI -. ftr VB CB -. ftr VBI CBI -.\} -.TH "WIDERIVER" "1" "2024/04/06" "wideriver" "User Manuals" -.hy +.TH "WIDERIVER" "1" "2024/04/23" "wideriver" "User Manuals" .SH NAME -.PP -\f[V]wideriver\f[R] - tiling window manager for the river wayland compositor +\f[CR]wideriver\f[R] - tiling window manager for the river wayland compositor .SH SYNOPSIS -.PP -\f[V]wideriver\f[R] [\f[I]OPTION\f[R]\&...|\f[I]COMMAND\f[R]\&...] +\f[CR]wideriver\f[R] [\f[I]OPTION\f[R]\&...|\f[I]COMMAND\f[R]\&...] .SH DESCRIPTION -.PP Tiling window manager for the river (https://github.com/riverwm/river) wayland compositor, inspired by dwm (https://dwm.suckless.org/) and xmonad (https://xmonad.org/) .IP \[bu] 2 Per-tag state @@ -36,6 +18,8 @@ Monocle with alternate borders Layout toggling .IP \[bu] 2 Dwindling, diminishing and even stacks +.IP \[bu] 2 +Inner, outer and smart gaps .PP .TS tab(@); @@ -55,7 +39,7 @@ _ T{ Left T}@T{ -\f[V]│ ├─┤\f[R] +\f[CR]│ ├─┤\f[R] T}@T{ Left T}@T{ @@ -66,7 +50,7 @@ T} T{ Right T}@T{ -\f[V]├─┤ │\f[R] +\f[CR]├─┤ │\f[R] T}@T{ Right T}@T{ @@ -77,7 +61,7 @@ T} T{ Top T}@T{ -\f[V]├─┬─┤\f[R] +\f[CR]├─┬─┤\f[R] T}@T{ Top T}@T{ @@ -88,7 +72,7 @@ T} T{ Bottom T}@T{ -\f[V]├─┴─┤\f[R] +\f[CR]├─┴─┤\f[R] T}@T{ Bottom T}@T{ @@ -99,7 +83,7 @@ T} T{ Wide T}@T{ -\f[V]├─┤ ├─┤\f[R] +\f[CR]├─┤ ├─┤\f[R] T}@T{ Mid T}@T{ @@ -118,7 +102,7 @@ T} T{ Monocle T}@T{ -\f[V]│ n │\f[R] +\f[CR]│ n │\f[R] T}@T{ All T}@T{ @@ -128,18 +112,15 @@ T}@T{ T} .TE .SH QUICK START -.PP -\f[V]$XDG_CONFIG_HOME/river/init\f[R] +\f[CR]$XDG_CONFIG_HOME/river/init\f[R] .PP Set server side decorations. .PP Required to display borders on all windows. .IP -.nf -\f[C] +.EX riverctl rule-add ssd -\f[R] -.fi +.EE .PP Set the layout generator and start it. .PP @@ -147,8 +128,7 @@ All defaults shown here, none are required. .PP Log file is strongly recommended. .IP -.nf -\f[C] +.EX # set layout manager riverctl default-layout wideriver @@ -161,6 +141,9 @@ wideriver \[rs] --ratio-master 0.50 \[rs] --count-wide-left 0 \[rs] --ratio-wide 0.35 \[rs] + --no-smart-gaps \[rs] + --inner-gaps 0 \[rs] + --outer-gaps 0 \[rs] --border-width 2 \[rs] --border-width-monocle 0 \[rs] --border-color-focused \[dq]0x93a1a1\[dq] \[rs] @@ -168,13 +151,11 @@ wideriver \[rs] --border-color-unfocused \[dq]0x586e75\[dq] \[rs] --log-threshold info \[rs] > \[dq]/tmp/wideriver.${XDG_VTNR}.${USER}.log\[dq] 2>&1 & -\f[R] -.fi +.EE .PP Create some command mappings e.g. .IP -.nf -\f[C] +.EX riverctl map normal $mod1 up send-layout-cmd wideriver \[dq]--layout monocle\[dq] riverctl map normal $mod1 down send-layout-cmd wideriver \[dq]--layout wide\[dq] riverctl map normal $mod1 left send-layout-cmd wideriver \[dq]--layout left\[dq] @@ -193,15 +174,12 @@ riverctl map normal $mod2 minus send-layout-cmd wideriver \[dq]--count -1\[dq] riverctl map normal $mod2 e send-layout-cmd wideriver \[dq]--stack even\[dq] riverctl map normal $mod2 w send-layout-cmd wideriver \[dq]--stack dwindle\[dq] riverctl map normal $mod2 i send-layout-cmd wideriver \[dq]--stack diminish\[dq] -\f[R] -.fi +.EE .SH EXAMPLE LAYOUTS AND STACKS .SS Left, Dwindle -.PP -\f[V]riverctl send-layout-cmd wideriver \[dq]--layout left --stack dwindle --count 1 --ratio 0.5\[dq]\f[R] +\f[CR]riverctl send-layout-cmd wideriver \[dq]--layout left --stack dwindle --count 1 --ratio 0.5\[dq]\f[R] .IP -.nf -\f[C] +.EX Master Stack ____________________________________________________________ | | | @@ -216,20 +194,17 @@ ____________________________________________________________ | | | | | | | | 5 | 6 | |____________________________|______________|______|_______| -\f[R] -.fi +.EE .PP The master area occupies half of the available space and contains one view. .PP The stack is filled in a diminishing manner in a downward then rightward directions. .PP -2 may be moved into the master area with \f[V]riverctl send-layout-cmd wideriver \[dq]--count +1\[dq]\f[R] and will be evenly placed below 1. +2 may be moved into the master area with \f[CR]riverctl send-layout-cmd wideriver \[dq]--count +1\[dq]\f[R] and will be evenly placed below 1. .SS Right, Even -.PP -\f[V]riverctl send-layout-cmd wideriver \[dq]--layout right --stack even --count 2 --ratio 0.55\[dq]\f[R] +\f[CR]riverctl send-layout-cmd wideriver \[dq]--layout right --stack even --count 2 --ratio 0.55\[dq]\f[R] .IP -.nf -\f[C] +.EX Stack Master ____________________________________________________________ | | | @@ -244,18 +219,15 @@ ____________________________________________________________ | | | | 5 | | |_________________________|________________________________| -\f[R] -.fi +.EE .PP The master area occupies 55% of the available width and contains two views split evenly. .PP The stack is split evenly and is filled in a downwards direction. .SS Wide, Diminish -.PP -\f[V]riverctl send-layout-cmd wideriver \[dq]--layout wide --stack diminish --count 3 --ratio 0.4\[dq]\f[R] +\f[CR]riverctl send-layout-cmd wideriver \[dq]--layout wide --stack diminish --count 3 --ratio 0.4\[dq]\f[R] .IP -.nf -\f[C] +.EX Left Stack Master Right Stack ________________________________________________________________________________ | 1 | | | @@ -270,8 +242,7 @@ ________________________________________________________________________________ | 3 | | 7 | | | |_______________________| |______________________|_______________________________|__________8____________| -\f[R] -.fi +.EE .PP The master area occupies 40% of the available with and contains one view. .PP @@ -279,14 +250,12 @@ The left stack contains 3 views, the right stack the remainder. .PP The left and right stacks each occupy 30% of the available width. .PP -5 may be moved into the master area with \f[V]riverctl send-layout-cmd wideriver \[dq]--count +1\[dq]\f[R]. +5 may be moved into the master area with \f[CR]riverctl send-layout-cmd wideriver \[dq]--count +1\[dq]\f[R]. 4 will be placed at the \[lq]top\[rq] of the stack, below 3. .SS Monocle -.PP -\f[V]riverctl send-layout-cmd wideriver \[dq]--layout monocle\[dq]\f[R] +\f[CR]riverctl send-layout-cmd wideriver \[dq]--layout monocle\[dq]\f[R] .IP -.nf -\f[C] +.EX ____________________________________________________________ | | | | @@ -300,12 +269,10 @@ ____________________________________________________________ | 5 | | 6 | |__________________________________________________________| -\f[R] -.fi +.EE .PP Only the currently focused view will be visible. .SH LAYOUTS -.PP The symbol is the layout name which may be shown in a status bar such as Waybar (https://github.com/Alexays/Waybar)\[cq]s river/layout (https://github.com/Alexays/Waybar/wiki/Module:-River#layout) module. .PP Dynamic settings are available via COMMANDS @@ -316,18 +283,16 @@ Dynamic settings are available via COMMANDS .PP When multiple tags are focused, the state is persisted for only the lowest tag. .SS Left / Right -.PP One master area occupying the full height of the available area with a stack area to the left or right. .PP \f[I]ratio\f[R] is the proportion of the available area occupied by master. .PP \f[I]count\f[R] is the number of evenly evenly stacked views in the master area. .PP -Left: \f[V]│ ├─┤\f[R] when \f[I]count\f[R] > 0 otherwise \f[V]│├──┤\f[R] +Left: \f[CR]│ ├─┤\f[R] when \f[I]count\f[R] > 0 otherwise \f[CR]│├──┤\f[R] .PP -Right: \f[V]├─┤ │\f[R] when \f[I]count\f[R] > 0 otherwise \f[V]├──┤│\f[R] +Right: \f[CR]├─┤ │\f[R] when \f[I]count\f[R] > 0 otherwise \f[CR]├──┤│\f[R] .SS Wide -.PP One master area occupying the full height of the available area with a stack area to the left and the right. .PP \f[I]ratio\f[R] is the proportion of the available area occupied by master. @@ -337,158 +302,182 @@ Stacks occupy half of the remaining area. .PP Master is centred when there are left and right stacks, otherwise it expands into the area that would be occupied the empty stacks. .PP -\f[V]├─┤ ├─┤\f[R] when \f[I]count\f[R] > 0 otherwise \f[V]││ ├─┤\f[R] +\f[CR]├─┤ ├─┤\f[R] when \f[I]count\f[R] > 0 otherwise \f[CR]││ ├─┤\f[R] .SS Monocle -.PP Only one view is focused, occupying all of the available space. .PP -\f[V]│ n │\f[R] with \f[V]n\f[R] showing number of views only when greater than 1. +\f[CR]│ n │\f[R] with \f[CR]n\f[R] showing number of views only when greater than 1. .SH STACK ARRANGEMENTS -.PP 3 arrangements are available for the stack area. It is persisted per tag and applied to all layouts for that tag. See above for an example of each arrangement. .PP Stacks follow one or two directions determined by the layout. .SS Even -.PP This is the \[lq]traditional\[rq] arrangement with uniformly sized stack views. .PP Arranged in a column or row in the first stack direction only. .SS Diminish -.PP Arranged in a column or row in the first stack direction only. .PP Height or width diminishes according to the view\[cq]s position in the stack: .PP -\f[V]2p / (n\[ha]2 + n)\f[R] +\f[CR]2p / (n\[ha]2 + n)\f[R] .PP -\f[V]n\f[R] number of views in the stack +\f[CR]n\f[R] number of views in the stack .PP -\f[V]p\f[R] position in the stack +\f[CR]p\f[R] position in the stack .SS Dwindle -.PP Arranged in a dwindling manner alternating in both stack directions. .PP Each view occupies half the available / remaining area. +.SH GAPS +Gaps, in pixels, between windows may be injected. +They are off by default. +.PP +\f[CR]--inner-gaps\f[R] (\f[CR]i\f[R]) are between windows. +.PP +\f[CR]--outer-gaps\f[R] (\f[CR]o\f[R]) are between the edge of the screen and windows. +.PP +\f[CR]--smart-gaps\f[R] automatically hides the gaps when there is only one view or monocle layout. +.IP +.EX + ---------------------------------------------------------- +| o o o o o o o o o o o o | +| ---------------------- ----------------------- | +| o | | i | | o | +| | | | | | +| o | | i | | o | +| | | ----------------------- | +| o | | i i i i i i i o | +| | | ----------------------- | +| o | | i | | o | +| | | | | | +| o | | i | | o | +| ---------------------- ----------------------- | +| o o o o o o o o o o o o | + ---------------------------------------------------------- +.EE .SH OPTIONS .TP -\f[V]--layout\f[R] \f[V]monocle\f[R]|\f[V]left\f[R]|\f[V]right\f[R]|\f[V]top\f[R]|\f[V]bottom\f[R]|\f[V]wide\f[R] -Initial layout, default \f[V]left\f[R]. +\f[CR]--layout\f[R] \f[CR]monocle\f[R]|\f[CR]left\f[R]|\f[CR]right\f[R]|\f[CR]top\f[R]|\f[CR]bottom\f[R]|\f[CR]wide\f[R] +Initial layout, default \f[CR]left\f[R]. .TP -\f[V]--layout-alt\f[R] \f[V]monocle\f[R]|\f[V]left\f[R]|\f[V]right\f[R]|\f[V]top\f[R]|\f[V]bottom\f[R]|\f[V]wide\f[R] -Initial alternate layout, default \f[V]monocle\f[R]. -Use \f[V]--layout-toggle\f[R] to switch to alternate layout. +\f[CR]--layout-alt\f[R] \f[CR]monocle\f[R]|\f[CR]left\f[R]|\f[CR]right\f[R]|\f[CR]top\f[R]|\f[CR]bottom\f[R]|\f[CR]wide\f[R] +Initial alternate layout, default \f[CR]monocle\f[R]. +Use \f[CR]--layout-toggle\f[R] to switch to alternate layout. .TP -\f[V]--stack\f[R] \f[V]even\f[R]|\f[V]diminish\f[R]|\f[V]dwindle\f[R] -Initial stacking method, default \f[V]dwindle\f[R]. +\f[CR]--stack\f[R] \f[CR]even\f[R]|\f[CR]diminish\f[R]|\f[CR]dwindle\f[R] +Initial stacking method, default \f[CR]dwindle\f[R]. .TP -\f[V]--count-master\f[R] \f[I]count\f[R] -Initial number of views in the master area, default \f[V]1\f[R], minimum \f[V]0\f[R]. +\f[CR]--count-master\f[R] \f[I]count\f[R] +Initial number of views in the master area, default \f[CR]1\f[R], minimum \f[CR]0\f[R]. Does not apply to wide layout. .TP -\f[V]--ratio-master\f[R] \f[I]ratio\f[R] -Initial proportion of the width or height the master area occupies, default \f[V]0.5\f[R], minimum \f[V]0.1\f[R], maximum \f[V]0.9\f[R]. +\f[CR]--ratio-master\f[R] \f[I]ratio\f[R] +Initial proportion of the width or height the master area occupies, default \f[CR]0.5\f[R], minimum \f[CR]0.1\f[R], maximum \f[CR]0.9\f[R]. Does not apply to wide layout. .TP -\f[V]--count-wide-left\f[R] \f[I]count\f[R] -Initial number of views in the wide layout\[cq]s left stack area, default \f[V]1\f[R], minimum \f[V]0\f[R]. +\f[CR]--count-wide-left\f[R] \f[I]count\f[R] +Initial number of views in the wide layout\[cq]s left stack area, default \f[CR]1\f[R], minimum \f[CR]0\f[R]. You may wish to set this to 0 for a more natural or intuitive feel when launching the first two views. .TP -\f[V]--ratio-wide\f[R] \f[I]ratio\f[R] -Initial proportion of the width the wide layout\[cq]s master area occupies, default \f[V]0.35\f[R], minimum \f[V]0.1\f[R], maximum \f[V]0.9\f[R]. -The default value is best suited to ultrawide monitors, a value of \f[V]0.5\f[R] may be more useful for 16:9 monitors. +\f[CR]--ratio-wide\f[R] \f[I]ratio\f[R] +Initial proportion of the width the wide layout\[cq]s master area occupies, default \f[CR]0.35\f[R], minimum \f[CR]0.1\f[R], maximum \f[CR]0.9\f[R]. +The default value is best suited to ultrawide monitors, a value of \f[CR]0.5\f[R] may be more useful for 16:9 monitors. +.TP +\f[CR]--(no-)smart-gaps\f[R] +Automatically hides the gaps when there is only one view or monocle layout. +.TP +\f[CR]--inner-gaps\f[R] \f[I]pixels\f[R] +Inner gaps width, default \f[CR]0\f[R], minimum \f[CR]0\f[R]. .TP -\f[V]--border-width\f[R] \f[I]pixels\f[R] -Border width for all layouts except monocle, default \f[V]2\f[R], minimum \f[V]0\f[R]. +\f[CR]--outer-gaps\f[R] \f[I]pixels\f[R] +Outer gaps width, default \f[CR]0\f[R], minimum \f[CR]0\f[R]. .TP -\f[V]--border-width-monocle\f[R] \f[I]pixels\f[R] -Border width for monocle layout, default \f[V]0\f[R], minimum \f[V]0\f[R]. +\f[CR]--border-width\f[R] \f[I]pixels\f[R] +Border width for all layouts except monocle, default \f[CR]2\f[R], minimum \f[CR]0\f[R]. .TP -\f[V]--border-color-focused\f[R] \f[V]0x\f[R]\f[I]RRGGBB\f[R][\f[I]AA\f[R]] -Border color for focused views in all layouts excluding monocle, default \f[V]0x93a1a1\f[R]. +\f[CR]--border-width-monocle\f[R] \f[I]pixels\f[R] +Border width for monocle layout, default \f[CR]0\f[R], minimum \f[CR]0\f[R]. .TP -\f[V]--border-color-focused-monocle\f[R] \f[V]0x\f[R]\f[I]RRGGBB\f[R][\f[I]AA\f[R]] -Border color for focused view in monocle layout, default \f[V]0x586e75\f[R]. +\f[CR]--border-color-focused\f[R] \f[CR]0x\f[R]\f[I]RRGGBB\f[R][\f[I]AA\f[R]] +Border color for focused views in all layouts excluding monocle, default \f[CR]0x93a1a1\f[R]. +.TP +\f[CR]--border-color-focused-monocle\f[R] \f[CR]0x\f[R]\f[I]RRGGBB\f[R][\f[I]AA\f[R]] +Border color for focused view in monocle layout, default \f[CR]0x586e75\f[R]. It is recommended to set this to the unfocused color or a darker colour as an always focused border can be distracting. .TP -\f[V]--border-color-unfocused\f[R] \f[V]0x\f[R]\f[I]RRGGBB\f[R][\f[I]AA\f[R]] -Border color for unfocused views in all layouts, default \f[V]0x586e75\f[R]. +\f[CR]--border-color-unfocused\f[R] \f[CR]0x\f[R]\f[I]RRGGBB\f[R][\f[I]AA\f[R]] +Border color for unfocused views in all layouts, default \f[CR]0x586e75\f[R]. Does not apply for monocle layout. .TP -\f[V]--log-threshold\f[R] \f[V]debug\f[R]|\f[V]info\f[R]|\f[V]warning\f[R]|\f[V]error\f[R] -Minimum log level, default \f[V]info\f[R]. +\f[CR]--log-threshold\f[R] \f[CR]debug\f[R]|\f[CR]info\f[R]|\f[CR]warning\f[R]|\f[CR]error\f[R] +Minimum log level, default \f[CR]info\f[R]. .SH COMMANDS -.PP When multiple tags are focused, the command is applied to and persisted for only the lowest tag. .TP -\f[V]--layout\f[R] \f[V]monocle\f[R]|\f[V]left\f[R]|\f[V]right\f[R]|\f[V]top\f[R]|\f[V]bottom\f[R]|\f[V]wide\f[R] +\f[CR]--layout\f[R] \f[CR]monocle\f[R]|\f[CR]left\f[R]|\f[CR]right\f[R]|\f[CR]top\f[R]|\f[CR]bottom\f[R]|\f[CR]wide\f[R] Set layout persistently for the tag, updating the alternate layout. .TP -\f[V]--layout-toggle\f[R] +\f[CR]--layout-toggle\f[R] Set layout to the alternate (previous) for the tag. .TP -\f[V]--stack\f[R] \f[V]diminish\f[R]|\f[V]dwindle\f[R]|\f[V]dwindle\f[R] +\f[CR]--stack\f[R] \f[CR]diminish\f[R]|\f[CR]dwindle\f[R]|\f[CR]dwindle\f[R] Set stacking method persistently for the tag. Applies to all layouts for the tag. .TP -\f[V]--count\f[R] [\f[V]+-\f[R]]\f[I]count\f[R] -Increment, decrement or set the master count, minimum \f[V]0\f[R]. +\f[CR]--count\f[R] [\f[CR]+-\f[R]]\f[I]count\f[R] +Increment, decrement or set the master count, minimum \f[CR]0\f[R]. For wide layout this is instead the left stack count. Discrete value for wide and all other layouts are persisted per tag. -Prefix with \f[V]+\f[R] to increment, \f[V]-\f[R] to decrement, or an absolute value. +Prefix with \f[CR]+\f[R] to increment, \f[CR]-\f[R] to decrement, or an absolute value. .TP -\f[V]--ratio\f[R] [\f[V]+-\f[R]]\f[I]pixels\f[R] -Increase, decrease or set the master ratio: the proportion of the width or height the master area occupies, minimum \f[V]0.1\f[R], maximum \f[V]0.9\f[R]. +\f[CR]--ratio\f[R] [\f[CR]+-\f[R]]\f[I]pixels\f[R] +Increase, decrease or set the master ratio: the proportion of the width or height the master area occupies, minimum \f[CR]0.1\f[R], maximum \f[CR]0.9\f[R]. Discrete tiling and wide values persisted per tag. -Prefix with \f[V]+\f[R] to increase, \f[V]-\f[R] to decrease, or an absolute value. +Prefix with \f[CR]+\f[R] to increase, \f[CR]-\f[R] to decrease, or an absolute value. .SH RECIPES .SS Wide Shuffling -.PP You can \[lq]shuffle\[rq] views through master, focusing the new master using: .PP -\f[V]riverctl send-layout-cmd wideriver \[aq]--count +1\[aq] && riverctl focus-view next\[dq]\f[R] +\f[CR]riverctl send-layout-cmd wideriver \[aq]--count +1\[aq] && riverctl focus-view next\[dq]\f[R] .PP -\f[V]riverctl send-layout-cmd wideriver \[aq]--count -1\[aq] && riverctl focus-view previous\[dq]\f[R] +\f[CR]riverctl send-layout-cmd wideriver \[aq]--count -1\[aq] && riverctl focus-view previous\[dq]\f[R] .SH FAQ .SS Name Does Not Always Update -.PP The layout name will not update when there are no views for the selected tags. This can occurs when setting a tag with no views or changing the layout for a tag with no views. .PP This may be resolved with a river enhancement: #1004 (https://github.com/riverwm/river/issues/1002) .SS Borders Are Not Shown -.PP Please ensure you have enabled server side decorations i.e.\ the borders: .IP -.nf -\f[C] +.EX riverctl rule-add ssd -\f[R] -.fi +.EE .PP You can still use client side decorations for specific applications e.g.: .IP -.nf -\f[C] +.EX riverctl rule-add -app-id audacity csd -\f[R] -.fi +.EE +.SS Random Pixels In Gaps When Using Fractional Scaling +This is a known river and wlroots issue: https://codeberg.org/river/river/issues/816 +.PP +Workaround: set a river background colour other than default black \f[CR]0x000000\f[R] +.PP +\f[CR]riverctl background-color \[dq]0x010101\[dq]\f[R] is sufficiently close. .SH ISSUES .SS Problems -.PP Please raise a Bug Report (https://github.com/alex-courtis/wideriver/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml) .SS Ideas -.PP Please create a Feature Request (https://github.com/alex-courtis/wideriver/issues/new?assignees=&labels=feature&projects=&template=feature_request.yml) .SS Questions or Discussions -.PP Please raise an Issue (https://github.com/alex-courtis/wideriver/issues/new) .SS Contributions -.PP CONTRIBUTING.md is most gratefully appreciated. .SH SEE ALSO -.PP https://github.com/alex-courtis/wideriver .SH AUTHORS Alexander Courtis. diff --git a/src/args.c b/src/args.c index 524fcae..f731fa3 100644 --- a/src/args.c +++ b/src/args.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -19,15 +20,19 @@ static struct option cli_long_options[] = { { "ratio-master", required_argument, 0, 0, }, // 4 { "count-wide-left", required_argument, 0, 0, }, // 5 { "ratio-wide", required_argument, 0, 0, }, // 6 - { "border-width", required_argument, 0, 0, }, // 7 - { "border-width-monocle", required_argument, 0, 0, }, // 8 - { "border-color-focused", required_argument, 0, 0, }, // 9 - { "border-color-focused-monocle", required_argument, 0, 0, }, // 10 - { "border-color-unfocused", required_argument, 0, 0, }, // 11 - { "help", no_argument, 0, 0, }, // 12 - { "help-defaults", no_argument, 0, 0, }, // 13 - { "log-threshold", required_argument, 0, 0, }, // 14 - { "version", no_argument, 0, 0, }, // 15 + { "smart-gaps", no_argument, 0, 0, }, // 7 + { "no-smart-gaps", no_argument, 0, 0, }, // 8 + { "inner-gaps", required_argument, 0, 0, }, // 9 + { "outer-gaps", required_argument, 0, 0, }, // 10 + { "border-width", required_argument, 0, 0, }, // 11 + { "border-width-monocle", required_argument, 0, 0, }, // 12 + { "border-color-focused", required_argument, 0, 0, }, // 13 + { "border-color-focused-monocle", required_argument, 0, 0, }, // 14 + { "border-color-unfocused", required_argument, 0, 0, }, // 15 + { "help", no_argument, 0, 0, }, // 16 + { "help-defaults", no_argument, 0, 0, }, // 17 + { "log-threshold", required_argument, 0, 0, }, // 18 + { "version", no_argument, 0, 0, }, // 19 { 0, 0, 0, 0, } }; @@ -95,54 +100,74 @@ void args_cli(int argc, char **argv) { } break; case 7: + cfg_set_smart_gaps(true); + break; + case 8: + cfg_set_smart_gaps(false); + break; + case 9: + if (!cfg_set_inner_gaps(optarg)) { + log_error("invalid --inner-gaps '%s'\n", optarg); + usage(EXIT_FAILURE); + return; + } + break; + case 10: + if (!cfg_set_outer_gaps(optarg)) { + log_error("invalid --outer-gaps '%s'\n", optarg); + usage(EXIT_FAILURE); + return; + } + break; + case 11: if (!cfg_set_border_width(optarg)) { log_error("invalid --border-width '%s'\n", optarg); usage(EXIT_FAILURE); return; } break; - case 8: + case 12: if (!cfg_set_border_width_monocle(optarg)) { log_error("invalid --border-width-monocle '%s'\n", optarg); usage(EXIT_FAILURE); return; } break; - case 9: + case 13: if (!cfg_set_border_color_focused(optarg)) { log_error("invalid --border-color-focused '%s'\n", optarg); usage(EXIT_FAILURE); return; } break; - case 10: + case 14: if (!cfg_set_border_color_focused_monocle(optarg)) { log_error("invalid --border-color-focused-monocle '%s'\n", optarg); usage(EXIT_FAILURE); return; } break; - case 11: + case 15: if (!cfg_set_border_color_unfocused(optarg)) { log_error("invalid --border-color-unfocused '%s'\n", optarg); usage(EXIT_FAILURE); return; } break; - case 12: + case 16: usage(EXIT_SUCCESS); return; - case 13: + case 17: usage_defaults(); return; - case 14: + case 18: if (!log_set_threshold(optarg)) { log_error("invalid --log-threshold '%s'\n", optarg); usage(EXIT_FAILURE); return; } break; - case 15: + case 19: fprintf(stdout, "wideriver version %s\n", VERSION); exit(EXIT_SUCCESS); return; @@ -160,6 +185,9 @@ void args_cli(int argc, char **argv) { log_info("--ratio-master %g", cfg->ratio_master); log_info("--count-wide-left %u", cfg->count_wide_left); log_info("--ratio-wide %g", cfg->ratio_wide); + log_info("--%ssmart-gaps", cfg->smart_gaps ? "" : "no-"); + log_info("--inner-gaps %u", cfg->inner_gaps); + log_info("--outer-gaps %u", cfg->outer_gaps); log_info("--border-width %u", cfg->border_width); log_info("--border-width-monocle %u", cfg->border_width_monocle); log_info("--border-color-focused %s", cfg->border_color_focused); diff --git a/src/arrange.c b/src/arrange.c index e9f9bf6..a7b8d00 100644 --- a/src/arrange.c +++ b/src/arrange.c @@ -77,17 +77,29 @@ void arrange_master_stack(const struct Demand* const demand, memset(master, 0, sizeof(struct Box)); memset(stack, 0, sizeof(struct Box)); + uint32_t inner_gap = tag->inner_gaps; + uint32_t outer_gap = tag->outer_gaps; + + if (demand->view_count == 1 && tag->smart_gaps) { + inner_gap = 0; + outer_gap = 0; + } + if (num_master == 0 && num_stack == 0) { return; } if (num_master == 0) { - stack->width = demand->usable_width; - stack->height = demand->usable_height; + stack->width = demand->usable_width - 2 * outer_gap; + stack->height = demand->usable_height - 2 * outer_gap; + stack->x = outer_gap; + stack->y = outer_gap; return; } if (num_stack == 0) { - master->width = demand->usable_width; - master->height = demand->usable_height; + master->width = demand->usable_width - 2 * outer_gap; + master->height = demand->usable_height - 2 * outer_gap; + master->x = outer_gap; + master->y = outer_gap; return; } @@ -95,18 +107,18 @@ void arrange_master_stack(const struct Demand* const demand, switch(tag->layout_cur) { case LEFT: case RIGHT: - master->width = demand->usable_width * tag->ratio_master + 0.5f; - master->height = demand->usable_height; - stack->width = demand->usable_width - master->width; - stack->height = demand->usable_height; + master->width = (demand->usable_width - 2 * outer_gap - inner_gap) * tag->ratio_master + 0.5f; + master->height = demand->usable_height - 2 * outer_gap; + stack->width = demand->usable_width - master->width - 2 * outer_gap - inner_gap; + stack->height = demand->usable_height - 2 * outer_gap; break; case TOP: case BOTTOM: - master->width = demand->usable_width; - master->height = demand->usable_height * tag->ratio_master + 0.5f; - stack->width = demand->usable_width; - stack->height = demand->usable_height - master->height; + master->width = demand->usable_width - 2 * outer_gap; + master->height = (demand->usable_height - 2 * outer_gap - inner_gap) * tag->ratio_master + 0.5f; + stack->width = demand->usable_width - 2 * outer_gap; + stack->height = demand->usable_height - master->height - 2 * outer_gap - inner_gap; break; default: @@ -117,18 +129,18 @@ void arrange_master_stack(const struct Demand* const demand, switch(tag->layout_cur) { case LEFT: case TOP: - master->x = 0; - master->y = 0; + master->x = outer_gap; + master->y = outer_gap; break; case RIGHT: - master->x = demand->usable_width - master->width; - master->y = 0; + master->x = outer_gap + stack->width + inner_gap; + master->y = outer_gap; break; case BOTTOM: - master->x = 0; - master->y = stack->height; + master->x = outer_gap; + master->y = outer_gap + stack->height + inner_gap; break; default: @@ -138,19 +150,19 @@ void arrange_master_stack(const struct Demand* const demand, // stack position switch(tag->layout_cur) { case LEFT: - stack->x = master->width; - stack->y = 0; + stack->x = outer_gap + master->width + inner_gap; + stack->y = outer_gap; break; case TOP: - stack->x = 0; - stack->y = master->height; + stack->x = outer_gap; + stack->y = outer_gap + master->height + inner_gap; break; case RIGHT: case BOTTOM: - stack->x = 0; - stack->y = 0; + stack->x = outer_gap; + stack->y = outer_gap; break; default: @@ -174,6 +186,14 @@ void arrange_wide(const struct Demand *demand, memset(before, 0, sizeof(struct Box)); memset(after, 0, sizeof(struct Box)); + uint32_t inner_gap = tag->inner_gaps; + uint32_t outer_gap = tag->outer_gaps; + + if (demand->view_count == 1 && tag->smart_gaps) { + inner_gap = 0; + outer_gap = 0; + } + // 000 if (num_master == 0 && num_before == 0 && num_after == 0) { return; @@ -181,78 +201,103 @@ void arrange_wide(const struct Demand *demand, // 010 if (!num_before && num_master && !num_after) { - master->width = demand->usable_width; - master->height = demand->usable_height; + master->width = demand->usable_width - 2 * outer_gap; + master->height = demand->usable_height - 2 * outer_gap; + master->x = outer_gap; + master->y = outer_gap; return; } // 001 if (!num_before && !num_master && num_after) { - after->width = demand->usable_width; - after->height = demand->usable_height; + after->width = demand->usable_width - 2 * outer_gap; + after->height = demand->usable_height - 2 * outer_gap; + after->x = outer_gap; + after->y = outer_gap; return; } // 100 if (num_before && !num_master && !num_after) { - before->width = demand->usable_width; - before->height = demand->usable_height; + before->width = demand->usable_width - 2 * outer_gap; + before->height = demand->usable_height - 2 * outer_gap; + before->x = outer_gap; + before->y = outer_gap; return; } // 101 if (num_before && !num_master && num_after) { - before->width = demand->usable_width / 2.0f + 0.5f; - before->height = demand->usable_height; - - after->width = demand->usable_width - before->width; - after->height = demand->usable_height; - after->x = before->width; + before->width = (demand->usable_width - 2 * outer_gap - inner_gap) / 2.0f + 0.5f; + before->height = demand->usable_height - 2 * outer_gap; + before->x = outer_gap; + before->y = outer_gap; + + after->width = demand->usable_width - before->width - 2 * outer_gap - inner_gap; + after->height = demand->usable_height - 2 * outer_gap; + after->x = outer_gap + before->width + inner_gap; + after->y = outer_gap; return; } // 011 if (!num_before && num_master && num_after) { - master->width = demand->usable_width * (tag->ratio_wide + (1.0f - tag->ratio_wide) / 2.0f) + 0.5f; - master->height = demand->usable_height; - - after->width = demand->usable_width - master->width; - after->height = demand->usable_height; - after->x = master->width; + master->width = (demand->usable_width - 2 * outer_gap - inner_gap) * (tag->ratio_wide + (1.0f - tag->ratio_wide) / 2.0f) + 0.5f; + master->height = demand->usable_height - 2 * outer_gap; + master->x = outer_gap; + master->y = outer_gap; + + after->width = demand->usable_width - master->width - 2 * outer_gap - inner_gap; + after->height = demand->usable_height - 2 * outer_gap; + after->x = outer_gap + master->width + inner_gap; + after->y = outer_gap; return; } // 110 if (num_before && num_master && !num_after) { - master->width = demand->usable_width * (tag->ratio_wide + (1.0f - tag->ratio_wide) / 2.0f) + 0.5f; - master->height = demand->usable_height; - master->x = demand->usable_width - master->width; - - before->width = master->x; - before->height = demand->usable_height; + master->width = (demand->usable_width - 2 * outer_gap - inner_gap) * (tag->ratio_wide + (1.0f - tag->ratio_wide) / 2.0f) + 0.5f; + master->height = demand->usable_height - 2 * outer_gap; + master->x = demand->usable_width - master->width - outer_gap; + master->y = outer_gap; + + before->width = master->x - outer_gap - inner_gap; + before->height = demand->usable_height - 2 * outer_gap; + before->x = outer_gap; + before->y = outer_gap; return; } // 111 - master->width = demand->usable_width * tag->ratio_wide + 0.5f; - master->height = demand->usable_height; + master->width = (demand->usable_width - 2 * (outer_gap + inner_gap)) * tag->ratio_wide + 0.5f; + master->height = demand->usable_height - 2 * outer_gap; master->x = (demand->usable_width - master->width) / 2.0f + 0.5f; + master->y = outer_gap; - before->width = master->x; - before->height = demand->usable_height; + before->width = master->x - outer_gap - inner_gap; + before->height = demand->usable_height - 2 * outer_gap; + before->x = outer_gap; + before->y = outer_gap; - after->width = demand->usable_width - master->width - before->width; - after->height = demand->usable_height; - after->x = master->x + master->width; + after->width = demand->usable_width - master->x - master->width - inner_gap - outer_gap; + after->height = demand->usable_height - 2 * outer_gap; + after->x = master->x + master->width + inner_gap; + after->y = outer_gap; } void arrange_monocle(const struct Demand *demand, + const struct Tag* const tag, struct SList **views) { if (!demand || !views) return; - struct Box usable = { 0, 0, demand->usable_width, demand->usable_height }; + uint32_t outer_gap = tag->smart_gaps ? 0 : tag->outer_gaps; + struct Box usable = { + .x = outer_gap, .y = outer_gap, + .width = demand->usable_width - 2 * outer_gap, + .height = demand->usable_height - 2 * outer_gap, + }; for (uint32_t i = 0; i < demand->view_count; i++) { struct Box *this = calloc(1, sizeof(struct Box)); @@ -268,6 +313,7 @@ void arrange_views(const struct Demand *demand, const enum Cardinal dir_next, const uint32_t num_total, const uint32_t num_remaining, + const uint32_t inner_gap, const struct Box box_total, const struct Box box_remaining, struct SList **views) { @@ -292,18 +338,18 @@ void arrange_views(const struct Demand *demand, uint32_t denom = 0; switch (stack) { case EVEN: - width = (double)(box_remaining.width) / num_remaining + 0.5f; - height = (double)(box_remaining.height) / num_remaining + 0.5f; + width = (double)(box_remaining.width - (num_remaining - 1) * inner_gap) / num_remaining + 0.5f; + height = (double)(box_remaining.height - (num_remaining - 1) * inner_gap) / num_remaining + 0.5f; break; case DIMINISH: for (uint32_t i = num_total; i > 0; i--) denom += i; - width = (double)(num_remaining) * box_total.width / denom + 0.5f; - height = (double)(num_remaining) * box_total.height / denom + 0.5f; + width = (double)(num_remaining) * (box_total.width - (num_total - 1) * inner_gap) / denom + 0.5f; + height = (double)(num_remaining) * (box_total.height - (num_total - 1) * inner_gap) / denom + 0.5f; break; case DWINDLE: - width = (double)(box_remaining.width) / 2 + 0.5f; - height = (double)(box_remaining.height) / 2 + 0.5f; + width = (double)(box_remaining.width - inner_gap) / 2 + 0.5f; + height = (double)(box_remaining.height - inner_gap) / 2 + 0.5f; break; } @@ -313,12 +359,12 @@ void arrange_views(const struct Demand *demand, case N: case S: this->height = height; - remaining.height -= this->height; + remaining.height -= this->height + inner_gap; break; case E: case W: this->width = width; - remaining.width -= this->width; + remaining.width -= this->width + inner_gap; break; } @@ -328,10 +374,10 @@ void arrange_views(const struct Demand *demand, this->y += box_remaining.height - this->height; break; case S: - remaining.y += this->height; + remaining.y += this->height + inner_gap; break; case E: - remaining.x += this->width; + remaining.x += this->width + inner_gap; break; case W: this->x += box_remaining.width - this->width; @@ -345,6 +391,7 @@ void arrange_views(const struct Demand *demand, stack == DWINDLE ? dir_next : dir_cur, stack == DWINDLE ? dir_cur : dir_next, num_total, num_remaining - 1, + inner_gap, box_total, remaining, views); } diff --git a/src/cfg.c b/src/cfg.c index 583ed9d..24ec893 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -15,6 +15,9 @@ struct Cfg c = { .ratio_master = RATIO_MASTER_DEFAULT, .count_wide_left = COUNT_WIDE_LEFT_DEFAULT, .ratio_wide = RATIO_WIDE_DEFAULT, + .smart_gaps = SMART_GAPS_DEFAULT, + .inner_gaps = INNER_GAPS_DEFAULT, + .outer_gaps = OUTER_GAPS_DEFAULT, .border_width = BORDER_WIDTH_DEFAULT, .border_width_monocle = BORDER_WIDTH_MONOCLE_DEFAULT, .border_color_focused = BORDER_COLOR_FOCUSED_DEFAULT, @@ -122,6 +125,34 @@ bool cfg_set_ratio_wide(const char *s) { } } +void cfg_set_smart_gaps(bool smart_gaps) { + c.smart_gaps = smart_gaps; +} + +bool cfg_set_inner_gaps(const char *s) { + char *endptr; + long l = strtol(s, &endptr, 10); + + if (*s == '\0' || endptr == s || *endptr != '\0' || l < INNER_GAPS_MIN) { + return false; + } else { + c.inner_gaps = l; + return true; + } +} + +bool cfg_set_outer_gaps(const char *s) { + char *endptr; + long l = strtol(s, &endptr, 10); + + if (*s == '\0' || endptr == s || *endptr != '\0' || l < OUTER_GAPS_MIN) { + return false; + } else { + c.outer_gaps = l; + return true; + } +} + bool cfg_set_border_width(const char *s) { char *endptr; long l = strtol(s, &endptr, 10); diff --git a/src/layout.c b/src/layout.c index 374dded..67c5741 100644 --- a/src/layout.c +++ b/src/layout.c @@ -130,30 +130,30 @@ struct SList *layout(const struct Demand *demand, const struct Tag *tag) { switch(tag->layout_cur) { case LEFT: // top to bottom, dwindle right down - arrange_views(demand, EVEN, S, S, num_master, num_master, box_master, box_master, &views); - arrange_views(demand, tag->stack, S, E, num_after, num_after, box_after, box_after, &views); + arrange_views(demand, EVEN, S, S, num_master, num_master, tag->inner_gaps, box_master, box_master, &views); + arrange_views(demand, tag->stack, S, E, num_after, num_after, tag->inner_gaps, box_after, box_after, &views); break; case RIGHT: // top to bottom, dwindle left down - arrange_views(demand, EVEN, S, S, num_master, num_master, box_master, box_master, &views); - arrange_views(demand, tag->stack, S, W, num_after, num_after, box_after, box_after, &views); + arrange_views(demand, EVEN, S, S, num_master, num_master, tag->inner_gaps, box_master, box_master, &views); + arrange_views(demand, tag->stack, S, W, num_after, num_after, tag->inner_gaps, box_after, box_after, &views); break; case TOP: // left to right, dwindle down right - arrange_views(demand, EVEN, E, E, num_master, num_master, box_master, box_master, &views); - arrange_views(demand, tag->stack, E, S, num_after, num_after, box_after, box_after, &views); + arrange_views(demand, EVEN, E, E, num_master, num_master, tag->inner_gaps, box_master, box_master, &views); + arrange_views(demand, tag->stack, E, S, num_after, num_after, tag->inner_gaps, box_after, box_after, &views); break; case BOTTOM: // left to right, dwindle up right - arrange_views(demand, EVEN, E, E, num_master, num_master, box_master, box_master, &views); - arrange_views(demand, tag->stack, E, N, num_after, num_after, box_after, box_after, &views); + arrange_views(demand, EVEN, E, E, num_master, num_master, tag->inner_gaps, box_master, box_master, &views); + arrange_views(demand, tag->stack, E, N, num_after, num_after, tag->inner_gaps, box_after, box_after, &views); break; case MONOCLE: - arrange_monocle(demand, &views); + arrange_monocle(demand, tag, &views); break; case WIDE: // left stack dwindle left up - arrange_views(demand, tag->stack, N, W, num_before, num_before, box_before, box_before, &views); + arrange_views(demand, tag->stack, N, W, num_before, num_before, tag->inner_gaps, box_before, box_before, &views); // reverse to push first view farthest away struct SList *views_reversed = NULL; @@ -163,10 +163,10 @@ struct SList *layout(const struct Demand *demand, const struct Tag *tag) { views = views_reversed; // only one master - arrange_views(demand, EVEN, S, S, num_master, num_master, box_master, box_master, &views); + arrange_views(demand, EVEN, S, S, num_master, num_master, tag->inner_gaps, box_master, box_master, &views); // right stack dwindle right down - arrange_views(demand, tag->stack, S, E, num_after, num_after, box_after, box_after, &views); + arrange_views(demand, tag->stack, S, E, num_after, num_after, tag->inner_gaps, box_after, box_after, &views); break; } diff --git a/src/tag.c b/src/tag.c index dfb2c07..9e9348e 100644 --- a/src/tag.c +++ b/src/tag.c @@ -19,6 +19,10 @@ struct Tag *tag_init(const uint32_t mask) { tag->count_wide_left = cfg->count_wide_left; tag->ratio_wide = cfg->ratio_wide; + tag->smart_gaps = cfg->smart_gaps; + tag->inner_gaps = cfg->inner_gaps; + tag->outer_gaps = cfg->outer_gaps; + tag->mask = mask; return tag; diff --git a/src/usage.c b/src/usage.c index aa281d2..b6bb9d1 100644 --- a/src/usage.c +++ b/src/usage.c @@ -23,6 +23,10 @@ void usage(const int status) { " --count-wide-left count %d %d <= count\n" " --ratio-wide ratio %.02f %.1g <= ratio <= %.1g\n" "\n" + " --(no-)smart-gaps\n" + " --inner-gaps pixels %d %d <= gap size\n" + " --outer-gaps pixels %d %d <= gap size\n" + "\n" " --border-width pixels %d %d <= width\n" " --border-width-monocle pixels %d %d <= width\n" "\n" @@ -51,6 +55,8 @@ void usage(const int status) { RATIO_MASTER_DEFAULT, RATIO_MIN, RATIO_MAX, COUNT_WIDE_LEFT_DEFAULT, COUNT_MIN, RATIO_WIDE_DEFAULT, RATIO_MIN, RATIO_MAX, + INNER_GAPS_DEFAULT, INNER_GAPS_MIN, + OUTER_GAPS_DEFAULT, OUTER_GAPS_MIN, BORDER_WIDTH_DEFAULT, BORDER_WIDTH_MIN, BORDER_WIDTH_MONOCLE_DEFAULT, BORDER_WIDTH_MONOCLE_MIN, BORDER_COLOR_FOCUSED_DEFAULT, @@ -76,6 +82,9 @@ void usage_defaults(void) { " --ratio-master %.02f \\\n" " --count-wide-left %d \\\n" " --ratio-wide %.02f \\\n" + " --no-smart-gaps \\\n" + " --inner-gaps %d \\\n" + " --outer-gaps %d \\\n" " --border-width %d \\\n" " --border-width-monocle %d \\\n" " --border-color-focused \"%s\" \\\n" @@ -86,6 +95,8 @@ void usage_defaults(void) { layout_name(LAYOUT_DEFAULT), layout_name(LAYOUT_ALT_DEFAULT), stack_name(STACK_DEFAULT), COUNT_MASTER_DEFAULT, RATIO_MASTER_DEFAULT, COUNT_WIDE_LEFT_DEFAULT, RATIO_WIDE_DEFAULT, + INNER_GAPS_DEFAULT, + OUTER_GAPS_DEFAULT, BORDER_WIDTH_DEFAULT, BORDER_WIDTH_MONOCLE_DEFAULT, BORDER_COLOR_FOCUSED_DEFAULT, BORDER_COLOR_FOCUSED_MONOCLE_DEFAULT, BORDER_COLOR_UNFOCUSED_DEFAULT, log_threshold_name(LOG_THRESHOLD_DEFAULT) diff --git a/tst/tst-args_cli.c b/tst/tst-args_cli.c index 66a4fce..b750871 100644 --- a/tst/tst-args_cli.c +++ b/tst/tst-args_cli.c @@ -32,7 +32,7 @@ int after_each(void **state) { } void args_parse_cli__valid(void **state) { - int argc = 27; + int argc = 32; char *argv[] = { "dummy", "--layout", "left", "--layout-alt", "right", @@ -41,6 +41,9 @@ void args_parse_cli__valid(void **state) { "--ratio-master", "0.2", "--count-wide-left", "8", "--ratio-wide", "0.8", + "--smart-gaps", + "--inner-gaps", "6", + "--outer-gaps", "6", "--border-width", "5", "--border-width-monocle", "10", "--border-color-focused", "0xAABBCC", @@ -58,6 +61,9 @@ void args_parse_cli__valid(void **state) { assert_float_equal(cfg->ratio_master, 0.2, 0.001); assert_int_equal(cfg->count_wide_left, 8); assert_float_equal(cfg->ratio_wide, 0.8, 0.001); + assert_true(cfg->smart_gaps); + assert_int_equal(cfg->inner_gaps, 6); + assert_int_equal(cfg->outer_gaps, 6); assert_int_equal(cfg->border_width, 5); assert_int_equal(cfg->border_width_monocle, 10); assert_str_equal(cfg->border_color_focused, "0xAABBCC"); @@ -72,6 +78,9 @@ void args_parse_cli__valid(void **state) { "--ratio-master 0.2\n" "--count-wide-left 8\n" "--ratio-wide 0.8\n" + "--smart-gaps\n" + "--inner-gaps 6\n" + "--outer-gaps 6\n" "--border-width 5\n" "--border-width-monocle 10\n" "--border-color-focused 0xAABBCC\n" @@ -172,6 +181,32 @@ void args_parse_cli__bad_ratio_wide(void **state) { assert_log(ERROR, "invalid --ratio-wide '-1'\n\n"); } +void args_parse_cli__bad_inner_gaps(void **state) { + int argc = 3; + char *argv[] = { "dummy", + "--inner-gaps", "-1", + }; + + expect_value(__wrap_usage, status, EXIT_FAILURE); + + args_cli(argc, argv); + + assert_log(ERROR, "invalid --inner-gaps '-1'\n\n"); +} + +void args_parse_cli__bad_outer_gaps(void **state) { + int argc = 3; + char *argv[] = { "dummy", + "--outer-gaps", "-1", + }; + + expect_value(__wrap_usage, status, EXIT_FAILURE); + + args_cli(argc, argv); + + assert_log(ERROR, "invalid --outer-gaps '-1'\n\n"); +} + void args_parse_cli__bad_border_width(void **state) { int argc = 3; char *argv[] = { "dummy", @@ -260,6 +295,8 @@ int main(void) { TEST(args_parse_cli__bad_ratio_master), TEST(args_parse_cli__bad_count_wide_left), TEST(args_parse_cli__bad_ratio_wide), + TEST(args_parse_cli__bad_inner_gaps), + TEST(args_parse_cli__bad_outer_gaps), TEST(args_parse_cli__bad_border_width), TEST(args_parse_cli__bad_border_width_monocle), TEST(args_parse_cli__bad_border_color_focused), diff --git a/tst/tst-arrange.c b/tst/tst-arrange.c index 2f061ce..d02b0d4 100644 --- a/tst/tst-arrange.c +++ b/tst/tst-arrange.c @@ -2,10 +2,13 @@ #include "asserts.h" #include +#include #include +#include "enum.h" #include "layout.h" #include "slist.h" +#include "tag.h" #include "arrange.h" @@ -28,8 +31,9 @@ int after_each(void **state) { void arrange_monocle__many(void **state) { struct SList *stack = NULL; struct Demand demand = { .view_count = 2, .usable_width = 5, .usable_height = 3 }; + struct Tag tag = { .layout_cur = MONOCLE, }; - arrange_monocle(&demand, &stack); + arrange_monocle(&demand, &tag, &stack); assert_int_equal(slist_length(stack), 2); @@ -39,9 +43,31 @@ void arrange_monocle__many(void **state) { slist_free_vals(&stack, NULL); } +void arrange_monocle__many_with_gaps(void **state) { + struct SList *stack = NULL; + struct Demand demand = { .view_count = 2, .usable_width = 5, .usable_height = 3 }; + struct Tag tag = { .layout_cur = MONOCLE, .smart_gaps = false, + .inner_gaps = 0, .outer_gaps = 1, }; + struct Tag smart_tag = { .layout_cur = MONOCLE, .smart_gaps = true, + .inner_gaps = 0, .outer_gaps = 1, }; + + arrange_monocle(&demand, &tag, &stack); + assert_int_equal(slist_length(stack), 2); + assert_box_equal(slist_at(stack, 1), 1, 1, 3, 1); + assert_box_equal(slist_at(stack, 0), 1, 1, 3, 1); + slist_free_vals(&stack, NULL); + + arrange_monocle(&demand, &smart_tag, &stack); + assert_int_equal(slist_length(stack), 2); + assert_box_equal(slist_at(stack, 0), 0, 0, 5, 3); + assert_box_equal(slist_at(stack, 1), 0, 0, 5, 3); + slist_free_vals(&stack, NULL); +} + int main(void) { const struct CMUnitTest tests[] = { TEST(arrange_monocle__many), + TEST(arrange_monocle__many_with_gaps), }; return RUN(tests); diff --git a/tst/tst-arrange_master_stack.c b/tst/tst-arrange_master_stack.c index d635db0..bbaaca1 100644 --- a/tst/tst-arrange_master_stack.c +++ b/tst/tst-arrange_master_stack.c @@ -2,6 +2,7 @@ #include "asserts.h" #include +#include #include "enum.h" #include "layout.h" @@ -64,6 +65,42 @@ void arrange_master_stack__zero_stack(void **state) { assert_box_equal(&stack, 0, 0, 0, 0); } +void arrange_master_stack__zero_master_with_gaps(void **state) { + struct Box master, stack; + + struct Demand demand = { .view_count = 1, .usable_width = 9, .usable_height = 5, }; + struct Tag tag = { .layout_cur = LEFT, .ratio_master = 0.5, + .smart_gaps = false, .inner_gaps = 1, .outer_gaps = 1, }; + struct Tag smart_tag = { .layout_cur = LEFT, .ratio_master = 0.5, + .smart_gaps = true, .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_master_stack(&demand, &tag, 0, 1, &master, &stack); + assert_box_equal(&master, 0, 0, 0, 0); + assert_box_equal(&stack, 1, 1, 7, 3); + + arrange_master_stack(&demand, &smart_tag, 0, 1, &master, &stack); + assert_box_equal(&master, 0, 0, 0, 0); + assert_box_equal(&stack, 0, 0, 9, 5); +} + +void arrange_master_stack__zero_stack_with_gaps(void **state) { + struct Box master, stack; + + struct Demand demand = { .view_count = 1, .usable_width = 9, .usable_height = 5, }; + struct Tag tag = { .layout_cur = LEFT, .ratio_master = 0.5, + .smart_gaps = false, .inner_gaps = 1, .outer_gaps = 1, }; + struct Tag smart_tag = { .layout_cur = LEFT, .ratio_master = 0.5, + .smart_gaps = true, .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_master_stack(&demand, &tag, 1, 0, &master, &stack); + assert_box_equal(&master, 1, 1, 7, 3); + assert_box_equal(&stack, 0, 0, 0, 0); + + arrange_master_stack(&demand, &smart_tag, 1, 0, &master, &stack); + assert_box_equal(&master, 0, 0, 9, 5); + assert_box_equal(&stack, 0, 0, 0, 0); +} + void arrange_master_stack__left(void **state) { struct Box master, stack; @@ -77,6 +114,18 @@ void arrange_master_stack__left(void **state) { assert_box_equal(&stack, 4, 0, 5, 5); } +void arrange_master_stack__left_with_gaps(void **state) { + struct Box master, stack; + + struct Demand demand = { .usable_width = 9, .usable_height = 5, }; + struct Tag tag = { .layout_cur = LEFT, .ratio_master = 0.4, + .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_master_stack(&demand, &tag, 1, 1, &master, &stack); + assert_box_equal(&master, 1, 1, 2, 3); + assert_box_equal(&stack, 4, 1, 4, 3); +} + void arrange_master_stack__right(void **state) { struct Box master, stack; @@ -90,6 +139,18 @@ void arrange_master_stack__right(void **state) { assert_box_equal(&stack, 0, 0, 5, 5); } +void arrange_master_stack__right_with_gaps(void **state) { + struct Box master, stack; + + struct Demand demand = { .usable_width = 9, .usable_height = 5, }; + struct Tag tag = { .layout_cur = RIGHT, .ratio_master = 0.4, + .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_master_stack(&demand, &tag, 1, 1, &master, &stack); + assert_box_equal(&master, 6, 1, 2, 3); + assert_box_equal(&stack, 1, 1, 4, 3); +} + void arrange_master_stack__top(void **state) { struct Box master, stack; @@ -103,6 +164,18 @@ void arrange_master_stack__top(void **state) { assert_box_equal(&stack, 0, 2, 9, 3); } +void arrange_master_stack__top_with_gaps(void **state) { + struct Box master, stack; + + struct Demand demand = { .usable_width = 9, .usable_height = 5, }; + struct Tag tag = { .layout_cur = TOP, .ratio_master = 0.3, + .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_master_stack(&demand, &tag, 1, 1, &master, &stack); + assert_box_equal(&master, 1, 1, 7, 1); + assert_box_equal(&stack, 1, 3, 7, 1); +} + void arrange_master_stack__bottom(void **state) { struct Box master, stack; @@ -116,6 +189,18 @@ void arrange_master_stack__bottom(void **state) { assert_box_equal(&stack, 0, 0, 9, 3); } +void arrange_master_stack__bottom_with_gaps(void **state) { + struct Box master, stack; + + struct Demand demand = { .usable_width = 9, .usable_height = 5, }; + struct Tag tag = { .layout_cur = BOTTOM, .ratio_master = 0.3, + .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_master_stack(&demand, &tag, 1, 1, &master, &stack); + assert_box_equal(&master, 1, 3, 7, 1); + assert_box_equal(&stack, 1, 1, 7, 1); +} + int main(void) { const struct CMUnitTest tests[] = { TEST(arrange_master_stack__zero), @@ -126,6 +211,14 @@ int main(void) { TEST(arrange_master_stack__right), TEST(arrange_master_stack__top), TEST(arrange_master_stack__bottom), + + TEST(arrange_master_stack__zero_master_with_gaps), + TEST(arrange_master_stack__zero_stack_with_gaps), + + TEST(arrange_master_stack__left_with_gaps), + TEST(arrange_master_stack__right_with_gaps), + TEST(arrange_master_stack__top_with_gaps), + TEST(arrange_master_stack__bottom_with_gaps), }; return RUN(tests); diff --git a/tst/tst-arrange_mid.c b/tst/tst-arrange_mid.c index 6f63204..b4b70a1 100644 --- a/tst/tst-arrange_mid.c +++ b/tst/tst-arrange_mid.c @@ -2,6 +2,7 @@ #include "asserts.h" #include +#include #include "layout.h" #include "tag.h" @@ -50,6 +51,26 @@ void arrange_wide__010(void **state) { assert_box_equal(&after, 0, 0, 0, 0); } +void arrange_wide__010_with_gaps(void **state) { + struct Box master, before, after; + + struct Demand demand = { .view_count = 1, .usable_width = 13, .usable_height = 5, }; + struct Tag tag = { .ratio_wide = 999, .smart_gaps = false, + .inner_gaps = 1, .outer_gaps = 1, }; + struct Tag smart_tag = { .ratio_wide = 999, .smart_gaps = true, + .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_wide(&demand, &tag, 0, 1, 0, &before, &master, &after); + assert_box_equal(&before, 0, 0, 0, 0); + assert_box_equal(&master, 1, 1, 11, 3); + assert_box_equal(&after, 0, 0, 0, 0); + + arrange_wide(&demand, &smart_tag, 0, 1, 0, &before, &master, &after); + assert_box_equal(&before, 0, 0, 0, 0); + assert_box_equal(&master, 0, 0, 13, 5); + assert_box_equal(&after, 0, 0, 0, 0); +} + void arrange_wide__001(void **state) { struct Box master, before, after; @@ -63,6 +84,26 @@ void arrange_wide__001(void **state) { assert_box_equal(&after, 0, 0, 13, 5); } +void arrange_wide__001_with_gaps(void **state) { + struct Box master, before, after; + + struct Demand demand = { .view_count = 1, .usable_width = 13, .usable_height = 5, }; + struct Tag tag = { .ratio_wide = 999, .smart_gaps = false, + .inner_gaps = 1, .outer_gaps = 1, }; + struct Tag smart_tag = { .ratio_wide = 999, .smart_gaps = true, + .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_wide(&demand, &tag, 0, 0, 1, &before, &master, &after); + assert_box_equal(&before, 0, 0, 0, 0); + assert_box_equal(&master, 0, 0, 0, 0); + assert_box_equal(&after, 1, 1, 11, 3); + + arrange_wide(&demand, &smart_tag, 0, 0, 1, &before, &master, &after); + assert_box_equal(&before, 0, 0, 0, 0); + assert_box_equal(&master, 0, 0, 0, 0); + assert_box_equal(&after, 0, 0, 13, 5); +} + void arrange_wide__100(void **state) { struct Box master, before, after; @@ -76,6 +117,26 @@ void arrange_wide__100(void **state) { assert_box_equal(&after, 0, 0, 0, 0); } +void arrange_wide__100_with_gaps(void **state) { + struct Box master, before, after; + + struct Demand demand = { .view_count = 1, .usable_width = 13, .usable_height = 5, }; + struct Tag tag = { .ratio_wide = 999, .smart_gaps = false, + .inner_gaps = 1, .outer_gaps = 1, }; + struct Tag smart_tag = { .ratio_wide = 999, .smart_gaps = true, + .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_wide(&demand, &tag, 1, 0, 0, &before, &master, &after); + assert_box_equal(&before, 1, 1, 11, 3); + assert_box_equal(&master, 0, 0, 0, 0); + assert_box_equal(&after, 0, 0, 0, 0); + + arrange_wide(&demand, &smart_tag, 1, 0, 0, &before, &master, &after); + assert_box_equal(&before, 0, 0, 13, 5); + assert_box_equal(&master, 0, 0, 0, 0); + assert_box_equal(&after, 0, 0, 0, 0); +} + void arrange_wide__101(void **state) { struct Box master, before, after; @@ -89,6 +150,18 @@ void arrange_wide__101(void **state) { assert_box_equal(&after, 7, 0, 6, 5); } +void arrange_wide__101_with_gaps(void **state) { + struct Box master, before, after; + + struct Demand demand = { .usable_width = 13, .usable_height = 5, }; + struct Tag tag = { .ratio_wide = 999, .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_wide(&demand, &tag, 1, 0, 1, &before, &master, &after); + assert_box_equal(&before, 1, 1, 5, 3); + assert_box_equal(&master, 0, 0, 0, 0); + assert_box_equal(&after, 7, 1, 5, 3); +} + void arrange_wide__011(void **state) { struct Box master, before, after; @@ -102,6 +175,18 @@ void arrange_wide__011(void **state) { assert_box_equal(&after, 8, 0, 5, 5); } +void arrange_wide__011_with_gaps(void **state) { + struct Box master, before, after; + + struct Demand demand = { .usable_width = 13, .usable_height = 5, }; + struct Tag tag = { .ratio_wide = 0.2, .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_wide(&demand, &tag, 0, 1, 1, &before, &master, &after); + assert_box_equal(&before, 0, 0, 0, 0); + assert_box_equal(&master, 1, 1, 6, 3); + assert_box_equal(&after, 8, 1, 4, 3); +} + void arrange_wide__110(void **state) { struct Box master, before, after; @@ -115,6 +200,18 @@ void arrange_wide__110(void **state) { assert_box_equal(&after, 0, 0, 0, 0); } +void arrange_wide__110_with_gaps(void **state) { + struct Box master, before, after; + + struct Demand demand = { .usable_width = 13, .usable_height = 5, }; + struct Tag tag = { .ratio_wide = 0.7, .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_wide(&demand, &tag, 1, 1, 0, &before, &master, &after); + assert_box_equal(&before, 1, 1, 1, 3); + assert_box_equal(&master, 3, 1, 9, 3); + assert_box_equal(&after, 0, 0, 0, 0); +} + void arrange_wide__111(void **state) { struct Box master, before, after; @@ -128,6 +225,18 @@ void arrange_wide__111(void **state) { assert_box_equal(&after, 10, 0, 3, 5); } +void arrange_wide__111_with_gaps(void **state) { + struct Box master, before, after; + + struct Demand demand = { .usable_width = 13, .usable_height = 5, }; + struct Tag tag = { .ratio_wide = 0.5, .inner_gaps = 1, .outer_gaps = 1, }; + + arrange_wide(&demand, &tag, 1, 1, 1, &before, &master, &after); + assert_box_equal(&before, 1, 1, 2, 3); + assert_box_equal(&master, 4, 1, 5, 3); + assert_box_equal(&after, 10, 1, 2, 3); +} + int main(void) { const struct CMUnitTest tests[] = { TEST(arrange_wide__000), @@ -142,6 +251,14 @@ int main(void) { TEST(arrange_wide__110), TEST(arrange_wide__111), + + TEST(arrange_wide__010_with_gaps), + TEST(arrange_wide__001_with_gaps), + TEST(arrange_wide__100_with_gaps), + TEST(arrange_wide__101_with_gaps), + TEST(arrange_wide__011_with_gaps), + TEST(arrange_wide__110_with_gaps), + TEST(arrange_wide__111_with_gaps), }; return RUN(tests); diff --git a/tst/tst-arrange_views.c b/tst/tst-arrange_views.c index 0d4ec13..4730140 100644 --- a/tst/tst-arrange_views.c +++ b/tst/tst-arrange_views.c @@ -31,7 +31,7 @@ void arrange_views__one(void **state) { struct Demand demand = { 0 }; struct Box usable = { 1, 2, 5, 11, }; - arrange_views(&demand, EVEN, N, N, 1, 1, usable, usable, &stack); + arrange_views(&demand, EVEN, N, N, 1, 1, 0, usable, usable, &stack); assert_int_equal(slist_length(stack), 1); @@ -45,7 +45,7 @@ void arrange_views__n(void **state) { struct Demand demand = { 0 }; struct Box usable = { 1, 2, 5, 11, }; - arrange_views(&demand, EVEN, N, N, 2, 2, usable, usable, &stack); + arrange_views(&demand, EVEN, N, N, 2, 2, 0, usable, usable, &stack); assert_int_equal(slist_length(stack), 2); @@ -55,12 +55,30 @@ void arrange_views__n(void **state) { slist_free_vals(&stack, NULL); } +void arrange_views__n_with_gaps(void **state) { + struct SList *stack = NULL; + struct Demand demand = { 0 }; + struct Box usable = { 1, 2, 5, 11, }; + + arrange_views(&demand, EVEN, N, N, 2, 2, /*inner_gap=*/1, usable, usable, &stack); + assert_int_equal(slist_length(stack), 2); + assert_box_equal(slist_at(stack, 0), 1, 8, 5, 5); + assert_box_equal(slist_at(stack, 1), 1, 2, 5, 5); + slist_free_vals(&stack, NULL); + + arrange_views(&demand, EVEN, N, N, 2, 2, /*inner_gap=*/2, usable, usable, &stack); + assert_int_equal(slist_length(stack), 2); + assert_box_equal(slist_at(stack, 0), 1, 8, 5, 5); + assert_box_equal(slist_at(stack, 1), 1, 2, 5, 4); + slist_free_vals(&stack, NULL); +} + void arrange_views__s(void **state) { struct SList *stack = NULL; struct Demand demand = { 0 }; struct Box usable = { 1, 2, 5, 11, }; - arrange_views(&demand, EVEN, S, S, 2, 2, usable, usable, &stack); + arrange_views(&demand, EVEN, S, S, 2, 2, 0, usable, usable, &stack); assert_int_equal(slist_length(stack), 2); @@ -70,12 +88,30 @@ void arrange_views__s(void **state) { slist_free_vals(&stack, NULL); } +void arrange_views__s_with_gaps(void **state) { + struct SList *stack = NULL; + struct Demand demand = { 0 }; + struct Box usable = { 1, 2, 5, 11, }; + + arrange_views(&demand, EVEN, S, S, 2, 2, /*inner_gap=*/1, usable, usable, &stack); + assert_int_equal(slist_length(stack), 2); + assert_box_equal(slist_at(stack, 0), 1, 2, 5, 5); + assert_box_equal(slist_at(stack, 1), 1, 8, 5, 5); + slist_free_vals(&stack, NULL); + + arrange_views(&demand, EVEN, S, S, 2, 2, /*inner_gap=*/2, usable, usable, &stack); + assert_int_equal(slist_length(stack), 2); + assert_box_equal(slist_at(stack, 0), 1, 2, 5, 5); + assert_box_equal(slist_at(stack, 1), 1, 9, 5, 4); + slist_free_vals(&stack, NULL); +} + void arrange_views__e(void **state) { struct SList *stack = NULL; struct Demand demand = { 0 }; struct Box usable = { 1, 2, 5, 11, }; - arrange_views(&demand, EVEN, E, E, 2, 2, usable, usable, &stack); + arrange_views(&demand, EVEN, E, E, 2, 2, 0, usable, usable, &stack); assert_int_equal(slist_length(stack), 2); @@ -85,12 +121,30 @@ void arrange_views__e(void **state) { slist_free_vals(&stack, NULL); } +void arrange_views__e_with_gaps(void **state) { + struct SList *stack = NULL; + struct Demand demand = { 0 }; + struct Box usable = { 1, 2, 5, 11, }; + + arrange_views(&demand, EVEN, E, E, 2, 2, /*inner_gap=*/1, usable, usable, &stack); + assert_int_equal(slist_length(stack), 2); + assert_box_equal(slist_at(stack, 0), 1, 2, 2, 11); + assert_box_equal(slist_at(stack, 1), 4, 2, 2, 11); + slist_free_vals(&stack, NULL); + + arrange_views(&demand, EVEN, E, E, 2, 2, /*inner_gap=*/2, usable, usable, &stack); + assert_int_equal(slist_length(stack), 2); + assert_box_equal(slist_at(stack, 0), 1, 2, 2, 11); + assert_box_equal(slist_at(stack, 1), 5, 2, 1, 11); + slist_free_vals(&stack, NULL); +} + void arrange_views__w(void **state) { struct SList *stack = NULL; struct Demand demand = { 0 }; struct Box usable = { 1, 2, 5, 11, }; - arrange_views(&demand, EVEN, W, W, 2, 2, usable, usable, &stack); + arrange_views(&demand, EVEN, W, W, 2, 2, 0, usable, usable, &stack); assert_int_equal(slist_length(stack), 2); @@ -100,12 +154,30 @@ void arrange_views__w(void **state) { slist_free_vals(&stack, NULL); } +void arrange_views__w_with_gaps(void **state) { + struct SList *stack = NULL; + struct Demand demand = { 0 }; + struct Box usable = { 1, 2, 5, 11, }; + + arrange_views(&demand, EVEN, W, W, 2, 2, /*inner_gap=*/1, usable, usable, &stack); + assert_int_equal(slist_length(stack), 2); + assert_box_equal(slist_at(stack, 0), 4, 2, 2, 11); + assert_box_equal(slist_at(stack, 1), 1, 2, 2, 11); + slist_free_vals(&stack, NULL); + + arrange_views(&demand, EVEN, W, W, 2, 2, /*inner_gap=*/2, usable, usable, &stack); + assert_int_equal(slist_length(stack), 2); + assert_box_equal(slist_at(stack, 0), 4, 2, 2, 11); + assert_box_equal(slist_at(stack, 1), 1, 2, 1, 11); + slist_free_vals(&stack, NULL); +} + void arrange_views__even(void **state) { struct SList *stack = NULL; struct Demand demand = { 0 }; struct Box usable = { 1, 2, 5, 11, }; - arrange_views(&demand, EVEN, S, S, 3, 3, usable, usable, &stack); + arrange_views(&demand, EVEN, S, S, 3, 3, 0, usable, usable, &stack); assert_int_equal(slist_length(stack), 3); @@ -116,12 +188,32 @@ void arrange_views__even(void **state) { slist_free_vals(&stack, NULL); } +void arrange_views__even_with_gaps(void **state) { + struct SList *stack = NULL; + struct Demand demand = { 0 }; + struct Box usable = { 1, 2, 5, 11, }; + + arrange_views(&demand, EVEN, S, S, 3, 3, /*inner_gap=*/1, usable, usable, &stack); + assert_int_equal(slist_length(stack), 3); + assert_box_equal(slist_at(stack, 0), 1, 2, 5, 3); + assert_box_equal(slist_at(stack, 1), 1, 6, 5, 3); + assert_box_equal(slist_at(stack, 2), 1, 10, 5, 3); + slist_free_vals(&stack, NULL); + + arrange_views(&demand, EVEN, S, S, 3, 3, /*inner_gap=*/2, usable, usable, &stack); + assert_int_equal(slist_length(stack), 3); + assert_box_equal(slist_at(stack, 0), 1, 2, 5, 2); + assert_box_equal(slist_at(stack, 1), 1, 6, 5, 3); + assert_box_equal(slist_at(stack, 2), 1, 11, 5, 2); + slist_free_vals(&stack, NULL); +} + void arrange_views__diminish(void **state) { struct SList *stack = NULL; struct Demand demand = { 0 }; struct Box usable = { 0, 0, 97, 1, }; - arrange_views(&demand, DIMINISH, E, E, 7, 7, usable, usable, &stack); + arrange_views(&demand, DIMINISH, E, E, 7, 7, 0, usable, usable, &stack); assert_int_equal(slist_length(stack), 7); @@ -136,12 +228,40 @@ void arrange_views__diminish(void **state) { slist_free_vals(&stack, NULL); } +void arrange_views__diminish_with_gaps(void **state) { + struct SList *stack = NULL; + struct Demand demand = { 0 }; + struct Box usable = { 0, 0, 97, 1, }; + + arrange_views(&demand, DIMINISH, E, E, 7, 7, /*inner_gap=*/1, usable, usable, &stack); + assert_int_equal(slist_length(stack), 7); + assert_box_equal(slist_at(stack, 0), 0, 0, 23, 1); // 91 * 7/28 up + assert_box_equal(slist_at(stack, 1), 24, 0, 20, 1); // 91 * 6/28 up + assert_box_equal(slist_at(stack, 2), 45, 0, 16, 1); // 91 * 5/28 + assert_box_equal(slist_at(stack, 3), 62, 0, 13, 1); // 91 * 4/28 + assert_box_equal(slist_at(stack, 4), 76, 0, 10, 1); // 91 * 3/28 up + assert_box_equal(slist_at(stack, 5), 87, 0, 7, 1); // 91 * 2/28 up + assert_box_equal(slist_at(stack, 6), 95, 0, 2, 1); // 91 * 1/28 + slist_free_vals(&stack, NULL); + + arrange_views(&demand, DIMINISH, E, E, 7, 7, /*inner_gap=*/2, usable, usable, &stack); + assert_int_equal(slist_length(stack), 7); + assert_box_equal(slist_at(stack, 0), 0, 0, 21, 1); // 85 * 7/28 + assert_box_equal(slist_at(stack, 1), 23, 0, 18, 1); // 85 * 6/28 + assert_box_equal(slist_at(stack, 2), 43, 0, 15, 1); // 85 * 5/28 + assert_box_equal(slist_at(stack, 3), 60, 0, 12, 1); // 85 * 4/28 + assert_box_equal(slist_at(stack, 4), 74, 0, 9, 1); // 85 * 3/28 + assert_box_equal(slist_at(stack, 5), 85, 0, 6, 1); // 85 * 2/28 + assert_box_equal(slist_at(stack, 6), 93, 0, 4, 1); // 85 * 1/28 + slist_free_vals(&stack, NULL); +} + void arrange_views__dwindle(void **state) { struct SList *stack = NULL; struct Demand demand = { 0 }; struct Box usable = { 100, 100, 37, 71, }; - arrange_views(&demand, DWINDLE, S, E, 8, 8, usable, usable, &stack); + arrange_views(&demand, DWINDLE, S, E, 8, 8, 0, usable, usable, &stack); assert_int_equal(slist_length(stack), 8); @@ -157,6 +277,24 @@ void arrange_views__dwindle(void **state) { slist_free_vals(&stack, NULL); } +void arrange_views__dwindle_with_gaps(void **state) { + struct SList *stack = NULL; + struct Demand demand = { 0 }; + struct Box usable = { 100, 100, 37, 71, }; + + arrange_views(&demand, DWINDLE, S, E, 8, 8, /*inner_gap=*/1, usable, usable, &stack); + assert_int_equal(slist_length(stack), 8); + assert_box_equal(slist_at(stack, 0), 100, 100, 37, 35); + assert_box_equal(slist_at(stack, 1), 100, 136, 18, 35); + assert_box_equal(slist_at(stack, 2), 119, 136, 18, 17); + assert_box_equal(slist_at(stack, 3), 119, 154, 9, 17); + assert_box_equal(slist_at(stack, 4), 129, 154, 8, 8); + assert_box_equal(slist_at(stack, 5), 129, 163, 4, 8); + assert_box_equal(slist_at(stack, 6), 134, 163, 3, 4); + assert_box_equal(slist_at(stack, 7), 134, 168, 3, 3); + slist_free_vals(&stack, NULL); +} + int main(void) { const struct CMUnitTest tests[] = { TEST(arrange_views__one), @@ -165,12 +303,17 @@ int main(void) { TEST(arrange_views__s), TEST(arrange_views__e), TEST(arrange_views__w), - TEST(arrange_views__even), - TEST(arrange_views__diminish), - TEST(arrange_views__dwindle), + + TEST(arrange_views__n_with_gaps), + TEST(arrange_views__s_with_gaps), + TEST(arrange_views__e_with_gaps), + TEST(arrange_views__w_with_gaps), + TEST(arrange_views__even_with_gaps), + TEST(arrange_views__diminish_with_gaps), + TEST(arrange_views__dwindle_with_gaps), }; return RUN(tests);