From 47dde98fd2cf11ea544bb1c8d8d548ee0bda0f54 Mon Sep 17 00:00:00 2001 From: Alexander Kassouni Date: Wed, 2 Sep 2026 21:20:10 -0700 Subject: some sway stuff --- flake.lock | 2 +- machines/common/default.nix | 2 +- machines/common/libinputcurve.nix | 105 +++++++++++++++++++++ users/kassouni/desktop/sway/default.nix | 8 ++ users/kassouni/desktop/sway/fuzzel.nix | 37 ++++++++ users/kassouni/desktop/sway/sway.nix | 42 +++++++++ users/kassouni/desktop/sway/waybar.nix | 141 ++++++++++++++++++++++++++++ users/kassouni/desktop/xmonad/default.nix | 7 ++ users/kassouni/desktop/xmonad/rofi.nix | 79 ++++++++++++++++ users/kassouni/desktop/xmonad/xbindkeys.nix | 32 +++++++ users/kassouni/desktop/xmonad/xmonad.nix | 9 ++ users/kassouni/ghostty.nix | 4 +- users/kassouni/home.nix | 15 +-- users/kassouni/neovim.nix | 20 +++- users/kassouni/palette/default.nix | 26 +---- users/kassouni/palette/fuzzel-palette.nix | 27 ++++++ users/kassouni/palette/rofi-palette.nix | 24 +++++ users/kassouni/rofi.nix | 79 ---------------- users/kassouni/theme.nix | 19 +++- users/kassouni/wayland/default.nix | 8 -- users/kassouni/wayland/fuzzel.nix | 23 ----- users/kassouni/wayland/sway.nix | 39 -------- users/kassouni/wayland/waybar.nix | 115 ----------------------- users/kassouni/xbindkeys.nix | 32 ------- 24 files changed, 559 insertions(+), 336 deletions(-) create mode 100644 machines/common/libinputcurve.nix create mode 100644 users/kassouni/desktop/sway/default.nix create mode 100644 users/kassouni/desktop/sway/fuzzel.nix create mode 100644 users/kassouni/desktop/sway/sway.nix create mode 100644 users/kassouni/desktop/sway/waybar.nix create mode 100644 users/kassouni/desktop/xmonad/default.nix create mode 100644 users/kassouni/desktop/xmonad/rofi.nix create mode 100644 users/kassouni/desktop/xmonad/xbindkeys.nix create mode 100644 users/kassouni/desktop/xmonad/xmonad.nix create mode 100644 users/kassouni/palette/fuzzel-palette.nix create mode 100644 users/kassouni/palette/rofi-palette.nix delete mode 100644 users/kassouni/rofi.nix delete mode 100644 users/kassouni/wayland/default.nix delete mode 100644 users/kassouni/wayland/fuzzel.nix delete mode 100644 users/kassouni/wayland/sway.nix delete mode 100644 users/kassouni/wayland/waybar.nix delete mode 100644 users/kassouni/xbindkeys.nix diff --git a/flake.lock b/flake.lock index bfda8af..cd48b89 100644 --- a/flake.lock +++ b/flake.lock @@ -332,7 +332,7 @@ "nixpkgs": "nixpkgs_4" }, "locked": { - "lastModified": 1788059985, + "lastModified": 1788239526, "narHash": "sha256-wNoWALnRqwCmDFct8ADoNtNsS5ihtobMy2WKVTeZw5A=", "path": "/home/kassouni/Developer/xmonad-config", "type": "path" diff --git a/machines/common/default.nix b/machines/common/default.nix index df8e60c..9af7dc0 100644 --- a/machines/common/default.nix +++ b/machines/common/default.nix @@ -2,11 +2,11 @@ { imports = [ ./base.nix + ./libinputcurve.nix ./pipewire.nix ./printer.nix ./rip.nix ./tailscale.nix ./user.nix - ./x11.nix ]; } diff --git a/machines/common/libinputcurve.nix b/machines/common/libinputcurve.nix new file mode 100644 index 0000000..8fefb9e --- /dev/null +++ b/machines/common/libinputcurve.nix @@ -0,0 +1,105 @@ +{ ... }: +# macOS trackpad pointer-acceleration curve, transplanted onto libinput. +# +# WHERE THE CURVE COMES FROM +# +# macOS accelerates one pointer report as (IOHIDFamily, IOHIDPointerAccelerator): +# +# velocity = floor(hypot(dx, dy)) * rate_multiplier +# mult = curve(velocity / (resolution / 67)) * (96/67) / velocity +# +# where curve() is a quartic up to TangentSpeedLinear, its tangent line up to +# TangentSpeedParabolicRoot, and the square root of a line beyond that. +# +# The coefficients below were recovered by fitting that form to ~2200 pointer +# reports captured from a real Apple trackpad (see ~/Downloads/code/rev), using +# the cursor's 1/256-point position deltas as ground truth rather than the +# integer CGEvent delta, which is rounded and cannot resolve better than +/-1: +# +# GainLinear 0.961648 TangentSpeedLinear 7.090 +# GainParabolic 0.829395 TangentSpeedParabolicRoot 17.978 +# GainCubic 0.122907 resolution 400 dpi @ 120 Hz +# GainQuartic 0 tracking index ~0.6875 +# +# The fit reproduces every measured velocity bin (v = 1..48 device units per +# report) to an rms of 2.4e-4 in gain. TangentSpeedParabolicRoot is the one +# value NOT measured -- the captures top out at 14.4 in/s, well below the +# roll-off -- so it is taken from the reported 32.2 in/s roll-off. It is +# corroborated: it puts the factor peak at 38.0 in/s against a reported ~40. +# +# TRANSLATION TO LIBINPUT +# +# libinput's custom profile has the same shape as Apple's algorithm -- both map +# an input speed to an output speed and use the ratio as the factor -- so the +# curve transplants directly. Only the units differ. libinput's x axis is +# *device units per millisecond* at the touchpad's own resolution (verified: +# tp_filter_motion passes x through raw, scaling only y for axis aspect), so +# the points below are specific to a 609.6 dpi (24 units/mm) touchpad. +# +# !! These numbers are tied to that resolution. magician's PIXA3854 is +# !! 609.6 dpi. A touchpad with a different resolution needs the table +# !! regenerated, or the curve will be wrong by the ratio of resolutions. +# !! Check with: libinput list-devices, or EVIOCGABS on the event node. +# !! To scope this to one machine instead, set services.libinput.touchpad.dev. +# +# WHAT DOES NOT TRANSFER +# +# Apple's floor() on velocity pins the gain flat below one device unit per +# report (< 0.30 in/s) and then steps it up 12% at exactly |d| = 1. libinput's +# f() is continuous and its points are uniformly spaced, so that discontinuity +# cannot be represented. What we get for free: f() runs linearly from the origin +# to the first knot, so the factor is constant across that first segment. The +# step below places the first knot at 0.3175 in/s -- essentially exactly Apple's +# 0.30 in/s plateau edge -- so the plateau's *width* is reproduced. Its value +# lands ~6% high, because continuity forbids the step. +# +# RANGE +# +# 64 points is libinput's hard maximum (libinput_config_accel_set_points rejects +# more), which forces a choice: uniform spacing means covering more speed costs +# low-speed resolution. 20 in/s is the sweet spot rather than a compromise -- +# past the last knot libinput extrapolates the final segment linearly, and +# Apple's curve is genuinely a straight line from 12.7 to 32.2 in/s, so the +# extrapolation is *exact* out to 30 in/s. Error only appears above the +# roll-off: +2.7% at 40 in/s, +16% at 60 in/s. Those are flick speeds where +# the pointer is being thrown across the screen anyway. +# +# Measured against the capture's own speed distribution: 0.22% weighted rms +# error, worst case 1.7% below 15 in/s. +let + # Overall speed trim. The curve's shape is independent of this; it scales + # cursor travel per finger inch uniformly. 1.0 maps one macOS point to one + # X11 pixel. Raise it if the pointer feels sluggish on a HiDPI screen that + # X is driving unscaled -- a macOS point is a *logical* unit, so an unscaled + # HiDPI X session covers less physical distance per point than macOS does. + gain = 1.0; + + # x = 0, step, 2*step, ... in touchpad device units per millisecond. + # step = 20 in/s / 63 intervals, at 609.6 dpi. + step = 0.193524; + + # f(x), the accelerated speed. libinput uses f(x)/x as the factor. + points = [ + 0.00000 0.03303 0.07349 0.12141 0.17680 0.23966 0.31000 0.38784 + 0.47319 0.56605 0.66644 0.77437 0.88985 1.01289 1.14350 1.28169 + 1.42748 1.58086 1.74186 1.91048 2.08674 2.27064 2.46220 2.66142 + 2.86832 3.08291 3.30520 3.53520 3.77291 4.01836 4.27155 4.53249 + 4.80119 5.07767 5.36193 5.65398 5.95384 6.26151 6.57702 6.90035 + 7.23154 7.56666 7.90177 8.23689 8.57201 8.90712 9.24224 9.57736 + 9.91247 10.24759 10.58270 10.91782 11.25294 11.58805 11.92317 12.25829 + 12.59340 12.92852 13.26364 13.59875 13.93387 14.26898 14.60410 14.93922 + ]; +in +{ + # services.libinput.enable defaults to services.xserver.enable, so this file + # is inert until X is turned on. + services.libinput.touchpad = { + accelProfile = "custom"; + accelStepMotion = step; + accelPointsMotion = map (p: p * gain) points; + + # Deliberately not setting accelSpeed: libinput ignores the speed setting + # entirely under the custom profile (custom_accelerator_set_speed only + # range-checks its argument and returns). The curve is the whole story. + }; +} diff --git a/users/kassouni/desktop/sway/default.nix b/users/kassouni/desktop/sway/default.nix new file mode 100644 index 0000000..947924a --- /dev/null +++ b/users/kassouni/desktop/sway/default.nix @@ -0,0 +1,8 @@ +{...}: { + imports = [ + ./waybar.nix + ./sway.nix + ./fuzzel.nix + ]; +} + diff --git a/users/kassouni/desktop/sway/fuzzel.nix b/users/kassouni/desktop/sway/fuzzel.nix new file mode 100644 index 0000000..d6d294b --- /dev/null +++ b/users/kassouni/desktop/sway/fuzzel.nix @@ -0,0 +1,37 @@ +{ + pkgs, + lib, + config, + ... +}: +let + inherit (config.colorScheme) ui editor common; + + # fuzzel wants RRGGBBAA with no leading '#'. + rgba = c: lib.toLower (lib.removePrefix "#" c) + "ff"; +in +{ + programs.fuzzel = { + enable = true; + settings = { + main = { + font = lib.mkForce "${config.theme.font.family}:size=${toString config.theme.font.size}"; + }; + + colors = { + background = rgba ui.bg; + text = rgba editor.fg; + match = rgba common.accent.tint; + selection = rgba ui.panel.bg; + selection-text = rgba editor.fg; + selection-match = rgba common.accent.tint; + border = rgba ui.line; + }; + + border = { + radius = 0; + }; + }; + }; + +} diff --git a/users/kassouni/desktop/sway/sway.nix b/users/kassouni/desktop/sway/sway.nix new file mode 100644 index 0000000..2e73d5f --- /dev/null +++ b/users/kassouni/desktop/sway/sway.nix @@ -0,0 +1,42 @@ +{ + pkgs, + config, + ... +}: +{ + programs.swaylock.enable = true; + wayland.windowManager.sway = { + enable = true; + wrapperFeatures.gtk = true; # Fixes common issues with GTK 3 apps + + config = { + window.titlebar = false; + menu = "fuzzel"; + modifier = "Mod4"; + # Use kitty as default terminal + terminal = "ghostty"; + }; + extraConfig = let + incr = "5"; + brightnessctl = "${pkgs.brightnessctl}/bin/brightnessctl"; + in '' + # home-manager inlines these values instead of emitting `set` lines + output * scale 1.75 + output * bg ${config.theme.desktop.wallpaper} fill + bindsym XF86MonBrightnessDown exec ${brightnessctl} set -e ${incr}%- + bindsym XF86MonBrightnessUp exec ${brightnessctl} set -e +${incr}% + bindsym XF86AudioRaiseVolume exec wpctl set-volume @DEFAULT_SINK@ ${incr}%+ + bindsym XF86AudioLowerVolume exec wpctl set-volume @DEFAULT_SINK@ ${incr}%- + input "type:keyboard" { + xkb_options caps:escape + } + + input "type:touchpad" { + natural_scroll enabled + dwt disabled + } + ''; + + }; +} + diff --git a/users/kassouni/desktop/sway/waybar.nix b/users/kassouni/desktop/sway/waybar.nix new file mode 100644 index 0000000..7cb37a0 --- /dev/null +++ b/users/kassouni/desktop/sway/waybar.nix @@ -0,0 +1,141 @@ +{ pkgs, inputs, config, ... }: +let + inherit (config.colorScheme) ui editor common; +in +{ + programs.waybar = { + enable = true; + settings = { + mainBar = + let + gap = 0; + in + { + layer = "top"; + position = "top"; + + margin-top = gap; + margin-left = gap; + margin-right = gap; + + modules-left = [ "sway/workspaces" ]; + modules-center = [ ]; + modules-right = [ + "cpu" + "wireplumber" + "battery" + "clock" + ]; + + clock = { + format = "{:%a %b %d %I:%M:%S}"; + interval = 1; + tooltip-format = "{calendar}"; + }; + + cpu = { + format = " {usage}%"; + }; + + battery = { + format = "{icon} {capacity}%"; + format-icons = { + default = [ + "󰂎" + "󰁺" + "󰁻" + "󰁼" + "󰁽" + "󰁾" + "󰁿" + "󰂀" + "󰂁" + "󰂂" + "󰁹" + ]; + + charging = [ + "󰢜" + "󰂆" + "󰂇" + "󰂈" + "󰢝" + "󰂉" + "󰢞" + "󰂊" + "󰂋" + "󰂅" + ]; + }; + + + states = { + low = 20; + }; + }; + + wireplumber = { + format = "󰋋 {volume}%"; + format-muted = "󰟎 {volume}%"; + on-click = "wpctl set-mute @DEFAULT_SINK@ toggle"; + max-volume = 150; + scroll-step = 1; + }; + + }; + }; + style = '' + * { + /* waybar's css font-size is px, but theme.font.size is points like + ghostty and fuzzel take. 1pt = 4/3px. */ + font-family: ${config.theme.font.family}; + font-size: ${builtins.toString (config.theme.font.size * 4 / 3)}px; + } + + window#waybar { + background: ${ui.bg}; + color: ${editor.fg}; + } + + .module { + padding: 0px 15px 0px; + } + + #workspaces button { + font-weight: normal; + color: ${ui.fg}; + background: none; + border: none; + } + + #workspaces button.focused { + color: ${common.accent.tint}; + } + + #workspaces button.urgent { + color: ${common.error}; + } + + #workspaces button:hover { + font-weight: bold; + color: ${editor.fg}; + box-shadow: none; + text-shadow: inherit; + background: none; + transition: none; + } + + /* matches the `states.low = 20` threshold on the battery module */ + #battery.low { + color: ${common.error}; + } + ''; + }; + + wayland.windowManager.sway.config.bars = [ + { + command = "waybar"; + } + ]; +} + diff --git a/users/kassouni/desktop/xmonad/default.nix b/users/kassouni/desktop/xmonad/default.nix new file mode 100644 index 0000000..d62dbe4 --- /dev/null +++ b/users/kassouni/desktop/xmonad/default.nix @@ -0,0 +1,7 @@ +{...}: { + imports = [ + ./xmonad.nix + ./rofi.nix + ./xbindkeys.nix + ]; +} diff --git a/users/kassouni/desktop/xmonad/rofi.nix b/users/kassouni/desktop/xmonad/rofi.nix new file mode 100644 index 0000000..740047b --- /dev/null +++ b/users/kassouni/desktop/xmonad/rofi.nix @@ -0,0 +1,79 @@ +{ config, ... }: + +let + inherit (config.lib.formats.rasi) mkLiteral; + colors = config.colorScheme; +in +{ + programs.rofi = { + enable = true; + font = "${config.theme.font.family} ${builtins.toString config.theme.font.size}"; + + theme = { + "*" = { + background-color = mkLiteral "transparent"; + text-color = mkLiteral colors.editor.fg; + }; + + window = { + background-color = mkLiteral colors.ui.popup.bg; + border = mkLiteral "2px"; + border-color = mkLiteral colors.ui.line; + border-radius = mkLiteral "8px"; + padding = mkLiteral "16px"; + width = mkLiteral "40%"; + }; + + inputbar = { + children = map mkLiteral [ "prompt" "entry" ]; + background-color = mkLiteral colors.ui.panel.bg; + border-radius = mkLiteral "4px"; + padding = mkLiteral "8px"; + spacing = mkLiteral "8px"; + }; + + prompt = { + text-color = mkLiteral colors.common.accent.tint; + }; + + entry = { + placeholder = "Search"; + placeholder-color = mkLiteral colors.ui.fg; + }; + + listview = { + lines = 8; + scrollbar = false; + spacing = mkLiteral "2px"; + margin = mkLiteral "12px 0px 0px 0px"; + }; + + element = { + padding = mkLiteral "8px"; + border-radius = mkLiteral "4px"; + spacing = mkLiteral "8px"; + }; + + "element normal urgent" = { + text-color = mkLiteral colors.common.error; + }; + + "element normal active" = { + text-color = mkLiteral colors.common.accent.tint; + }; + + "element selected normal" = { + background-color = mkLiteral colors.common.accent.tint; + text-color = mkLiteral colors.surface.base; + }; + + element-icon = { + size = mkLiteral "1em"; + }; + + element-text = { + text-color = mkLiteral "inherit"; + }; + }; + }; +} diff --git a/users/kassouni/desktop/xmonad/xbindkeys.nix b/users/kassouni/desktop/xmonad/xbindkeys.nix new file mode 100644 index 0000000..41ef7ed --- /dev/null +++ b/users/kassouni/desktop/xmonad/xbindkeys.nix @@ -0,0 +1,32 @@ +{ pkgs, ... }: +let + wpctl = "${pkgs.wireplumber}/bin/wpctl"; + + xbindkeysrc = pkgs.writeText "xbindkeysrc" '' + "${wpctl} set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+" + XF86AudioRaiseVolume + + "${wpctl} set-volume @DEFAULT_AUDIO_SINK@ 5%-" + XF86AudioLowerVolume + + "${wpctl} set-mute @DEFAULT_AUDIO_SINK@ toggle" + XF86AudioMute + ''; +in +{ + systemd.user.services.xbindkeys = { + Unit = { + Description = "xbindkeys"; + PartOf = [ "graphical-session.target" ]; + After = [ "graphical-session.target" ]; + }; + + Service = { + # -n keeps it in the foreground so systemd can supervise it + ExecStart = "${pkgs.xbindkeys}/bin/xbindkeys -n -f ${xbindkeysrc}"; + Restart = "on-failure"; + }; + + Install.WantedBy = [ "graphical-session.target" ]; + }; +} diff --git a/users/kassouni/desktop/xmonad/xmonad.nix b/users/kassouni/desktop/xmonad/xmonad.nix new file mode 100644 index 0000000..d49dcc1 --- /dev/null +++ b/users/kassouni/desktop/xmonad/xmonad.nix @@ -0,0 +1,9 @@ +{pkgs, config, ...}: { + xmonad-config = { + launcher = "rofi -show drun -show-icons"; + terminal = "ghostty"; + wallpaper = config.theme.desktop.wallpaper; + xmobar.font = config.theme.font; + }; +} + diff --git a/users/kassouni/ghostty.nix b/users/kassouni/ghostty.nix index 8c4aefd..cbfaee1 100644 --- a/users/kassouni/ghostty.nix +++ b/users/kassouni/ghostty.nix @@ -3,9 +3,9 @@ programs.ghostty = { enable = true; settings = { - theme = "Ayu Light"; + theme = "Ayu"; font-family = config.theme.font.family; - font-size = 24; + font-size = config.theme.font.size; window-decoration = "none"; }; }; diff --git a/users/kassouni/home.nix b/users/kassouni/home.nix index 9268980..84b69f1 100644 --- a/users/kassouni/home.nix +++ b/users/kassouni/home.nix @@ -8,11 +8,12 @@ imports = [ ./neovim.nix ./claude.nix - ./rofi.nix ./music.nix ./fonts.nix ./slippi.nix ./ssh.nix + ./desktop/sway + ./desktop/xmonad ./rip.nix ./ayu/ayu-dark.nix ./git.nix @@ -21,23 +22,11 @@ ./firefox.nix ./cursor.nix ./bash.nix - ./xbindkeys.nix ./scripts ./palette ]; home.username = "kassouni"; - - xmonad-config = { - launcher = "rofi -show drun -show-icons"; - terminal = "ghostty"; - wallpaper = pkgs.fetchurl { - url = "https://i.redd.it/p5flk9uzzxxz.jpg"; - sha256 = "1qcl6yh6l24sz3qprj3iyzinj3lyh3c426i6zwambwg816yg3bxs"; - }; - xmobar.font = config.theme.font; - }; - home.sessionVariables = { "TEST_VAR" = "asdf"; }; diff --git a/users/kassouni/neovim.nix b/users/kassouni/neovim.nix index ec90023..3aac114 100644 --- a/users/kassouni/neovim.nix +++ b/users/kassouni/neovim.nix @@ -9,6 +9,11 @@ enable = true; defaultEditor = true; + globals = { + mapleader = " "; + maplocalleader = " "; + }; + opts = { number = true; relativenumber = true; @@ -19,9 +24,22 @@ plugins = { # Existing plugins sleuth.enable = true; - telescope.enable = true; + telescope = { + enable = true; + keymaps = { + "ff" = { + action = "find_files"; + options.desc = "Telescope find files"; + }; + "fg" = { + action = "live_grep"; + options.desc = "Telescope live grep"; + }; + }; + }; highlight-colors.enable = true; web-devicons.enable = true; + which-key.enable = true; # LSP lsp = { diff --git a/users/kassouni/palette/default.nix b/users/kassouni/palette/default.nix index 403aa60..0600510 100644 --- a/users/kassouni/palette/default.nix +++ b/users/kassouni/palette/default.nix @@ -1,12 +1,4 @@ -{ - config, - lib, - pkgs, - ... -}: -let - cfg = config.kassouni.palette; -in +{ lib, ... }: { imports = [ ./bluetooth.nix @@ -14,6 +6,8 @@ in ./rebuild.nix ./screenshot.nix ./shutdown.nix + ./rofi-palette.nix + ./fuzzel-palette.nix ]; options.kassouni.palette.commands = lib.mkOption { @@ -37,18 +31,8 @@ in ); default = [ ]; description = '' - Entries shown in the rofi command palette. + Entries shown in the command palette. Rendered by both front ends: + `open-palette` (rofi, X11) and `open-palette-fuzzel` (fuzzel, Wayland). ''; }; - - config.home.packages = [ - (pkgs.writers.writeBashBin "open-palette" '' - set -eu - exes=(${lib.concatMapStringsSep " " (c: "${c.exe}") cfg.commands}) - idx=$(${pkgs.rofi}/bin/rofi -dmenu -i -format i -p palette <