diff --git a/home/pc/common/home.nix b/home/pc/common/home.nix index 69fae73..3592249 100644 --- a/home/pc/common/home.nix +++ b/home/pc/common/home.nix @@ -17,7 +17,8 @@ home.homeDirectory = "/home/autumn"; home.stateVersion = "25.05"; - programs.kitty.enable = true; + programs.ghostty.enable = true; + programs.ghostty.settings.font-size = 24; stylix = { targets.waybar.enable = false; diff --git a/home/pc/common/programs/carapace.nix b/home/pc/common/programs/carapace.nix index 4c20955..b74d54b 100644 --- a/home/pc/common/programs/carapace.nix +++ b/home/pc/common/programs/carapace.nix @@ -1,7 +1,7 @@ -{...}: +{ ... }: { - programs.carapace = { - enable = true; - enableNushellIntegration = true; - }; + programs.carapace = { + enable = true; + enableFishIntegration = true; + }; } diff --git a/home/pc/common/programs/default.nix b/home/pc/common/programs/default.nix index 7c8f6cf..06232a9 100644 --- a/home/pc/common/programs/default.nix +++ b/home/pc/common/programs/default.nix @@ -6,12 +6,13 @@ ./btop.nix ./carapace.nix ./fastfetch.nix + ./fish.nix ./git.nix ./hyfetch.nix ./mako.nix ./ncspot.nix ./nix-search-tv.nix - ./nushell.nix + #./nushell.nix ./starship.nix ./tmux.nix ./vesktop.nix diff --git a/home/pc/common/programs/fish.nix b/home/pc/common/programs/fish.nix new file mode 100644 index 0000000..f8e3059 --- /dev/null +++ b/home/pc/common/programs/fish.nix @@ -0,0 +1,51 @@ +{ pkgs, ... }: +{ + home.packages = [ + pkgs.nix-your-shell + pkgs.lolcat + pkgs.kittysay + pkgs.eza + ]; + + programs.fish = { + enable = true; + shellInit = '' + function fish_prompt + string join ' ' -- (set_color magenta) $(prompt_pwd) :3 (set_color normal) + end + + function fish_greeting + echo hii autumn :3\nthe time is $(date +%T) | kittysay | lolcat + end + + function ls --description "List directory contents" + eza --icons=always $argv + end + + function nix-shell --description "Start an interactive shell based on a Nix expression" + nix-your-shell fish nix-shell -- $argv + end + + function nix --description "Reproducible and declarative configuration management" + nix-your-shell fish nix -- $argv + end + ''; + + shellInitLast = '' + set fish_color_normal green + set fish_color_command magenta --bold + set fish_color_quote yellow + set fish_color_redirection white + set fish_color_end magenta + set fish_color_error red --bold + set fish_color_param blue + set fish_color_valid_path blue + set fish_color_option blue + set fish_color_comment brblack --italics + set fish_color_operator yellow + set fish_color_escape blue --italics + set fish_color_autosuggestion brblack + set fish_color_cancel brblack + ''; + }; +} diff --git a/home/pc/common/programs/foot.nix b/home/pc/common/programs/foot.nix deleted file mode 100644 index 4ca5c39..0000000 --- a/home/pc/common/programs/foot.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ config, lib, ... }: -{ - programs.foot = { - enable = true; - settings = { - main.font = lib.mkForce "${config.stylix.fonts.monospace.name}:size=19"; - main.font-size-adjustment = 1; - cursor.style = "beam"; - key-bindings.spawn-terminal = "none"; - }; - }; -} diff --git a/home/pc/common/programs/starship.nix b/home/pc/common/programs/starship.nix index bd3cac8..f134014 100644 --- a/home/pc/common/programs/starship.nix +++ b/home/pc/common/programs/starship.nix @@ -1,5 +1,6 @@ -{...}: { +{ ... }: +{ programs.starship = { - enable = true; + #enable = true; }; } diff --git a/home/pc/common/programs/waybar.nix b/home/pc/common/programs/waybar.nix index 1b90957..042dd80 100644 --- a/home/pc/common/programs/waybar.nix +++ b/home/pc/common/programs/waybar.nix @@ -4,7 +4,7 @@ { "position": "top", "spacing": 4, - "modules-left": ["custom/icon", "ext/workspaces", "tray"], + "modules-left": ["custom/icon", "dwl/tags", "tray"], "modules-center": ["clock"], "modules-right": ["pulseaudio", "cpu", "memory", "temperature", "battery"], "custom/icon": { @@ -15,7 +15,8 @@ "spacing": 10 }, "clock": { - "format": "{:%Y/%m/%d - %A - %R}" + "format": "{:%Y/%m/%d - %A - %R}", + "tooltip": false }, "cpu": { "format": " {usage}%", @@ -64,11 +65,16 @@ * { font-family: "${config.stylix.fonts.serif.name}"; font-size: 16px; - min-height: 10px; + min-height: 16px; + } + + window#waybar { + margin-top: 6px; + padding-left: 10px; + padding-right: 10px; } #custom-icon, - #workspaces, #tray, #clock, #pulseaudio, @@ -76,47 +82,24 @@ #memory, #temperature, #battery { - border-width: 2px; - border-style: solid; - border-color: #${config.stylix.base16Scheme.palette.base0D}; - border-radius: 10px; margin-top: 6px; margin-left: 8px; padding-left: 10px; margin-bottom: 0px; padding-right: 10px; - border-radius: 10px; - transition: none; - color: #${config.stylix.base16Scheme.palette.base05}; + color: #${config.stylix.base16Scheme.palette.base05}; background: #${config.stylix.base16Scheme.palette.base00}; + border: 2px solid #${config.stylix.base16Scheme.palette.base0D}; } #custom-icon { margin-left: 10px; } + #memory { margin-right: 10px; } - - window#waybar { - background: transparent; - } - - window#waybar.hidden { - opacity: 0.2; - } - - #window { - margin-top: 6px; - padding-left: 10px; - padding-right: 10px; - border-radius: 10px; - transition: none; - color: transparent; - background: transparent; - } - #battery.critical:not(.charging) { animation-name: blink; animation-duration: 0.5s; @@ -131,8 +114,23 @@ } } - #workspaces button.active { + #tags { + margin-top: 6px; + margin-left: 8px; + padding-left: 10px; + margin-bottom: 0px; + padding-right: 10px; + border: 2px solid #${config.stylix.base16Scheme.palette.base0D}; + } + + #tags button.occupied { + color: #${config.stylix.base16Scheme.palette.base0B}; + background: #${config.stylix.base16Scheme.palette.base00}; + } + + #tags button.focused { color: #${config.stylix.base16Scheme.palette.base0D}; + background: #${config.stylix.base16Scheme.palette.base00}; } ''; }; diff --git a/home/pc/common/programs/zoxide.nix b/home/pc/common/programs/zoxide.nix index 2ab0513..27ce770 100644 --- a/home/pc/common/programs/zoxide.nix +++ b/home/pc/common/programs/zoxide.nix @@ -2,6 +2,6 @@ { programs.zoxide = { enable = true; - enableNushellIntegration = true; + enableFishIntegration = true; }; } diff --git a/home/pc/common/stylix.nix b/home/pc/common/stylix.nix deleted file mode 100644 index 5919b74..0000000 --- a/home/pc/common/stylix.nix +++ /dev/null @@ -1,82 +0,0 @@ -{ - inputs, - pkgs, - ... -}: -{ - stylix = - let - palette = { - base00 = "11111a"; # #11111a - base01 = "181825"; # #181825 - base02 = "313244"; # #313244 - base03 = "45475a"; # #45475a - base04 = "585b70"; # #585b70 - base05 = "cdd6f4"; # #cdd6f4 - base06 = "f5e0dc"; # #f5e0dc - base07 = "b4befe"; # #b4befe - base08 = "f38ba8"; # #f38ba8 - base09 = "fab387"; # #fab387 - base0A = "f9e2af"; # #f9e2af - base0B = "a6e3a1"; # #a6e3a1 - base0C = "94e2d5"; # #94e2d5 - base0D = "f5c2e7"; # #f5c2e7 - base0E = "cba6f7"; # #cba6f7 - base0F = "f2cdcd"; # #f2cdcd - - #base00 = "1d2021"; # #1d2021 - #base01 = "3c3836"; # #3c3836 - #base02 = "504945"; # #504945 - #base03 = "665c54"; # #665c54 - #base04 = "bdae93"; # #bdae93 - #base05 = "d5c4a1"; # #d5c4a1 - #base06 = "ebdbb2"; # #ebdbb2 - #base07 = "fbf1c7"; # #fbf1c7 - #base08 = "fb4934"; # #fb4934 - #base09 = "fe8019"; # #fe8019 - #base0A = "fabd2f"; # #fabd2f - #base0B = "b8bb26"; # #b8bb26 - #base0C = "8ec07c"; # #8ec07c - #base0D = "83a598"; # #83a598 - #base0E = "d3869b"; # #d3869b - #base0F = "d65d0e"; # #d65d0e - }; - wallpaper = inputs.nix-wallpaper.packages.${pkgs.stdenv.hostPlatform.system}.default.override { - width = 2560; - height = 1440; - backgroundColor = "#${palette.base00}"; - logoColors = { - color0 = "#${palette.base0D}"; - color1 = "#${palette.base0D}"; - color2 = "#${palette.base0D}"; - color3 = "#${palette.base0D}"; - color4 = "#${palette.base0D}"; - color5 = "#${palette.base0D}"; - }; - }; - in - - { - enable = true; - image = "${wallpaper}/share/wallpapers/nixos-wallpaper.png"; - polarity = "dark"; - targets.waybar.enable = false; - targets.zathura.enable = false; - targets.vesktop.enable = false; - targets.zen-browser.enable = false; - fonts = - let - font = { - package = pkgs.nerd-fonts.caskaydia-cove; - name = "CaskaydiaCove Nerd Font"; - }; - in - { - monospace = font; - serif = font; - sansSerif = font; - emoji = font; - }; - base16Scheme.palette = palette; - }; -} diff --git a/host/pc/common/configuration.nix b/host/pc/common/configuration.nix index ffa77e4..87d5a51 100644 --- a/host/pc/common/configuration.nix +++ b/host/pc/common/configuration.nix @@ -10,8 +10,10 @@ imports = [ inputs.home-manager.nixosModules.home-manager inputs.stylix.nixosModules.stylix + inputs.base16.nixosModule ./stylix.nix + ./dwl/dwl.nix ]; boot.loader.efi.canTouchEfiVariables = true; @@ -49,11 +51,15 @@ enable = true; }; - services.displayManager.sddm = { + # services.displayManager.sddm = { + # enable = true; + # theme = "catppuccin-mocha-mauve"; + # wayland.enable = true; + # package = pkgs.kdePackages.sddm; + # }; + + services.displayManager.ly = { enable = true; - theme = "catppuccin-mocha-mauve"; - wayland.enable = true; - package = pkgs.kdePackages.sddm; }; programs.steam = { @@ -84,6 +90,8 @@ hardware.bluetooth.enable = true; hardware.bluetooth.powerOnBoot = true; + programs.fish.enable = true; + users.users.${userName} = { isNormalUser = true; extraGroups = [ @@ -91,7 +99,7 @@ "wheel" ]; packages = [ ]; - shell = pkgs.nushell; + shell = pkgs.fish; }; nix.settings.experimental-features = [ diff --git a/host/pc/common/dwl/config.h b/host/pc/common/dwl/config.h new file mode 100644 index 0000000..634135a --- /dev/null +++ b/host/pc/common/dwl/config.h @@ -0,0 +1,244 @@ +#define BASE_00 "@base00@" +#define BASE_01 "@base01@" +#define BASE_02 "@base02@" +#define BASE_03 "@base03@" +#define BASE_04 "@base04@" +#define BASE_05 "@base05@" +#define BASE_06 "@base06@" +#define BASE_07 "@base07@" +#define BASE_08 "@base08@" +#define BASE_09 "@base09@" +#define BASE_0A "@base0A@" +#define BASE_0B "@base0B@" +#define BASE_0C "@base0C@" +#define BASE_0D "@base0D@" +#define BASE_0E "@base0E@" +#define BASE_0F "@base0F@" + +#define WALLPAPER "@wallpaper@" + +/* Taken from https://github.com/jt0w/.dotfiles/blob/a6db8ad08ce834bb8ba9d15b999281d6635f5169/modules/nixos/dwl/config.def.h */ +#define HEX(c) \ + ((c) >= '0' && (c) <= '9' ? (c) - '0' \ + : (c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 \ + : (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 \ + : 0) + +#define HEXBYTE(s, i) ((HEX((s)[i]) << 4) | HEX((s)[(i) + 1])) + +#define RGB_HEX_TO_RGBA(s) \ + ((uint32_t)((HEXBYTE(s, 0) << 24) | (HEXBYTE(s, 2) << 16) | \ + (HEXBYTE(s, 4) << 8) | 0xFF)) + +/* Taken from https://github.com/djpohly/dwl/issues/466 */ +#define COLOR(hex) \ + {((hex >> 24) & 0xFF) / 255.0f, ((hex >> 16) & 0xFF) / 255.0f, \ + ((hex >> 8) & 0xFF) / 255.0f, (hex & 0xFF) / 255.0f} + +/* appearance */ +static const int sloppyfocus = 1; /* focus follows mouse */ +static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ +static const unsigned int borderpx = 2; /* border pixel of windows */ + +static const float rootcolor[] = COLOR(RGB_HEX_TO_RGBA(BASE_00)); +static const float bordercolor[] = COLOR(RGB_HEX_TO_RGBA(BASE_02)); +static const float focuscolor[] = COLOR(RGB_HEX_TO_RGBA(BASE_0D)); +static const float urgentcolor[] = COLOR(RGB_HEX_TO_RGBA(BASE_08)); + +// gaps patch +static const int smartgaps = 0; /* 1 means no outer gap when there is only one window */ +static int gaps = 1; /* 1 means gaps between windows are added */ +static const unsigned int gappx = 10; /* gap pixel between windows */ + +/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ +static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */ + +enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; + +static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */ +static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */ + +/* tagging - TAGCOUNT must be no greater than 31 */ +#define TAGCOUNT (9) + +/* logging */ +static int log_level = WLR_ERROR; + +/* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ +static const Rule rules[] = { + /* app_id title tags mask isfloating monitor */ + /* examples: */ + { "mpv", NULL, 0, 1, 0 }, +}; + +/* layout(s) */ +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* monitors */ +/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator + * WARNING: negative values other than (-1, -1) cause problems with Xwayland clients + * https://gitlab.freedesktop.org/xorg/xserver/-/issues/899 +*/ +/* NOTE: ALWAYS add a fallback rule, even if you are completely sure it won't be used */ +static const MonitorRule monrules[] = { + /* name mfact nmaster scale layout rotate/reflect x y */ + /* example of a HiDPI laptop monitor: + { "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, + */ + /* defaults */ + + // https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/monitorconfig/monitorconfig.patch + { "DP-2", 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1, 2560, 1440, 180.0f, 0, 1}, + { "HDMI-A-2", 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1, 1920, 1080, 60.0f, 0, 1}, +}; + +// https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/autostart/autostart.patch +static const char *const autostart[] = { + "wlr-randr", "--output", "DP-2", "--mode", "2560x1440@180Hz", NULL, + "swww-daemon", NULL, + "swww", "img", WALLPAPER, "-t", "none", NULL, + "waybar", NULL, + NULL, +}; + +/* keyboard */ +static const struct xkb_rule_names xkb_rules = { + /* can specify fields: rules, model, layout, variant, options */ + /* example: + .options = "ctrl:nocaps", + */ + .layout = "us", +}; + +static const int repeat_rate = 25; +static const int repeat_delay = 600; + +/* Trackpad */ +static const int tap_to_click = 1; +static const int tap_and_drag = 1; +static const int drag_lock = 1; +static const int natural_scrolling = 0; +static const int disable_while_typing = 1; +static const int left_handed = 0; +static const int middle_button_emulation = 0; +/* You can choose between: +LIBINPUT_CONFIG_SCROLL_NO_SCROLL +LIBINPUT_CONFIG_SCROLL_2FG +LIBINPUT_CONFIG_SCROLL_EDGE +LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN +*/ +static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; + +/* You can choose between: +LIBINPUT_CONFIG_CLICK_METHOD_NONE +LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS +LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER +*/ +static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; + +/* You can choose between: +LIBINPUT_CONFIG_SEND_EVENTS_ENABLED +LIBINPUT_CONFIG_SEND_EVENTS_DISABLED +LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE +*/ +static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; + +/* You can choose between: +LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT +LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE +*/ +static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; +static const double accel_speed = 1.0; + +/* You can choose between: +LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle +LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right +*/ +static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; + +/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */ +#define MODKEY WLR_MODIFIER_LOGO + +#define TAGKEYS(KEY,SKEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} } + +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +static const char *termcmd[] = { "ghostty", NULL }; +static const char *menucmd[] = { "rofi", "-show", "drun", NULL }; +static const char *powrcmd[] = { "rofi", "-show", "pm", "-modi", "pm:rofi-power-menu", "--choices", "suspend/reboot/shutdown", NULL }; +static const char *scshcmd[] = { "gscreenshot", "-c", "-s", NULL }; +static const char *brsrcmd[] = { "zen-beta", NULL }; + +static const Key keys[] = { + /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ + /* modifier key function argument */ + { MODKEY, XKB_KEY_d, spawn, {.v = menucmd} }, + { MODKEY, XKB_KEY_q, spawn, {.v = termcmd} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_P, spawn, {.v = powrcmd} }, + { MODKEY, XKB_KEY_s, spawn, {.v = scshcmd} }, + { MODKEY, XKB_KEY_w, spawn, {.v = brsrcmd} }, + + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapclients, {.i = DIR_UP} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapclients, {.i = DIR_DOWN} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapclients, {.i = DIR_RIGHT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapclients, {.i = DIR_LEFT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_k, swapclients, {.i = DIR_UP} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_j, swapclients, {.i = DIR_DOWN} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_h, swapclients, {.i = DIR_RIGHT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_l, swapclients, {.i = DIR_LEFT} }, + + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, focusmon, {.i = WLR_DIRECTION_LEFT} }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, focusmon, {.i = WLR_DIRECTION_RIGHT} }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_h, focusmon, {.i = WLR_DIRECTION_LEFT} }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_l, focusmon, {.i = WLR_DIRECTION_RIGHT} }, + + { MODKEY, XKB_KEY_Left, focusdir, {.ui = 0} }, + { MODKEY, XKB_KEY_Right, focusdir, {.ui = 1} }, + { MODKEY, XKB_KEY_Up, focusdir, {.ui = 2} }, + { MODKEY, XKB_KEY_Down, focusdir, {.ui = 3} }, + { MODKEY, XKB_KEY_h, focusdir, {.ui = 0} }, + { MODKEY, XKB_KEY_l, focusdir, {.ui = 1} }, + { MODKEY, XKB_KEY_k, focusdir, {.ui = 2} }, + { MODKEY, XKB_KEY_j, focusdir, {.ui = 3} }, + + { MODKEY, XKB_KEY_Return, zoom, {0} }, + { MODKEY, XKB_KEY_c, killclient, {0} }, + { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XKB_KEY_v, togglefloating, {0} }, + { MODKEY, XKB_KEY_f, togglefullscreen, {0} }, + TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), + TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), + TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), + TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3), + TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4), + TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5), + TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6), + TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7), + TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_M, quit, {0} }, + + /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ + { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} }, + /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is + * do not remove them. + */ +#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} } + CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6), + CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12), +}; + +static const Button buttons[] = { + { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} }, + { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, +}; diff --git a/host/pc/common/dwl/dwl.nix b/host/pc/common/dwl/dwl.nix new file mode 100644 index 0000000..41cf55e --- /dev/null +++ b/host/pc/common/dwl/dwl.nix @@ -0,0 +1,47 @@ +{ config, pkgs, ... }: +{ + environment.systemPackages = [ + pkgs.gscreenshot + pkgs.wlr-randr + ]; + programs.dwl = { + enable = true; + package = + (pkgs.dwl.override { + configH = pkgs.replaceVars ./config.h { + base00 = config.lib.stylix.colors.base00; + base01 = config.lib.stylix.colors.base01; + base02 = config.lib.stylix.colors.base02; + base03 = config.lib.stylix.colors.base03; + base04 = config.lib.stylix.colors.base04; + base05 = config.lib.stylix.colors.base05; + base06 = config.lib.stylix.colors.base06; + base07 = config.lib.stylix.colors.base07; + base08 = config.lib.stylix.colors.base08; + base09 = config.lib.stylix.colors.base09; + base0A = config.lib.stylix.colors.base0A; + base0B = config.lib.stylix.colors.base0B; + base0C = config.lib.stylix.colors.base0C; + base0D = config.lib.stylix.colors.base0D; + base0E = config.lib.stylix.colors.base0E; + base0F = config.lib.stylix.colors.base0F; + + wallpaper = config.stylix.image; + }; + }).overrideAttrs + (oldAttrs: { + buildInputs = oldAttrs.buildInputs or [ ] ++ [ + pkgs.libdrm + pkgs.fcft + ]; + patches = oldAttrs.patches or [ ] ++ [ + ./patches/autostart-0.7.patch + ./patches/btrtile-v0.7-gaps.patch + ./patches/focusdir.patch + ./patches/gaps.patch + ./patches/ipc.patch + ./patches/monitorconfig.patch + ]; + }); + }; +} diff --git a/host/pc/common/dwl/patches/autostart-0.7.patch b/host/pc/common/dwl/patches/autostart-0.7.patch new file mode 100644 index 0000000..12e6d7e --- /dev/null +++ b/host/pc/common/dwl/patches/autostart-0.7.patch @@ -0,0 +1,154 @@ +From 787f7252d63945996f009828aff3c44afd0f7781 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= + +Date: Sat, 8 Jul 2023 17:11:36 -0600 +Subject: [PATCH] port autostart patch from dwm +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +https://dwm.suckless.org/patches/cool_autostart/ +Signed-off-by: Leonardo Hernández Hernández +--- + config.def.h | 7 +++++++ + dwl.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++----- + 2 files changed, 61 insertions(+), 5 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 22d2171..8dc6502 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -20,6 +20,13 @@ static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You ca + /* logging */ + static int log_level = WLR_ERROR; + ++/* Autostart */ ++static const char *const autostart[] = { ++ "wbg", "/path/to/your/image", NULL, ++ NULL /* terminate */ ++}; ++ ++ + /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ + static const Rule rules[] = { + /* app_id title tags mask isfloating monitor */ +diff --git a/dwl.c b/dwl.c +index 5bf995e..e8b8727 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -249,6 +249,7 @@ static void arrange(Monitor *m); + static void arrangelayer(Monitor *m, struct wl_list *list, + struct wlr_box *usable_area, int exclusive); + static void arrangelayers(Monitor *m); ++static void autostartexec(void); + static void axisnotify(struct wl_listener *listener, void *data); + static void buttonpress(struct wl_listener *listener, void *data); + static void chvt(const Arg *arg); +@@ -432,6 +433,9 @@ static xcb_atom_t netatom[NetLast]; + /* attempt to encapsulate suck into one file */ + #include "client.h" + ++static pid_t *autostart_pids; ++static size_t autostart_len; ++ + /* function implementations */ + void + applybounds(Client *c, struct wlr_box *bbox) +@@ -580,6 +584,27 @@ arrangelayers(Monitor *m) + } + } + ++void ++autostartexec(void) { ++ const char *const *p; ++ size_t i = 0; ++ ++ /* count entries */ ++ for (p = autostart; *p; autostart_len++, p++) ++ while (*++p); ++ ++ autostart_pids = calloc(autostart_len, sizeof(pid_t)); ++ for (p = autostart; *p; i++, p++) { ++ if ((autostart_pids[i] = fork()) == 0) { ++ setsid(); ++ execvp(*p, (char *const *)p); ++ die("dwl: execvp %s:", *p); ++ } ++ /* skip arguments */ ++ while (*++p); ++ } ++} ++ + void + axisnotify(struct wl_listener *listener, void *data) + { +@@ -676,11 +701,21 @@ checkidleinhibitor(struct wlr_surface *exclude) + void + cleanup(void) + { ++ size_t i; + #ifdef XWAYLAND + wlr_xwayland_destroy(xwayland); + xwayland = NULL; + #endif + wl_display_destroy_clients(dpy); ++ ++ /* kill child processes */ ++ for (i = 0; i < autostart_len; i++) { ++ if (0 < autostart_pids[i]) { ++ kill(autostart_pids[i], SIGTERM); ++ waitpid(autostart_pids[i], NULL, 0); ++ } ++ } ++ + if (child_pid > 0) { + kill(-child_pid, SIGTERM); + waitpid(child_pid, NULL, 0); +@@ -1497,18 +1532,31 @@ void + handlesig(int signo) + { + if (signo == SIGCHLD) { +-#ifdef XWAYLAND + siginfo_t in; + /* wlroots expects to reap the XWayland process itself, so we + * use WNOWAIT to keep the child waitable until we know it's not + * XWayland. + */ + while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid +- && (!xwayland || in.si_pid != xwayland->server->pid)) +- waitpid(in.si_pid, NULL, 0); +-#else +- while (waitpid(-1, NULL, WNOHANG) > 0); ++#ifdef XWAYLAND ++ && (!xwayland || in.si_pid != xwayland->server->pid) + #endif ++ ) { ++ pid_t *p, *lim; ++ waitpid(in.si_pid, NULL, 0); ++ if (in.si_pid == child_pid) ++ child_pid = -1; ++ if (!(p = autostart_pids)) ++ continue; ++ lim = &p[autostart_len]; ++ ++ for (; p < lim; p++) { ++ if (*p == in.si_pid) { ++ *p = -1; ++ break; ++ } ++ } ++ } + } else if (signo == SIGINT || signo == SIGTERM) { + quit(NULL); + } +@@ -2224,6 +2272,7 @@ run(char *startup_cmd) + die("startup: backend_start"); + + /* Now that the socket exists and the backend is started, run the startup command */ ++ autostartexec(); + if (startup_cmd) { + int piperw[2]; + if (pipe(piperw) < 0) +-- +2.45.2 + diff --git a/host/pc/common/dwl/patches/btrtile-v0.7-gaps.patch b/host/pc/common/dwl/patches/btrtile-v0.7-gaps.patch new file mode 100644 index 0000000..e81b60d --- /dev/null +++ b/host/pc/common/dwl/patches/btrtile-v0.7-gaps.patch @@ -0,0 +1,1074 @@ +From d56f0b61d6b611a977c065d0834f76b42c0aa038 Mon Sep 17 00:00:00 2001 +From: julmajustus +Date: Sat, 8 Feb 2025 05:41:12 +0200 +Subject: [PATCH] btrtile-gaps init + +--- + btrtile.c | 716 +++++++++++++++++++++++++++++++++++++++++++++++++++ + config.def.h | 12 + + dwl.c | 173 +++++++++++-- + 3 files changed, 874 insertions(+), 27 deletions(-) + create mode 100644 btrtile.c + +diff --git a/btrtile.c b/btrtile.c +new file mode 100644 +index 0000000..3ff3e20 +--- /dev/null ++++ b/btrtile.c +@@ -0,0 +1,716 @@ ++/* ************************************************************************** */ ++/* */ ++/* ::: :::::::: */ ++/* btrtile.c :+: :+: :+: */ ++/* +:+ +:+ +:+ */ ++/* By: jmakkone +#+ +:+ +#+ */ ++/* +#+#+#+#+#+ +#+ */ ++/* Created: 2024/12/15 00:26:07 by jmakkone #+# #+# */ ++/* Updated: 2025/02/08 04:52:00 by jmakkone ### ########.fr */ ++/* */ ++/* ************************************************************************** */ ++ ++typedef enum { ++ COORD_X, ++ COORD_Y ++} CoordType; ++ ++typedef struct LayoutNode { ++ unsigned int is_client_node; ++ unsigned int split_vertically; ++ float split_ratio; ++ struct LayoutNode *left; ++ struct LayoutNode *right; ++ struct LayoutNode *split_node; ++ Client *client; ++} LayoutNode; ++ ++struct TreeLayout { ++ LayoutNode *root[TAGCOUNT + 1]; ++ struct wl_list tiled_clients[TAGCOUNT + 1]; ++}; ++ ++static void add_client_to_tiled_list(Client *c, struct wl_list *tiled_clients); ++static void apply_layout(Monitor *m, LayoutNode *node, ++ struct wlr_box area, unsigned int is_root); ++static void btrtile(Monitor *m); ++static LayoutNode *create_client_node(Client *c); ++static LayoutNode *create_split_node(unsigned int split_vertically, ++ LayoutNode *left, LayoutNode *right); ++static void destroy_node_tree(LayoutNode *node); ++static void destroy_tree_layout(Monitor *m); ++static LayoutNode *find_client_node(LayoutNode *node, Client *c); ++static LayoutNode *find_split_node(LayoutNode *root, LayoutNode *child); ++static LayoutNode *find_suitable_split_node(LayoutNode *client_node, ++ unsigned int need_vertical); ++static LayoutNode *find_closest_client_node(LayoutNode *split_node, ++ enum Direction dir, int current_x, ++ int current_y, LayoutNode **closest, ++ int *closest_dist); ++static unsigned int get_client_center(LayoutNode *node, CoordType type); ++static unsigned int get_current_tag(Monitor *m); ++static void init_tree_layout(Monitor *m); ++static void insert_client(Monitor *m, Client *focused, Client *new_client, ++ LayoutNode **root, struct wl_list *tiled_clients); ++static unsigned int is_client_tiled(Client *c, struct wl_list *tiled_clients); ++static LayoutNode *remove_client_node(LayoutNode *node, Client *c); ++static void remove_client(Monitor *m, Client *c, ++ LayoutNode **root, struct wl_list *tiled_clients); ++static void setratio_h(const Arg *arg); ++static void setratio_v(const Arg *arg); ++static void swapclients(const Arg *arg); ++static Client *xytoclient(double x, double y, uint32_t tag); ++ ++static int resizing_from_mouse = 0; ++static double resize_last_update_x, resize_last_update_y; ++static uint32_t last_resize_time = 0; ++ ++void ++add_client_to_tiled_list(Client *c, struct wl_list *tiled_clients) ++{ ++ if (!is_client_tiled(c, tiled_clients)) ++ wl_list_insert(tiled_clients, &c->link_tiled); ++} ++ ++void ++apply_layout(Monitor *m, LayoutNode *node, ++ struct wlr_box area, unsigned int is_root) ++{ ++ float ratio; ++ int mid; ++ unsigned int e = m->gaps; ++ struct wlr_box left_area, right_area; ++ ++ if (!node) ++ return; ++ ++ if (is_root && e) { ++ area.x += gappx; ++ area.y += gappx; ++ area.width -= 2 * gappx; ++ area.height -= 2 * gappx; ++ } ++ ++ if (node->is_client_node) { ++ resize(node->client, area, 0); ++ node->client->old_geom = area; ++ return; ++ } ++ ++ ratio = node->split_ratio; ++ if (ratio == 0.0f) ++ ratio = 0.5f; ++ if (ratio < 0.05f) ++ ratio = 0.05f; ++ if (ratio > 0.95f) ++ ratio = 0.95f; ++ ++ if (node->split_vertically) { ++ mid = (int)(area.width * ratio); ++ left_area = (struct wlr_box){ area.x, area.y, mid, area.height}; ++ right_area = (struct wlr_box){ area.x + mid, area.y, ++ area.width - mid, area.height}; ++ ++ if (e) { ++ left_area.width -= gappx / 2; ++ right_area.x += gappx / 2; ++ right_area.width -= gappx / 2; ++ } ++ } else { ++ mid = (int)(area.height * ratio); ++ left_area = (struct wlr_box){ area.x, area.y, area.width, mid }; ++ right_area = (struct wlr_box){ area.x, area.y + mid, ++ area.width, area.height - mid }; ++ ++ if (e) { ++ left_area.height -= gappx / 2; ++ right_area.y += gappx / 2; ++ right_area.height -= gappx / 2; ++ } ++ } ++ ++ apply_layout(m, node->left, left_area, 0); ++ apply_layout(m, node->right, right_area, 0); ++} ++ ++void btrtile(Monitor *m) ++{ ++ uint32_t active_tags = m->tagset[m->seltags]; ++ unsigned int n = 0, found = 0, curtag; ++ Client *c, *cc, *tmp, *cur, *focused_client = NULL; ++ LayoutNode **root_ptr; ++ struct wl_list current_clients, *tiled_clients; ++ struct wlr_box full_area = m->w; ++ ++ /* We skip handling clients in btrtile if multiple tags are selected */ ++ if (!m->tree_layout || (active_tags && (active_tags & (active_tags - 1)))) ++ return; ++ curtag = get_current_tag(m); ++ root_ptr = &m->tree_layout->root[curtag]; ++ tiled_clients = &m->tree_layout->tiled_clients[curtag]; ++ wl_list_for_each_reverse(c, &clients, link) { ++ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) ++ n++; ++ } ++ ++ /* If no visible clients, clear the node tree and tiled_clients list. */ ++ if (n == 0) { ++ destroy_node_tree(*root_ptr); ++ *root_ptr = NULL; ++ wl_list_init(tiled_clients); ++ return; ++ } ++ ++ focused_client = xytoclient(cursor->x, cursor->y, curtag); ++ /* If no focused client found, pick the first visible */ ++ if (!focused_client) { ++ wl_list_for_each_reverse(c, &clients, link) { ++ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) { ++ focused_client = c; ++ break; ++ } ++ } ++ } ++ ++ /* Build a temporary list of currently visible tiled clients. ++ * If new clients are found add them to tree.*/ ++ wl_list_init(¤t_clients); ++ wl_list_for_each_reverse(c, &clients, link) { ++ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) { ++ found = 0; ++ /* If client has multiple tags set, we might create infinite ++ * point to self loops by adding the same client to multiple ++ * tiled_clients lists, so hacky way to prevent that is to revert ++ * clients active tags to current active tag if multiple ++ * tags are selected. */ ++ if (c->tags != 1u << curtag) ++ c->tags = 1u << curtag; ++ wl_list_for_each(cc, tiled_clients, link_tiled) { ++ if (cc == c) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) { ++ insert_client(m, focused_client, c, root_ptr, tiled_clients); ++ /* Recursion failsafe to handle inserted clients individually ++ * if a batch of new clients is added. */ ++ apply_layout(m, *root_ptr, full_area, 1); ++ btrtile(m); ++ return; ++ } ++ wl_list_insert(¤t_clients, &c->link_temp); ++ } ++ } ++ ++ /* Compare the current list of clients to the previous one and remove ++ * clients that no longer exist on the current tag. ++ * This handles cases where user moves clients to other tags. ++ * When client is closed or killed we manage the list and tree clean up in ++ * destroynotify. */ ++ wl_list_for_each_safe(cc, tmp, tiled_clients, link_tiled) { ++ found = 0; ++ wl_list_for_each(cur, ¤t_clients, link_temp) { ++ if (cur == cc) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) ++ remove_client(m, cc, root_ptr, tiled_clients); ++ } ++ ++ /* Rebuild the updated client list */ ++ wl_list_init(tiled_clients); ++ wl_list_for_each(cur, ¤t_clients, link_temp) { ++ wl_list_insert(tiled_clients, &cur->link_tiled); ++ } ++ ++ /* Tile visible clients. */ ++ apply_layout(m, *root_ptr, full_area, 1); ++} ++ ++LayoutNode * ++create_client_node(Client *c) ++{ ++ LayoutNode *node = calloc(1, sizeof(LayoutNode)); ++ ++ if (!node) ++ return NULL; ++ node->is_client_node = 1; ++ node->split_ratio = 0.5f; ++ node->client = c; ++ return node; ++} ++ ++LayoutNode * ++create_split_node(unsigned int split_vertically, ++ LayoutNode *left, LayoutNode *right) ++{ ++ LayoutNode *node = calloc(1, sizeof(LayoutNode)); ++ ++ if (!node) ++ return NULL; ++ node->is_client_node = 0; ++ node->split_ratio = 0.5f; ++ node->split_vertically = split_vertically; ++ node->left = left; ++ node->right = right; ++ if (left) ++ left->split_node = node; ++ if (right) ++ right->split_node = node; ++ return node; ++} ++ ++unsigned int ++is_client_tiled(Client *c, struct wl_list *tiled_clients) ++{ ++ Client *cc; ++ wl_list_for_each(cc, tiled_clients, link_tiled) { ++ if (cc == c) ++ return 1; ++ } ++ return 0; ++} ++ ++void ++destroy_node_tree(LayoutNode *node) ++{ ++ if (!node) ++ return; ++ if (!node->is_client_node) { ++ destroy_node_tree(node->left); ++ destroy_node_tree(node->right); ++ } ++ free(node); ++} ++ ++void ++destroy_tree_layout(Monitor *m) ++{ ++ if (!m) ++ return; ++ for (int i = 0; i <= TAGCOUNT; i++) { ++ destroy_node_tree(m->tree_layout->root[i]); ++ } ++} ++ ++LayoutNode * ++find_client_node(LayoutNode *node, Client *c) ++{ ++ LayoutNode *res; ++ ++ if (!node) ++ return NULL; ++ if (node->is_client_node) ++ return (node->client == c) ? node : NULL; ++ res = find_client_node(node->left, c); ++ return res ? res : find_client_node(node->right, c); ++} ++ ++LayoutNode * ++find_split_node(LayoutNode *root, LayoutNode *child) ++{ ++ LayoutNode *res; ++ ++ if (!root || root->is_client_node) ++ return NULL; ++ if (root->left == child || root->right == child) ++ return root; ++ res = find_split_node(root->left, child); ++ return res ? res : find_split_node(root->right, child); ++} ++ ++LayoutNode * ++find_suitable_split_node(LayoutNode *client_node, unsigned int need_vertical) ++{ ++ LayoutNode *node = client_node; ++ unsigned int curtag; ++ ++ curtag = get_current_tag(selmon); ++ /* If we're starting from a client_node, go up one level first */ ++ if (node->is_client_node) { ++ node = node->split_node ? node->split_node : ++ find_split_node(selmon->tree_layout->root[curtag], node); ++ } ++ ++ /* Climb the tree until we find a node that is not client_node and ++ * match needed orientation. */ ++ while (node && (node->is_client_node || ++ node->split_vertically != need_vertical)) { ++ node = node->split_node ? node->split_node : ++ find_split_node(selmon->tree_layout->root[curtag], node); ++ } ++ ++ return node; ++} ++ ++LayoutNode * ++find_closest_client_node(LayoutNode *split_node, enum Direction dir, ++ int current_x, int current_y, LayoutNode **closest, ++ int *closest_dist) ++{ ++ int client_center_x, client_center_y, dist, is_candidate; ++ if (!split_node) ++ return NULL; ++ ++ if (split_node->is_client_node && split_node->client) { ++ client_center_x = get_client_center(split_node, COORD_X); ++ client_center_y = get_client_center(split_node, COORD_Y); ++ dist = 0; ++ is_candidate = 0; ++ ++ switch (dir) { ++ case DIR_LEFT: ++ if (client_center_x < current_x) { ++ dist = current_x - client_center_x; ++ is_candidate = 1; ++ } ++ break; ++ case DIR_RIGHT: ++ if (client_center_x > current_x) { ++ dist = client_center_x - current_x; ++ is_candidate = 1; ++ } ++ break; ++ case DIR_UP: ++ if (client_center_y < current_y) { ++ dist = current_y - client_center_y; ++ is_candidate = 1; ++ } ++ break; ++ case DIR_DOWN: ++ if (client_center_y > current_y) { ++ dist = client_center_y - current_y; ++ is_candidate = 1; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ if (is_candidate && dist < *closest_dist) { ++ *closest_dist = dist; ++ *closest = split_node; ++ } ++ } ++ ++ /* Recursively search in left and right split_nodes */ ++ find_closest_client_node(split_node->left, dir, current_x, current_y, ++ closest, closest_dist); ++ find_closest_client_node(split_node->right, dir, current_x, current_y, ++ closest, closest_dist); ++ ++ return *closest; ++} ++ ++unsigned int ++get_client_center(LayoutNode *node, CoordType type) ++{ ++ if (!node || !node->is_client_node || !node->client) ++ return 0; ++ ++ switch (type) { ++ case COORD_X: ++ return node->client->old_geom.x + node->client->old_geom.width / 2; ++ case COORD_Y: ++ return node->client->old_geom.y + node->client->old_geom.height / 2; ++ default: ++ return 0; ++ } ++} ++ ++unsigned int ++get_current_tag(Monitor *m) ++{ ++ uint32_t active; ++ ++ if (!m) ++ return 0; ++ ++ active = m->tagset[m->seltags]; ++ for (int i = 0; i < TAGCOUNT; i++) { ++ if (active & (1u << i)) ++ return i; ++ } ++ return 0; ++} ++ ++void ++init_tree_layout(Monitor *m) ++{ ++ if (!m) ++ return; ++ m->tree_layout = calloc(1, sizeof(TreeLayout)); ++ for (int i = 0; i <= TAGCOUNT; i++) { ++ m->tree_layout->root[i] = NULL; ++ wl_list_init(&m->tree_layout->tiled_clients[i]); ++ } ++} ++ ++void ++insert_client(Monitor *m, Client *focused, Client *new_client, ++ LayoutNode **root, struct wl_list *tiled_clients) ++{ ++ int mid_x, mid_y; ++ LayoutNode *old_root, *client_node, *old_client_node, *new_client_node; ++ unsigned int wider; ++ ++ /* If there is no root node, inserted client must be the first one. ++ * If there's no focused client we treat the inserted client as a new root node.*/ ++ if (!*root) { ++ *root = create_client_node(new_client); ++ add_client_to_tiled_list(new_client, tiled_clients); ++ return; ++ } ++ if (!focused || !(client_node = find_client_node(*root, focused))) { ++ old_root = *root; ++ *root = create_split_node(1, old_root, create_client_node(new_client)); ++ add_client_to_tiled_list(new_client, tiled_clients); ++ return; ++ } ++ /* We check the cursor location on splittable area and choose ++ * the new client's position. On horizontal splits left node represent ++ * the upper node and vice versa.*/ ++ mid_x = focused->old_geom.x + focused->old_geom.width / 2; ++ mid_y = focused->old_geom.y + focused->old_geom.height / 2; ++ old_client_node = create_client_node(client_node->client); ++ new_client_node = create_client_node(new_client); ++ ++ wider = focused->old_geom.width >= focused->old_geom.height; ++ if (wider) { ++ /* Vertical split */ ++ client_node->split_vertically = 1; ++ if (cursor->x <= mid_x) { ++ client_node->left = new_client_node; ++ client_node->right = old_client_node; ++ } else { ++ client_node->left = old_client_node; ++ client_node->right = new_client_node; ++ } ++ } else { ++ /* Horizontal split */ ++ client_node->split_vertically = 0; ++ if (cursor->y <= mid_y) { ++ client_node->left = new_client_node; ++ client_node->right = old_client_node; ++ } else { ++ client_node->left = old_client_node; ++ client_node->right = new_client_node; ++ } ++ } ++ /* The old client node becomes the splitnode for the old and new client ++ * nodes.*/ ++ client_node->is_client_node = 0; ++ client_node->client = NULL; ++ add_client_to_tiled_list(new_client, tiled_clients); ++} ++ ++LayoutNode * ++remove_client_node(LayoutNode *node, Client *c) ++{ ++ LayoutNode *tmp; ++ if (!node) ++ return NULL; ++ if (node->is_client_node) { ++ /* If this client_node is the client we're removing, ++ * return NULL to remove it */ ++ if (node->client == c) { ++ free(node); ++ return NULL; ++ } ++ return node; ++ } ++ ++ node->left = remove_client_node(node->left, c); ++ node->right = remove_client_node(node->right, c); ++ ++ /* If one of the client node is NULL after removal and the other is not, ++ * we "lift" the other client node up to replace this split node. */ ++ if (!node->left && node->right) { ++ tmp = node->right; ++ ++ /* Save pointer to split node */ ++ if (tmp) ++ tmp->split_node = node->split_node; ++ ++ free(node); ++ return tmp; ++ } ++ ++ if (!node->right && node->left) { ++ tmp = node->left; ++ ++ /* Save pointer to split node */ ++ if (tmp) ++ tmp->split_node = node->split_node; ++ ++ free(node); ++ return tmp; ++ } ++ ++ /* If both children exist or both are NULL (empty tree), ++ * return node as is. */ ++ return node; ++} ++ ++void ++remove_client(Monitor *m, Client *c, LayoutNode **root, ++ struct wl_list *tiled_clients) ++{ ++ Client *cc, *tmp; ++ ++ *root = remove_client_node(*root, c); ++ wl_list_for_each_safe(cc, tmp, tiled_clients, link_tiled) { ++ if (cc == c) { ++ wl_list_remove(&cc->link_tiled); ++ break; ++ } ++ } ++} ++ ++void ++setratio_h(const Arg *arg) ++{ ++ Client *sel = focustop(selmon); ++ LayoutNode *client_node, *node; ++ float new_ratio; ++ ++ if (!arg || !sel || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ ++ client_node = find_client_node(selmon->tree_layout->root[get_current_tag(selmon)], sel); ++ if (!client_node) ++ return; ++ ++ /* Find a suitable vertical node */ ++ node = find_suitable_split_node(client_node, 1); ++ if (!node) ++ return; ++ ++ new_ratio = arg->f ? (node->split_ratio + arg->f) : 0.5f; ++ if (new_ratio < 0.05f) ++ new_ratio = 0.05f; ++ if (new_ratio > 0.95f) ++ new_ratio = 0.95f; ++ ++ node->split_ratio = new_ratio; ++ /* Skip the arrange if done resizing by mouse, ++ * we call arrange from motionotify */ ++ if (!resizing_from_mouse) { ++ arrange(selmon); ++ } ++} ++ ++void ++setratio_v(const Arg *arg) ++{ ++ float new_ratio; ++ Client *sel = focustop(selmon); ++ LayoutNode *client_node, *node; ++ ++ if (!arg || !sel || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ ++ client_node = find_client_node(selmon->tree_layout->root[get_current_tag(selmon)], sel); ++ if (!client_node) ++ return; ++ ++ /* Find a suitable horizontal node */ ++ node = find_suitable_split_node(client_node, 0); ++ if (!node) ++ return; ++ ++ new_ratio = arg->f ? (node->split_ratio + arg->f) : 0.5f; ++ if (new_ratio < 0.05f) new_ratio = 0.5f; ++ if (new_ratio > 0.95f) new_ratio = 0.95f; ++ ++ node->split_ratio = new_ratio; ++ /* Skip the arrange if done resizing by mouse, ++ * we call arrange from motionotify */ ++ if (!resizing_from_mouse) { ++ arrange(selmon); ++ } ++} ++ ++void ++swapclients(const Arg *arg) ++{ ++ Client *tmp, *sel = focustop(selmon); ++ enum Direction dir = (enum Direction)arg->i; ++ LayoutNode *client_node, *target = NULL, *split_node = NULL; ++ unsigned int current_x, current_y, curtag = get_current_tag(selmon); ++ int closest_dist = INT_MAX; ++ ++ if (!arg || !sel || !selmon->lt[selmon->sellt]->arrange) ++ return; ++ ++ client_node = find_client_node(selmon->tree_layout->root[curtag], sel); ++ if (!client_node) ++ return; ++ ++ current_x = get_client_center(client_node, COORD_X); ++ current_y = get_client_center(client_node, COORD_Y); ++ ++ /* For up/down swaps, restrict search within the current horizontal split ++ * node if no suitable horizontal split node is found, default to vertical */ ++ if (dir == DIR_UP || dir == DIR_DOWN) { ++ split_node = find_suitable_split_node(client_node, 0); ++ if (!split_node) ++ return; ++ } else { ++ split_node = selmon->tree_layout->root[curtag]; ++ } ++ ++ /* Find the closest client node in the specified direction and swap ++ * the clients */ ++ find_closest_client_node(split_node, dir, current_x, current_y, ++ &target, &closest_dist); ++ ++ if (target && target->is_client_node && target->client) { ++ tmp = client_node->client; ++ client_node->client = target->client; ++ target->client = tmp; ++ ++ arrange(selmon); ++ } ++} ++ ++Client * ++xytoclient(double x, double y, uint32_t tag) { ++ Client *c, *closest = NULL; ++ double dist, mindist = INT_MAX, dx, dy; ++ ++ wl_list_for_each_reverse(c, &selmon->tree_layout->tiled_clients[tag], link_tiled) { ++ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen && ++ x >= c->geom.x && x <= (c->geom.x + c->geom.width) && ++ y >= c->geom.y && y <= (c->geom.y + c->geom.height)){ ++ return c; ++ } ++ } ++ ++ /* If no client was found at cursor position fallback to closest. */ ++ wl_list_for_each_reverse(c, &selmon->tree_layout->tiled_clients[tag], link_tiled) { ++ if (VISIBLEON(c, selmon) && !c->isfloating && !c->isfullscreen) { ++ dx = 0, dy = 0; ++ ++ if (x < c->geom.x) ++ dx = c->geom.x - x; ++ else if (x > (c->geom.x + c->geom.width)) ++ dx = x - (c->geom.x + c->geom.width); ++ ++ if (y < c->geom.y) ++ dy = c->geom.y - y; ++ else if (y > (c->geom.y + c->geom.height)) ++ dy = y - (c->geom.y + c->geom.height); ++ ++ dist = sqrt(dx * dx + dy * dy); ++ if (dist < mindist) { ++ mindist = dist; ++ closest = c; ++ } ++ } ++ } ++ return closest; ++} +diff --git a/config.def.h b/config.def.h +index 22d2171..92f3ad6 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -13,7 +13,10 @@ static const float focuscolor[] = COLOR(0x005577ff); + static const float urgentcolor[] = COLOR(0xff0000ff); + /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ + static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */ ++static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */ ++static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */ + ++enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; + /* tagging - TAGCOUNT must be no greater than 31 */ + #define TAGCOUNT (9) + +@@ -31,6 +34,7 @@ static const Rule rules[] = { + /* layout(s) */ + static const Layout layouts[] = { + /* symbol arrange function */ ++ { "|w|", btrtile }, + { "[]=", tile }, + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +@@ -148,6 +152,14 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Up, swapclients, {.i = DIR_UP} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, swapclients, {.i = DIR_DOWN} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, swapclients, {.i = DIR_RIGHT} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, swapclients, {.i = DIR_LEFT} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Right, setratio_h, {.f = +0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Left, setratio_h, {.f = -0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Up, setratio_v, {.f = -0.025f} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_Down, setratio_v, {.f = +0.025f} }, + TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), + TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), + TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), +diff --git a/dwl.c b/dwl.c +index a2711f6..7682366 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -1,6 +1,7 @@ + /* + * See LICENSE file for copyright and license details. + */ ++#include + #include + #include + #include +@@ -103,6 +104,7 @@ typedef struct { + const Arg arg; + } Button; + ++typedef struct TreeLayout TreeLayout; + typedef struct Monitor Monitor; + typedef struct { + /* Must keep these three elements in this order */ +@@ -139,8 +141,11 @@ typedef struct { + #endif + unsigned int bw; + uint32_t tags; +- int isfloating, isurgent, isfullscreen; ++ int isfloating, isurgent, isfullscreen, was_tiled; + uint32_t resize; /* configure serial of a pending resize */ ++ struct wlr_box old_geom; ++ struct wl_list link_tiled; ++ struct wl_list link_temp; + } Client; + + typedef struct { +@@ -208,6 +213,7 @@ struct Monitor { + int nmaster; + char ltsymbol[16]; + int asleep; ++ TreeLayout *tree_layout; + }; + + typedef struct { +@@ -250,6 +256,7 @@ static void arrangelayer(Monitor *m, struct wl_list *list, + struct wlr_box *usable_area, int exclusive); + static void arrangelayers(Monitor *m); + static void axisnotify(struct wl_listener *listener, void *data); ++static void btrtile(Monitor *m); + static void buttonpress(struct wl_listener *listener, void *data); + static void chvt(const Arg *arg); + static void checkidleinhibitor(struct wlr_surface *exclude); +@@ -333,6 +340,9 @@ static void setmon(Client *c, Monitor *m, uint32_t newtags); + static void setpsel(struct wl_listener *listener, void *data); + static void setsel(struct wl_listener *listener, void *data); + static void setup(void); ++static void setratio_h(const Arg *arg); ++static void setratio_v(const Arg *arg); ++static void swapclients(const Arg *arg); + static void spawn(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); + static void tag(const Arg *arg); +@@ -431,6 +441,7 @@ static xcb_atom_t netatom[NetLast]; + + /* attempt to encapsulate suck into one file */ + #include "client.h" ++#include "btrtile.c" + + /* function implementations */ + void +@@ -600,10 +611,15 @@ buttonpress(struct wl_listener *listener, void *data) + { + struct wlr_pointer_button_event *event = data; + struct wlr_keyboard *keyboard; +- uint32_t mods; +- Client *c; ++ struct wl_list *tiled_clients; ++ LayoutNode **root, *old_root; ++ uint32_t mods, curtag, active_tags = selmon->tagset[selmon->seltags]; ++ Client *c, *target = NULL; + const Button *b; + ++ curtag = get_current_tag(selmon); ++ root = &selmon->tree_layout->root[curtag]; ++ tiled_clients = &selmon->tree_layout->tiled_clients[curtag]; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + + switch (event->state) { +@@ -632,15 +648,47 @@ buttonpress(struct wl_listener *listener, void *data) + /* If you released any buttons, we exit interactive move/resize mode. */ + /* TODO should reset to the pointer focus's current setcursor */ + if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { ++ c = grabc; ++ if (c && c->was_tiled && !strcmp(selmon->ltsymbol, "|w|")) { ++ /* Check if more than one tag is active, if so we escape */ ++ if (active_tags && (active_tags & (active_tags - 1))) ++ break; ++ if (cursor_mode == CurMove && c->isfloating) { ++ target = xytoclient(cursor->x, cursor->y, curtag); ++ ++ if (target && !target->isfloating && !target->isfullscreen) { ++ insert_client(selmon, target, c, root, tiled_clients); ++ } else { ++ if (!root) { ++ *root = create_client_node(c); ++ add_client_to_tiled_list(c, tiled_clients); ++ } else { ++ old_root = *root; ++ *root = create_split_node(1, old_root, create_client_node(c)); ++ add_client_to_tiled_list(c, tiled_clients); ++ } ++ } ++ ++ setfloating(c, 0); ++ arrange(selmon); ++ ++ } else if (cursor_mode == CurResize && !c->isfloating) { ++ resizing_from_mouse = 0; ++ } ++ } else { ++ if (cursor_mode == CurResize && resizing_from_mouse) ++ resizing_from_mouse = 0; ++ } ++ /* Default behaviour */ + wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); + cursor_mode = CurNormal; + /* Drop the window off on its new monitor */ + selmon = xytomon(cursor->x, cursor->y); + setmon(grabc, selmon, 0); ++ grabc = NULL; + return; +- } else { +- cursor_mode = CurNormal; + } ++ cursor_mode = CurNormal; + break; + } + /* If the event wasn't handled by the compositor, notify the client with +@@ -720,6 +768,9 @@ cleanupmon(struct wl_listener *listener, void *data) + wlr_output_layout_remove(output_layout, m->wlr_output); + wlr_scene_output_destroy(m->scene_output); + ++ destroy_tree_layout(m); ++ free(m->tree_layout); ++ m->tree_layout = NULL; + closemon(m); + wlr_scene_node_destroy(&m->fullscreen_bg->node); + free(m); +@@ -1024,6 +1075,7 @@ createmon(struct wl_listener *listener, void *data) + + wl_list_insert(&mons, &m->link); + printstatus(); ++ init_tree_layout(m); + + /* The xdg-protocol specifies: + * +@@ -1263,6 +1315,15 @@ destroynotify(struct wl_listener *listener, void *data) + wl_list_remove(&c->destroy.link); + wl_list_remove(&c->set_title.link); + wl_list_remove(&c->fullscreen.link); ++ /* We check if the destroyed client was part of any tiled_list, to catch ++ * client removals even if they would not be currently managed by btrtile */ ++ if (selmon && selmon->tree_layout) { ++ for (int i = 0; i < TAGCOUNT; i++) { ++ remove_client(selmon, c, ++ &selmon->tree_layout->root[i], ++ &selmon->tree_layout->tiled_clients[i]); ++ } ++ } + #ifdef XWAYLAND + if (c->type != XDGShell) { + wl_list_remove(&c->activate.link); +@@ -1809,7 +1870,8 @@ void + motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel) + { +- double sx = 0, sy = 0, sx_confined, sy_confined; ++ int tiled = 0; ++ double sx = 0, sy = 0, sx_confined, sy_confined, dx_total, dy_total; + Client *c = NULL, *w = NULL; + LayerSurface *l = NULL; + struct wlr_surface *surface = NULL; +@@ -1863,18 +1925,56 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d + /* Update drag icon's position */ + wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); + +- /* If we are currently grabbing the mouse, handle and return */ ++ /* Skip if internal call or already resizing */ ++ if (time == 0 && resizing_from_mouse) ++ goto focus; ++ ++ tiled = grabc && !grabc->isfloating && !grabc->isfullscreen; + if (cursor_mode == CurMove) { + /* Move the grabbed client to the new position. */ +- resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy, +- .width = grabc->geom.width, .height = grabc->geom.height}, 1); +- return; ++ if (grabc && grabc->isfloating) { ++ resize(grabc, (struct wlr_box){ ++ .x = (int)round(cursor->x) - grabcx, ++ .y = (int)round(cursor->y) - grabcy, ++ .width = grabc->geom.width, ++ .height = grabc->geom.height ++ }, 1); ++ return; ++ } + } else if (cursor_mode == CurResize) { +- resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, +- .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); +- return; ++ if (tiled && resizing_from_mouse) { ++ dx_total = cursor->x - resize_last_update_x; ++ dy_total = cursor->y - resize_last_update_y; ++ ++ if (time - last_resize_time >= resize_interval_ms) { ++ Arg a = {0}; ++ if (fabs(dx_total) > fabs(dy_total)) { ++ a.f = (float)(dx_total * resize_factor); ++ setratio_h(&a); ++ } else { ++ a.f = (float)(dy_total * resize_factor); ++ setratio_v(&a); ++ } ++ arrange(selmon); ++ ++ last_resize_time = time; ++ resize_last_update_x = cursor->x; ++ resize_last_update_y = cursor->y; ++ } ++ ++ } else if (grabc && grabc->isfloating) { ++ /* Floating resize as original */ ++ resize(grabc, (struct wlr_box){ ++ .x = grabc->geom.x, ++ .y = grabc->geom.y, ++ .width = (int)round(cursor->x) - grabc->geom.x, ++ .height = (int)round(cursor->y) - grabc->geom.y ++ }, 1); ++ return; ++ } + } + ++focus: + /* If there's no client surface under the cursor, set the cursor image to a + * default. This is what makes the cursor image appear when you move it + * off of a client or over its border. */ +@@ -1908,22 +2008,41 @@ moveresize(const Arg *arg) + if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) + return; + +- /* Float the window and tell motionnotify to grab it */ +- setfloating(grabc, 1); +- switch (cursor_mode = arg->ui) { +- case CurMove: +- grabcx = (int)round(cursor->x) - grabc->geom.x; +- grabcy = (int)round(cursor->y) - grabc->geom.y; +- wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); +- break; +- case CurResize: +- /* Doesn't work for X11 output - the next absolute motion event +- * returns the cursor to where it started */ +- wlr_cursor_warp_closest(cursor, NULL, ++ cursor_mode = arg->ui; ++ grabc->was_tiled = (!grabc->isfloating && !grabc->isfullscreen); ++ ++ if (grabc->was_tiled) { ++ switch (cursor_mode) { ++ case CurMove: ++ setfloating(grabc, 1); ++ grabcx = (int)round(cursor->x) - grabc->geom.x; ++ grabcy = (int)round(cursor->y) - grabc->geom.y; ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); ++ break; ++ case CurResize: ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); ++ resize_last_update_x = cursor->x; ++ resize_last_update_y = cursor->y; ++ resizing_from_mouse = 1; ++ break; ++ } ++ } else { ++ /* Default floating logic */ ++ /* Float the window and tell motionnotify to grab it */ ++ setfloating(grabc, 1); ++ switch (cursor_mode) { ++ case CurMove: ++ grabcx = (int)round(cursor->x) - grabc->geom.x; ++ grabcy = (int)round(cursor->y) - grabc->geom.y; ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); ++ break; ++ case CurResize: ++ wlr_cursor_warp_closest(cursor, NULL, + grabc->geom.x + grabc->geom.width, + grabc->geom.y + grabc->geom.height); +- wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); +- break; ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); ++ break; ++ } + } + } + +-- +2.45.3 + diff --git a/host/pc/common/dwl/patches/focusdir.patch b/host/pc/common/dwl/patches/focusdir.patch new file mode 100644 index 0000000..b74fd3e --- /dev/null +++ b/host/pc/common/dwl/patches/focusdir.patch @@ -0,0 +1,90 @@ +From a0e71a687b7fcaebdaf1da80c09bf5563bff46b1 Mon Sep 17 00:00:00 2001 +From: ldev +Date: Mon, 12 Feb 2024 21:50:24 +0100 +Subject: [PATCH] focusdir + +--- + config.def.h | 4 ++++ + dwl.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 49 insertions(+) + +diff --git a/config.def.h b/config.def.h +index 9009517..2a1a82e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -124,6 +124,10 @@ static const Key keys[] = { + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, + { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, + { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_h, focusdir, {.ui = 0} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_l, focusdir, {.ui = 1} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_k, focusdir, {.ui = 2} }, ++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_j, focusdir, {.ui = 3} }, + { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, + { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, + { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} }, +diff --git a/dwl.c b/dwl.c +index bf02a6d..64d5de7 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -268,6 +269,7 @@ static Monitor *dirtomon(enum wlr_direction dir); + static void focusclient(Client *c, int lift); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); ++static void focusdir(const Arg *arg); + static Client *focustop(Monitor *m); + static void fullscreennotify(struct wl_listener *listener, void *data); + static void handlesig(int signo); +@@ -1271,6 +1273,49 @@ focusstack(const Arg *arg) + focusclient(c, 1); + } + ++void focusdir(const Arg *arg) ++{ ++ /* Focus the left, right, up, down client relative to the current focused client on selmon */ ++ Client *c, *sel = focustop(selmon); ++ if (!sel || sel->isfullscreen) ++ return; ++ ++ int dist=INT_MAX; ++ Client *newsel = NULL; ++ int newdist=INT_MAX; ++ wl_list_for_each(c, &clients, link) { ++ if (!VISIBLEON(c, selmon)) ++ continue; /* skip non visible windows */ ++ ++ if (arg->ui == 0 && sel->geom.x <= c->geom.x) { ++ /* Client isn't on our left */ ++ continue; ++ } ++ if (arg->ui == 1 && sel->geom.x >= c->geom.x) { ++ /* Client isn't on our right */ ++ continue; ++ } ++ if (arg->ui == 2 && sel->geom.y <= c->geom.y) { ++ /* Client isn't above us */ ++ continue; ++ } ++ if (arg->ui == 3 && sel->geom.y >= c->geom.y) { ++ /* Client isn't below us */ ++ continue; ++ } ++ ++ dist=abs(sel->geom.x-c->geom.x)+abs(sel->geom.y-c->geom.y); ++ if (dist < newdist){ ++ newdist = dist; ++ newsel=c; ++ } ++ } ++ if (newsel != NULL){ ++ focusclient(newsel, 1); ++ } ++} ++ ++ + /* We probably should change the name of this, it sounds like + * will focus the topmost client of this mon, when actually will + * only return that client */ +-- +2.43.0 + diff --git a/host/pc/common/dwl/patches/gaps.patch b/host/pc/common/dwl/patches/gaps.patch new file mode 100644 index 0000000..c025baf --- /dev/null +++ b/host/pc/common/dwl/patches/gaps.patch @@ -0,0 +1,127 @@ +From 50e3dd4746b6cb719efb9f8213b94ac52a5320d9 Mon Sep 17 00:00:00 2001 +From: peesock +Date: Mon, 24 Jun 2024 20:06:42 -0700 +Subject: [PATCH] gaps! + +Co-authored-by: sewn +Co-authored-by: serenevoid +--- + config.def.h | 4 ++++ + dwl.c | 34 ++++++++++++++++++++++++++-------- + 2 files changed, 30 insertions(+), 8 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 22d2171..b388b4e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -6,6 +6,9 @@ + /* appearance */ + static const int sloppyfocus = 1; /* focus follows mouse */ + static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ ++static const int smartgaps = 0; /* 1 means no outer gap when there is only one window */ ++static int gaps = 1; /* 1 means gaps between windows are added */ ++static const unsigned int gappx = 10; /* gap pixel between windows */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const float rootcolor[] = COLOR(0x222222ff); + static const float bordercolor[] = COLOR(0x444444ff); +@@ -135,6 +138,7 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} }, + { MODKEY, XKB_KEY_Return, zoom, {0} }, + { MODKEY, XKB_KEY_Tab, view, {0} }, ++ { MODKEY, XKB_KEY_g, togglegaps, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} }, + { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, +diff --git a/dwl.c b/dwl.c +index dc0437e..dc851df 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -199,6 +199,7 @@ struct Monitor { + struct wlr_box w; /* window area, layout-relative */ + struct wl_list layers[4]; /* LayerSurface.link */ + const Layout *lt[2]; ++ int gaps; + unsigned int seltags; + unsigned int sellt; + uint32_t tagset[2]; +@@ -336,6 +337,7 @@ static void tagmon(const Arg *arg); + static void tile(Monitor *m); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); ++static void togglegaps(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unlocksession(struct wl_listener *listener, void *data); +@@ -949,6 +951,8 @@ createmon(struct wl_listener *listener, void *data) + + wlr_output_state_init(&state); + /* Initialize monitor state using configured rules */ ++ m->gaps = gaps; ++ + m->tagset[0] = m->tagset[1] = 1; + for (r = monrules; r < END(monrules); r++) { + if (!r->name || strstr(wlr_output->name, r->name)) { +@@ -2638,7 +2642,7 @@ tagmon(const Arg *arg) + void + tile(Monitor *m) + { +- unsigned int mw, my, ty; ++ unsigned int h, r, e = m->gaps, mw, my, ty; + int i, n = 0; + Client *c; + +@@ -2647,23 +2651,30 @@ tile(Monitor *m) + n++; + if (n == 0) + return; ++ if (smartgaps == n) ++ e = 0; + + if (n > m->nmaster) +- mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0; ++ mw = m->nmaster ? (int)roundf((m->w.width + gappx*e) * m->mfact) : 0; + else + mw = m->w.width; +- i = my = ty = 0; ++ i = 0; ++ my = ty = gappx*e; + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + continue; + if (i < m->nmaster) { +- resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw, +- .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0); +- my += c->geom.height; ++ r = MIN(n, m->nmaster) - i; ++ h = (m->w.height - my - gappx*e - gappx*e * (r - 1)) / r; ++ resize(c, (struct wlr_box){.x = m->w.x + gappx*e, .y = m->w.y + my, ++ .width = mw - 2*gappx*e, .height = h}, 0); ++ my += c->geom.height + gappx*e; + } else { ++ r = n - i; ++ h = (m->w.height - ty - gappx*e - gappx*e * (r - 1)) / r; + resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty, +- .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0); +- ty += c->geom.height; ++ .width = m->w.width - mw - gappx*e, .height = h}, 0); ++ ty += c->geom.height + gappx*e; + } + i++; + } +@@ -2686,6 +2697,13 @@ togglefullscreen(const Arg *arg) + setfullscreen(sel, !sel->isfullscreen); + } + ++void ++togglegaps(const Arg *arg) ++{ ++ selmon->gaps = !selmon->gaps; ++ arrange(selmon); ++} ++ + void + toggletag(const Arg *arg) + { +-- +2.45.2 + diff --git a/host/pc/common/dwl/patches/ipc.patch b/host/pc/common/dwl/patches/ipc.patch new file mode 100644 index 0000000..fd51b84 --- /dev/null +++ b/host/pc/common/dwl/patches/ipc.patch @@ -0,0 +1,601 @@ +From 6c6d655b68770ce82a24fde9b58c4d97b672553a Mon Sep 17 00:00:00 2001 +From: choc +Date: Mon, 23 Oct 2023 10:35:17 +0800 +Subject: [PATCH] implement dwl-ipc-unstable-v2 + https://codeberg.org/dwl/dwl-patches/wiki/ipc + +--- + Makefile | 14 +- + config.def.h | 1 + + dwl.c | 261 ++++++++++++++++++++++++++---- + protocols/dwl-ipc-unstable-v2.xml | 181 +++++++++++++++++++++ + 4 files changed, 419 insertions(+), 38 deletions(-) + create mode 100644 protocols/dwl-ipc-unstable-v2.xml + +diff --git a/Makefile b/Makefile +index 3358bae..28ed034 100644 +--- a/Makefile ++++ b/Makefile +@@ -17,12 +17,14 @@ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CF + LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` -lm $(LIBS) + + all: dwl +-dwl: dwl.o util.o +- $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ ++dwl: dwl.o util.o dwl-ipc-unstable-v2-protocol.o ++ $(CC) dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ + dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \ + pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \ +- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h ++ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \ ++ dwl-ipc-unstable-v2-protocol.h + util.o: util.c util.h ++dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.c dwl-ipc-unstable-v2-protocol.h + + # wayland-scanner is a tool which generates C headers and rigging for Wayland + # protocols, which are specified in XML. wlroots requires you to rig these up +@@ -45,6 +47,12 @@ wlr-output-power-management-unstable-v1-protocol.h: + xdg-shell-protocol.h: + $(WAYLAND_SCANNER) server-header \ + $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ ++dwl-ipc-unstable-v2-protocol.h: ++ $(WAYLAND_SCANNER) server-header \ ++ protocols/dwl-ipc-unstable-v2.xml $@ ++dwl-ipc-unstable-v2-protocol.c: ++ $(WAYLAND_SCANNER) private-code \ ++ protocols/dwl-ipc-unstable-v2.xml $@ + + config.h: + cp config.def.h $@ +diff --git a/config.def.h b/config.def.h +index 22d2171..1593033 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -127,6 +127,7 @@ static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, ++ { MODKEY, XKB_KEY_b, togglebar, {0} }, + { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, + { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, + { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, +diff --git a/dwl.c b/dwl.c +index a2711f6..d9b8bae 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -67,6 +67,7 @@ + #include + #endif + ++#include "dwl-ipc-unstable-v2-protocol.h" + #include "util.h" + + /* macros */ +@@ -143,6 +144,12 @@ typedef struct { + uint32_t resize; /* configure serial of a pending resize */ + } Client; + ++typedef struct { ++ struct wl_list link; ++ struct wl_resource *resource; ++ Monitor *mon; ++} DwlIpcOutput; ++ + typedef struct { + uint32_t mod; + xkb_keysym_t keysym; +@@ -188,6 +195,7 @@ typedef struct { + + struct Monitor { + struct wl_list link; ++ struct wl_list dwl_ipc_outputs; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; + struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ +@@ -285,6 +293,17 @@ static void destroysessionlock(struct wl_listener *listener, void *data); + static void destroysessionmgr(struct wl_listener *listener, void *data); + static void destroykeyboardgroup(struct wl_listener *listener, void *data); + static Monitor *dirtomon(enum wlr_direction dir); ++static void dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); ++static void dwl_ipc_manager_destroy(struct wl_resource *resource); ++static void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output); ++static void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource); ++static void dwl_ipc_output_destroy(struct wl_resource *resource); ++static void dwl_ipc_output_printstatus(Monitor *monitor); ++static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); ++static void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags); ++static void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index); ++static void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset); ++static void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource); + static void focusclient(Client *c, int lift); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); +@@ -338,6 +357,7 @@ static void startdrag(struct wl_listener *listener, void *data); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *m); ++static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -413,6 +433,9 @@ static struct wlr_box sgeom; + static struct wl_list mons; + static Monitor *selmon; + ++static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output}; ++static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags}; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -706,6 +729,10 @@ cleanupmon(struct wl_listener *listener, void *data) + LayerSurface *l, *tmp; + size_t i; + ++ DwlIpcOutput *ipc_output, *ipc_output_tmp; ++ wl_list_for_each_safe(ipc_output, ipc_output_tmp, &m->dwl_ipc_outputs, link) ++ wl_resource_destroy(ipc_output->resource); ++ + /* m->layers[i] are intentionally not unlinked */ + for (i = 0; i < LENGTH(m->layers); i++) { + wl_list_for_each_safe(l, tmp, &m->layers[i], link) +@@ -986,6 +1013,8 @@ createmon(struct wl_listener *listener, void *data) + m = wlr_output->data = ecalloc(1, sizeof(*m)); + m->wlr_output = wlr_output; + ++ wl_list_init(&m->dwl_ipc_outputs); ++ + for (i = 0; i < LENGTH(m->layers); i++) + wl_list_init(&m->layers[i]); + +@@ -1336,6 +1365,192 @@ dirtomon(enum wlr_direction dir) + return selmon; + } + ++void ++dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) ++{ ++ struct wl_resource *manager_resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id); ++ if (!manager_resource) { ++ wl_client_post_no_memory(client); ++ return; ++ } ++ wl_resource_set_implementation(manager_resource, &dwl_manager_implementation, NULL, dwl_ipc_manager_destroy); ++ ++ zdwl_ipc_manager_v2_send_tags(manager_resource, TAGCOUNT); ++ ++ for (unsigned int i = 0; i < LENGTH(layouts); i++) ++ zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); ++} ++ ++void ++dwl_ipc_manager_destroy(struct wl_resource *resource) ++{ ++ /* No state to destroy */ ++} ++ ++void ++dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor = wlr_output_from_resource(output)->data; ++ struct wl_resource *output_resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface, wl_resource_get_version(resource), id); ++ if (!output_resource) ++ return; ++ ++ ipc_output = ecalloc(1, sizeof(*ipc_output)); ++ ipc_output->resource = output_resource; ++ ipc_output->mon = monitor; ++ wl_resource_set_implementation(output_resource, &dwl_output_implementation, ipc_output, dwl_ipc_output_destroy); ++ wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link); ++ dwl_ipc_output_printstatus_to(ipc_output); ++} ++ ++void ++dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource) ++{ ++ wl_resource_destroy(resource); ++} ++ ++static void ++dwl_ipc_output_destroy(struct wl_resource *resource) ++{ ++ DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource); ++ wl_list_remove(&ipc_output->link); ++ free(ipc_output); ++} ++ ++void ++dwl_ipc_output_printstatus(Monitor *monitor) ++{ ++ DwlIpcOutput *ipc_output; ++ wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link) ++ dwl_ipc_output_printstatus_to(ipc_output); ++} ++ ++void ++dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) ++{ ++ Monitor *monitor = ipc_output->mon; ++ Client *c, *focused; ++ int tagmask, state, numclients, focused_client, tag; ++ const char *title, *appid; ++ focused = focustop(monitor); ++ zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); ++ ++ for (tag = 0 ; tag < TAGCOUNT; tag++) { ++ numclients = state = focused_client = 0; ++ tagmask = 1 << tag; ++ if ((tagmask & monitor->tagset[monitor->seltags]) != 0) ++ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE; ++ ++ wl_list_for_each(c, &clients, link) { ++ if (c->mon != monitor) ++ continue; ++ if (!(c->tags & tagmask)) ++ continue; ++ if (c == focused) ++ focused_client = 1; ++ if (c->isurgent) ++ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT; ++ ++ numclients++; ++ } ++ zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state, numclients, focused_client); ++ } ++ title = focused ? client_get_title(focused) : ""; ++ appid = focused ? client_get_appid(focused) : ""; ++ ++ zdwl_ipc_output_v2_send_layout(ipc_output->resource, monitor->lt[monitor->sellt] - layouts); ++ zdwl_ipc_output_v2_send_title(ipc_output->resource, title); ++ zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid); ++ zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, monitor->ltsymbol); ++ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FULLSCREEN_SINCE_VERSION) { ++ zdwl_ipc_output_v2_send_fullscreen(ipc_output->resource, focused ? focused->isfullscreen : 0); ++ } ++ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FLOATING_SINCE_VERSION) { ++ zdwl_ipc_output_v2_send_floating(ipc_output->resource, focused ? focused->isfloating : 0); ++ } ++ zdwl_ipc_output_v2_send_frame(ipc_output->resource); ++} ++ ++void ++dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ Client *selected_client; ++ unsigned int newtags = 0; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ ++ monitor = ipc_output->mon; ++ selected_client = focustop(monitor); ++ if (!selected_client) ++ return; ++ ++ newtags = (selected_client->tags & and_tags) ^ xor_tags; ++ if (!newtags) ++ return; ++ ++ selected_client->tags = newtags; ++ if (selmon == monitor) ++ focusclient(focustop(monitor), 1); ++ arrange(selmon); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ ++ monitor = ipc_output->mon; ++ if (index >= LENGTH(layouts)) ++ return; ++ if (index != monitor->lt[monitor->sellt] - layouts) ++ monitor->sellt ^= 1; ++ ++ monitor->lt[monitor->sellt] = &layouts[index]; ++ arrange(monitor); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ unsigned int newtags = tagmask & TAGMASK; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ monitor = ipc_output->mon; ++ ++ if (!newtags || newtags == monitor->tagset[monitor->seltags]) ++ return; ++ if (toggle_tagset) ++ monitor->seltags ^= 1; ++ ++ monitor->tagset[monitor->seltags] = newtags; ++ if (selmon == monitor) ++ focusclient(focustop(monitor), 1); ++ arrange(monitor); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource) ++{ ++ wl_resource_destroy(resource); ++} ++ + void + focusclient(Client *c, int lift) + { +@@ -2036,41 +2251,8 @@ void + printstatus(void) + { + Monitor *m = NULL; +- Client *c; +- uint32_t occ, urg, sel; +- const char *appid, *title; +- +- wl_list_for_each(m, &mons, link) { +- occ = urg = 0; +- wl_list_for_each(c, &clients, link) { +- if (c->mon != m) +- continue; +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- if ((c = focustop(m))) { +- title = client_get_title(c); +- appid = client_get_appid(c); +- printf("%s title %s\n", m->wlr_output->name, title ? title : broken); +- printf("%s appid %s\n", m->wlr_output->name, appid ? appid : broken); +- printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen); +- printf("%s floating %d\n", m->wlr_output->name, c->isfloating); +- sel = c->tags; +- } else { +- printf("%s title \n", m->wlr_output->name); +- printf("%s appid \n", m->wlr_output->name); +- printf("%s fullscreen \n", m->wlr_output->name); +- printf("%s floating \n", m->wlr_output->name); +- sel = 0; +- } +- +- printf("%s selmon %u\n", m->wlr_output->name, m == selmon); +- printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n", +- m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg); +- printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); +- } +- fflush(stdout); ++ wl_list_for_each(m, &mons, link) ++ dwl_ipc_output_printstatus(m); + } + + void +@@ -2625,6 +2807,8 @@ setup(void) + LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply); + LISTEN_STATIC(&output_mgr->events.test, outputmgrtest); + ++ wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind); ++ + /* Make sure XWayland clients don't connect to the parent X server, + * e.g when running in the x11 backend or the wayland backend and the + * compositor has Xwayland support */ +@@ -2722,6 +2906,13 @@ tile(Monitor *m) + } + } + ++void ++togglebar(const Arg *arg) { ++ DwlIpcOutput *ipc_output; ++ wl_list_for_each(ipc_output, &selmon->dwl_ipc_outputs, link) ++ zdwl_ipc_output_v2_send_toggle_visibility(ipc_output->resource); ++} ++ + void + togglefloating(const Arg *arg) + { +diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml +new file mode 100644 +index 0000000..0a6e7e5 +--- /dev/null ++++ b/protocols/dwl-ipc-unstable-v2.xml +@@ -0,0 +1,181 @@ ++ ++ ++ ++ ++ This protocol allows clients to update and get updates from dwl. ++ ++ Warning! The protocol described in this file is experimental and ++ backward incompatible changes may be made. Backward compatible ++ changes may be added together with the corresponding interface ++ version bump. ++ Backward incompatible changes are done by bumping the version ++ number in the protocol and interface names and resetting the ++ interface version. Once the protocol is to be declared stable, ++ the 'z' prefix and the version number in the protocol and ++ interface names are removed and the interface version number is ++ reset. ++ ++ ++ ++ ++ This interface is exposed as a global in wl_registry. ++ ++ Clients can use this interface to get a dwl_ipc_output. ++ After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. ++ The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. ++ ++ ++ ++ ++ Indicates that the client will not the dwl_ipc_manager object anymore. ++ Objects created through this instance are not affected. ++ ++ ++ ++ ++ ++ Get a dwl_ipc_outout for the specified wl_output. ++ ++ ++ ++ ++ ++ ++ ++ This event is sent after binding. ++ A roundtrip after binding guarantees the client recieved all tags. ++ ++ ++ ++ ++ ++ ++ This event is sent after binding. ++ A roundtrip after binding guarantees the client recieved all layouts. ++ ++ ++ ++ ++ ++ ++ ++ Observe and control a dwl output. ++ ++ Events are double-buffered: ++ Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. ++ ++ Request are not double-buffered: ++ The compositor will update immediately upon request. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Indicates to that the client no longer needs this dwl_ipc_output. ++ ++ ++ ++ ++ ++ Indicates the client should hide or show themselves. ++ If the client is visible then hide, if hidden then show. ++ ++ ++ ++ ++ ++ Indicates if the output is active. Zero is invalid, nonzero is valid. ++ ++ ++ ++ ++ ++ ++ Indicates that a tag has been updated. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Indicates a new layout is selected. ++ ++ ++ ++ ++ ++ ++ Indicates the title has changed. ++ ++ ++ ++ ++ ++ ++ Indicates the appid has changed. ++ ++ ++ ++ ++ ++ ++ Indicates the layout has changed. Since layout symbols are dynamic. ++ As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. ++ You can ignore the zdwl_ipc_output.layout event. ++ ++ ++ ++ ++ ++ ++ Indicates that a sequence of status updates have finished and the client should redraw. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ The tags are updated as follows: ++ new_tags = (current_tags AND and_tags) XOR xor_tags ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Indicates if the selected client on this output is fullscreen. ++ ++ ++ ++ ++ ++ ++ Indicates if the selected client on this output is floating. ++ ++ ++ ++ ++ +-- +2.51.2 + + diff --git a/host/pc/common/dwl/patches/monitorconfig.patch b/host/pc/common/dwl/patches/monitorconfig.patch new file mode 100644 index 0000000..4922f8d --- /dev/null +++ b/host/pc/common/dwl/patches/monitorconfig.patch @@ -0,0 +1,95 @@ +From 73f70cd9d817a307030f360f6c8a2500046b8b76 Mon Sep 17 00:00:00 2001 +From: Palanix +Date: Mon, 4 Apr 2022 16:08:29 +0200 +Subject: [PATCH] Updated patch now allowing setting x and y +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Co-authored-by: Leonardo Hernández Hernández +--- + config.def.h | 11 +++++++---- + dwl.c | 25 +++++++++++++++++++------ + 2 files changed, 26 insertions(+), 10 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 8f498d2..4ccacd2 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -38,12 +38,15 @@ static const Layout layouts[] = { + /* monitors */ + /* NOTE: ALWAYS add a fallback rule, even if you are completely sure it won't be used */ + static const MonitorRule monrules[] = { +- /* name mfact nmaster scale layout rotate/reflect x y */ +- /* example of a HiDPI laptop monitor: +- { "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, ++ /* name mfact nmaster scale layout rotate/reflect x y resx resy rate mode adaptive*/ ++ /* example of a HiDPI laptop monitor at 120Hz: ++ { "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, 0, 0, 120.000f, 1, 1}, ++ * mode let's the user decide on how dwl should implement the modes: ++ * -1 Sets a custom mode following the users choice ++ * All other number's set the mode at the index n, 0 is the standard mode; see wlr-randr + */ + /* defaults */ +- { NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, ++ { NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1, 0, 0, 0.0f, 0 ,1}, + }; + + /* keyboard */ +diff --git a/dwl.c b/dwl.c +index 52bfbc8..9609b6d 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -215,6 +215,11 @@ typedef struct { + const Layout *lt; + enum wl_output_transform rr; + int x, y; ++ int resx; ++ int resy; ++ float rate; ++ int mode; ++ int adaptive; + } MonitorRule; + + typedef struct { +@@ -865,6 +870,7 @@ createmon(struct wl_listener *listener, void *data) + /* This event is raised by the backend when a new output (aka a display or + * monitor) becomes available. */ + struct wlr_output *wlr_output = data; ++ struct wlr_output_mode *mode = wl_container_of(wlr_output->modes.next, mode, link); + const MonitorRule *r; + size_t i; + struct wlr_output_state state; +@@ -893,16 +899,23 @@ createmon(struct wl_listener *listener, void *data) + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); + wlr_output_state_set_scale(&state, r->scale); + wlr_output_state_set_transform(&state, r->rr); ++ ++ wlr_output_state_set_adaptive_sync_enabled(&state, r->adaptive); ++ ++ if(r->mode == -1) ++ wlr_output_state_set_custom_mode(&state, r->resx, r->resy, ++ (int) (r->rate > 0 ? r->rate * 1000 : 0)); ++ else if (!wl_list_empty(&wlr_output->modes)) { ++ for (int j = 0; j < r->mode; j++) { ++ mode = wl_container_of(mode->link.next, mode, link); ++ } ++ wlr_output_state_set_mode(&state, mode); ++ } ++ + break; + } + } + +- /* The mode is a tuple of (width, height, refresh rate), and each +- * monitor supports only a specific set of modes. We just pick the +- * monitor's preferred mode; a more sophisticated compositor would let +- * the user configure it. */ +- wlr_output_state_set_mode(&state, wlr_output_preferred_mode(wlr_output)); +- + /* Set up event listeners */ + LISTEN(&wlr_output->events.frame, &m->frame, rendermon); + LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); +-- +2.45.1 + diff --git a/host/pc/common/dwl/patches/relative-mouse-resize.patch b/host/pc/common/dwl/patches/relative-mouse-resize.patch new file mode 100644 index 0000000..e1dfc57 --- /dev/null +++ b/host/pc/common/dwl/patches/relative-mouse-resize.patch @@ -0,0 +1,102 @@ +From 5c75c67fe49e5ab89e4a61dfb2fe74c768477b90 Mon Sep 17 00:00:00 2001 +From: wochap +Date: Fri, 5 Jul 2024 11:13:53 -0500 +Subject: [PATCH] implement relative-muse-resize + +--- + dwl.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 59 insertions(+), 7 deletions(-) + +diff --git a/dwl.c b/dwl.c +index dc0437e..ebf9ef1 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -401,7 +401,8 @@ static struct wlr_seat *seat; + static KeyboardGroup *kb_group; + static unsigned int cursor_mode; + static Client *grabc; +-static int grabcx, grabcy; /* client-relative */ ++static Client initial_grabc; ++static int grabcx, grabcy, grabx, graby, grabcenterx, grabcentery; /* client-relative */ + + static struct wlr_output_layout *output_layout; + static struct wlr_box sgeom; +@@ -1821,8 +1822,27 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d + .width = grabc->geom.width, .height = grabc->geom.height}, 1); + return; + } else if (cursor_mode == CurResize) { +- resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, +- .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); ++ if (grabcenterx < grabx) { ++ if (grabcentery < graby) { ++ /* bottom-right */ ++ resize(grabc, (struct wlr_box){.x = initial_grabc.geom.x, .y = initial_grabc.geom.y, ++ .width = (int)round(cursor->x) - initial_grabc.geom.x, .height = (int)round(cursor->y) - initial_grabc.geom.y}, 1); ++ } else { ++ /* top-right */ ++ resize(grabc, (struct wlr_box){.x = initial_grabc.geom.x, .y = (int)round(cursor->y), ++ .width = (int)round(cursor->x) - initial_grabc.geom.x, .height = initial_grabc.geom.y + initial_grabc.geom.height - (int)round(cursor->y)}, 1); ++ } ++ } else { ++ if (grabcentery < graby) { ++ /* bottom-left */ ++ resize(grabc, (struct wlr_box){.x = (int)round(cursor->x), .y = initial_grabc.geom.y, ++ .width = initial_grabc.geom.x + initial_grabc.geom.width - (int)round(cursor->x), .height = (int)round(cursor->y) - initial_grabc.geom.y}, 1); ++ } else { ++ /* top-left */ ++ resize(grabc, (struct wlr_box){.x = (int)round(cursor->x), .y = (int)round(cursor->y), ++ .width = initial_grabc.geom.x + initial_grabc.geom.width - (int)round(cursor->x), .height = initial_grabc.geom.y + initial_grabc.geom.height - (int)round(cursor->y)}, 1); ++ } ++ } + return; + } + +@@ -1870,10 +1890,42 @@ moveresize(const Arg *arg) + case CurResize: + /* Doesn't work for X11 output - the next absolute motion event + * returns the cursor to where it started */ +- wlr_cursor_warp_closest(cursor, NULL, +- grabc->geom.x + grabc->geom.width, +- grabc->geom.y + grabc->geom.height); +- wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); ++ initial_grabc = *grabc; ++ grabx = (int)round(cursor->x); ++ graby = (int)round(cursor->y); ++ grabcx = (int)round(cursor->x) - grabc->geom.x; ++ grabcy = (int)round(cursor->y) - grabc->geom.y; ++ grabcenterx = grabc->geom.width / 2 + grabc->geom.x; ++ grabcentery = grabc->geom.height / 2 + grabc->geom.y; ++ if (grabcenterx < grabx) { ++ if (grabcentery < graby) { ++ /* bottom-right */ ++ wlr_cursor_warp_closest(cursor, NULL, ++ grabc->geom.x + grabc->geom.width, ++ grabc->geom.y + grabc->geom.height); ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); ++ } else { ++ /* top-right */ ++ wlr_cursor_warp_closest(cursor, NULL, ++ grabc->geom.x + grabc->geom.width, ++ grabc->geom.y); ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "ne-resize"); ++ } ++ } else { ++ if (grabcentery < graby) { ++ /* bottom-left */ ++ wlr_cursor_warp_closest(cursor, NULL, ++ grabc->geom.x, ++ grabc->geom.y + grabc->geom.height); ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "sw-resize"); ++ } else { ++ /* top-left */ ++ wlr_cursor_warp_closest(cursor, NULL, ++ grabc->geom.x, ++ grabc->geom.y); ++ wlr_cursor_set_xcursor(cursor, cursor_mgr, "nw-resize"); ++ } ++ } + break; + } + } +-- +2.45.1 \ No newline at end of file diff --git a/host/pc/common/stylix.nix b/host/pc/common/stylix.nix index e88d846..971cc74 100644 --- a/host/pc/common/stylix.nix +++ b/host/pc/common/stylix.nix @@ -11,10 +11,11 @@ let phases = "buildPhase"; buildPhase = '' mkdir -p $out/ - for FILE in ${pkgs.base16-schemes}/share/themes/*; do - FILEBASENAME="$(basename $FILE .yaml).json" - cat $FILE | ${pkgs.yaml2json}/bin/yaml2json | sed s/#//g > $out/$FILEBASENAME - done + #for FILE in ${pkgs.base16-schemes}/share/themes/*; do + FILE=${pkgs.base16-schemes}/share/themes/catppuccin-mocha.yaml + FILEBASENAME="$(basename $FILE .yaml).json" + cat $FILE | ${pkgs.yaml2json}/bin/yaml2json | sed s/#//g > $out/$FILEBASENAME + #done ls -l $out/ ''; }; @@ -27,6 +28,11 @@ let name = "CaskaydiaCove Nerd Font"; }; + emojiFont = { + package = pkgs.dejavu_fonts; + name = "DejaVu Sans Mono"; + }; + makeTheme = jsonScheme: polarity: let @@ -37,7 +43,7 @@ let base16Scheme.palette = palette; inherit polarity; fonts = { - emoji = font; + emoji = emojiFont; monospace = font; sansSerif = font; serif = font; @@ -64,19 +70,71 @@ let in { - stylix = makeTheme "catppuccin-mocha" "dark"; + #stylix = makeTheme "catppuccin-mocha" "dark"; - specialisation = { - catppuccin-mocha.configuration.stylix = lib.mkForce (makeTheme "catppuccin-mocha" "dark"); - catppuccin-latte.configuration.stylix = lib.mkForce (makeTheme "catppuccin-latte" "light"); - gruvbox-dark-hard.configuration.stylix = lib.mkForce (makeTheme "gruvbox-dark-hard" "dark"); - heetch.configuration.stylix = lib.mkForce (makeTheme "heetch" "dark"); - }; + #specialisation = { + # catppuccin-mocha.configuration.stylix = lib.mkForce (makeTheme "catppuccin-mocha" "dark"); + # catppuccin-latte.configuration.stylix = lib.mkForce (makeTheme "catppuccin-latte" "light"); + # gruvbox-dark-hard.configuration.stylix = lib.mkForce (makeTheme "gruvbox-dark-hard" "dark"); + # heetch.configuration.stylix = lib.mkForce (makeTheme "heetch" "dark"); + #}; - security.sudo.extraConfig = '' - ${userName} ALL = (root) NOPASSWD: /nix/var/nix/profiles/system/specialisation/catppuccin-mocha/bin/switch-to-configuration - ${userName} ALL = (root) NOPASSWD: /nix/var/nix/profiles/system/specialisation/catppuccin-latte/bin/switch-to-configuration - ${userName} ALL = (root) NOPASSWD: /nix/var/nix/profiles/system/specialisation/gruvbox-dark-hard/bin/switch-to-configuration - ${userName} ALL = (root) NOPASSWD: /nix/var/nix/profiles/system/specialisation/heetch/bin/switch-to-configuration - ''; + stylix = + let + palette = { + base00 = "11111a"; # #11111a + base01 = "181825"; # #181825 + base02 = "313244"; # #313244 + base03 = "45475a"; # #45475a + base04 = "585b70"; # #585b70 + base05 = "cdd6f4"; # #cdd6f4 + base06 = "f5e0dc"; # #f5e0dc + base07 = "b4befe"; # #b4befe + base08 = "f38ba8"; # #f38ba8 + base09 = "fab387"; # #fab387 + base0A = "f9e2af"; # #f9e2af + base0B = "a6e3a1"; # #a6e3a1 + base0C = "94e2d5"; # #94e2d5 + base0D = "f5c2e7"; # #f5c2e7 + base0E = "cba6f7"; # #cba6f7 + base0F = "f2cdcd"; # #f2cdcd + }; + polarity = "dark"; + in + { + enable = true; + base16Scheme.palette = palette; + inherit polarity; + fonts = { + emoji = emojiFont; + monospace = font; + sansSerif = font; + serif = font; + }; + image = + let + wallpaper = inputs.nix-wallpaper.packages.${pkgs.stdenv.hostPlatform.system}.default.override { + width = 2560; + height = 1440; + backgroundColor = "#${palette.base00}"; + logoColors = { + color0 = "#${palette.base0D}"; + color1 = "#${palette.base0D}"; + color2 = "#${palette.base0D}"; + color3 = "#${palette.base0D}"; + color4 = "#${palette.base0D}"; + color5 = "#${palette.base0D}"; + }; + }; + + in + "${wallpaper}/share/wallpapers/nixos-wallpaper.png"; + }; + + #security.sudo.extraConfig = '' + # ${userName} ALL = (root) NOPASSWD: /nix/var/nix/profiles/system/specialisation/catppuccin-mocha/bin/switch-to-configuration + # ${userName} ALL = (root) NOPASSWD: /nix/var/nix/profiles/system/specialisation/catppuccin-latte/bin/switch-to-configuration + # ${userName} ALL = (root) NOPASSWD: /nix/var/nix/profiles/system/specialisation/gruvbox-dark-hard/bin/switch-to-configuration + # ${userName} ALL = (root) NOPASSWD: /nix/var/nix/profiles/system/specialisation/heetch/bin/switch-to-configuration + #''; }