diff --git a/backgrounds/cherry-blossom.jpg b/backgrounds/cherry-blossom.jpg new file mode 100644 index 00000000..a5147380 Binary files /dev/null and b/backgrounds/cherry-blossom.jpg differ diff --git a/backgrounds/eta-soffa.jpg b/backgrounds/eta-soffa.jpg new file mode 100644 index 00000000..ceb1a283 Binary files /dev/null and b/backgrounds/eta-soffa.jpg differ diff --git a/backgrounds/eta-tak.jpg b/backgrounds/eta-tak.jpg new file mode 100644 index 00000000..6d573e84 Binary files /dev/null and b/backgrounds/eta-tak.jpg differ diff --git a/backgrounds/stenpiren.jpg b/backgrounds/stenpiren.jpg new file mode 100644 index 00000000..ece6c9de Binary files /dev/null and b/backgrounds/stenpiren.jpg differ diff --git a/flake.nix b/flake.nix index 2bea903e..c3d1b1dd 100644 --- a/flake.nix +++ b/flake.nix @@ -39,9 +39,13 @@ url = "github:youwen5/signal-desktop-flake"; inputs.nixpkgs.follows = "nixpkgs"; }; + niri = { + url = "github:sodiboo/niri-flake"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { self, nixpkgs, nixpkgs-unstable, home-manager, nix-darwin, kak, unispect, ansi-utils, unambig-path, nixos-apple-silicon, signal-aarch64 }: + outputs = { self, nixpkgs, nixpkgs-unstable, home-manager, nix-darwin, kak, unispect, ansi-utils, unambig-path, nixos-apple-silicon, signal-aarch64, niri }: let mkPkgs = system: import nixpkgs { system = system; config.allowUnfree = true; }; mkPkgsUnstable = system: import nixpkgs-unstable { system = system; config.allowUnfree = true; }; @@ -177,7 +181,7 @@ use-iwd = false; }; graphical = mkNixOsGraphical { - inherit pkgs pkgs-unstable; + inherit pkgs pkgs-unstable niri; background = "pan-wire-3.png"; use-display-manager = true; }; @@ -215,6 +219,7 @@ inherit system; modules = [ nixos-apple-silicon.nixosModules.apple-silicon-support + niri.nixosModules.niri ./nixos/machines/foxhut/hardware.nix (import ./nixos/machines/foxhut/boot.nix { inherit pkgs pkgs-unstable asahi-firmware; }) (import ./nixos/machines/foxhut/machine.nix { inherit pkgs pkgs-unstable asahi-firmware signal-aarch64; }) diff --git a/home/graphical.nix b/home/graphical.nix index 1852caea..38b52c38 100644 --- a/home/graphical.nix +++ b/home/graphical.nix @@ -1,4 +1,5 @@ { pkgs }: +{ config, ... }: let alacritty = import ./alacritty/alacritty.nix pkgs; @@ -17,11 +18,42 @@ let ''; }; + + XWAYLAND_DISPLAY = ":3"; + + x-wayland-clipboard-daemon = pkgs.writeShellApplication { + name = "x-wayland-clipboard-daemon"; + runtimeInputs = [ pkgs.python313 pkgs.wl-clipboard pkgs.xclip ]; + text = '' + python3 ${../x-wayland-clipboard-daemon/x-wayland-clipboard-daemon.py} + ''; + }; in rec { home.packages = with pkgs; [ - fira-code ibm-plex + networkmanagerapplet # for tray icon + nerd-fonts.roboto-mono # specifically needed for waybar + noto-fonts fira-code ibm-plex + fontconfig + inkscape + xwayland-satellite + pwvucontrol + swaybg + libnotify + blueberry + + wl-clipboard xclip wev + + quodlibet + xfce.thunar + puddletag + rawtherapee + brightnessctl + obs-studio ] + ++ (with pkgs.kdePackages; [ + gwenview okular + ]) ++ (if pkgs.stdenv.isDarwin then [alloy6-mac] else [pkgs.alloy6]) ++ (if pkgs.stdenv.isDarwin then [] else [pkgs.keepassxc pkgs.vesktop pkgs.ares]) ; @@ -36,4 +68,406 @@ in rec { ]); extraConfig = builtins.readFile ../dotfiles/init.el; }; + + home.pointerCursor = + let miku-cursor = pkgs.stdenv.mkDerivation { + name = "miku-cursor"; + src = pkgs.fetchFromGitHub { + owner = "supermariofps"; + repo = "hatsune-miku-windows-linux-cursors"; + tag = "1.2.6"; + sha256 = "sha256-OQjjOc9VnxJ7tWNmpHIMzNWX6WsavAOkgPwK1XAMwtE="; + }; + buildPhase = '' + mkdir -p $out/share/icons + cp -r $src/miku-cursor-linux $out/share/icons/miku-cursor + ''; + }; + in { + size = 96; # size gets overwritten by niri + package = miku-cursor; + name = "miku-cursor"; + }; + + xdg.portal = { + enable = true; + extraPortals = [ + pkgs.xdg-desktop-portal-gnome + ]; + config.common.default = [ "gnome" ]; + }; + + programs.niri.settings = { + debug.render-drm-device = "/dev/dri/renderD128"; + input.keyboard = { + xkb = { + layout = "fox"; + options = "caps:escape"; + }; + repeat-delay = 160; + repeat-rate = 30; + }; + input.touchpad = { + tap = false; + click-method = "button-areas"; + + natural-scroll = false; + accel-speed = -0.1; + scroll-factor = 0.5; + }; + input.mouse = { + accel-speed = -0.6; + }; + outputs."eDP-1" = { + scale = 1.0; + }; + binds = with config.lib.niri.actions; { + # common programs + "Mod+Shift+T" = { hotkey-overlay.title = "run alacritty"; action = spawn "alacritty"; }; + "Mod+Shift+F" = { hotkey-overlay.title = "run thunar"; action = spawn "thunar"; }; + "Mod+Shift+I" = { hotkey-overlay.title = "run firefox"; action = spawn "firefox"; }; + "Mod+Shift+K" = { hotkey-overlay.title = "run keepass"; action = spawn "keepassxc"; }; + + "Mod+Space" = { hotkey-overlay.title = "rofi launcher"; action = spawn "rofi" "-modes" "drun" "-show" "drun"; }; + "Mod+E" = { hotkey-overlay.title = "niri msg"; action = spawn "sh" "${./niri-action.sh}"; }; + "Mod+Tab" = { hotkey-overlay.title = "focus last window"; action = focus-window-previous; }; + + "Mod+Comma" = { hotkey-overlay.title = "show these hotkeys"; action = show-hotkey-overlay; }; + "Mod+Escape" = { hotkey-overlay.title = "quit niri"; action = quit; }; + "Mod+Q" = { hotkey-overlay.title = "close window"; action = close-window; }; + "Mod+F" = { hotkey-overlay.title = "switch width"; action = switch-preset-column-width; }; + "Mod+T" = { hotkey-overlay.title = "switch to tabbed view"; action = toggle-column-tabbed-display; }; + "Mod+Down" = { hotkey-overlay.title = "next tab"; action = focus-window-down; }; + "Mod+Up" = { hotkey-overlay.title = "previous tab"; action = focus-window-up; }; + "Mod+Shift+3" = { hotkey-overlay.title = "screenshot"; action = screenshot-screen { write-to-disk = false; }; }; + "Mod+Shift+4" = { hotkey-overlay.title = "screenshot region"; action = screenshot; }; + "Mod+Shift+5" = { hotkey-overlay.title = "screenshot window"; action = screenshot-window { write-to-disk = false; }; }; + + "Mod+TouchpadScrollRight" = { hotkey-overlay.title = "expand window"; action = set-window-width "+10"; }; + "Mod+TouchpadScrollLeft" = { hotkey-overlay.title = "shrink window"; action = set-window-width "-10"; }; + "Mod+TouchpadScrollUp" = { hotkey-overlay.title = "expand window"; action = set-window-height "+10"; }; + "Mod+TouchpadScrollDown" = { hotkey-overlay.title = "shrink window"; action = set-window-height "-10"; }; + + "MouseForward" = { hotkey-overlay.title = "next column"; action = focus-column-right; }; + "MouseBack" = { hotkey-overlay.title = "previous column"; action = focus-column-left; }; + "Mod+MouseForward" = { hotkey-overlay.title = "next workspace"; action = focus-workspace-down; }; + "Mod+MouseBack" = { hotkey-overlay.title = "previous workspace"; action = focus-workspace-up; }; + }; + switch-events = with config.lib.niri.actions; { + "lid-close" = { action = spawn "sh" "-c" "niri msg action do-screen-transition && swaylock"; }; + }; + + spawn-at-startup = [ + { command = [ "${pkgs.xwayland-satellite}/bin/xwayland-satellite" XWAYLAND_DISPLAY ]; } + { command = [ "${pkgs.swaybg}/bin/swaybg" "--image" "${../backgrounds/eta-tak.jpg}" ]; } + { command = [ "${x-wayland-clipboard-daemon}" ]; } + { command = [ "${pkgs.dbus}/bin/dbus-update-activation-environment" "--systemd" "WAYLAND_DISPLAY" "XDG_CURRENT_DESKTOP" ]; } + ]; + environment.DISPLAY = XWAYLAND_DISPLAY; + + window-rules = [ + { + draw-border-with-background = false; + geometry-corner-radius = + let rad = 15.0; + in { bottom-left = rad; bottom-right = rad; top-right = rad; top-left = rad; }; + clip-to-geometry = true; + } + { + excludes = [ + { app-id = "^org.kde.gwenview$"; } + { app-id = ''^Gimp-\d+\.\d+$''; } + { title = '' - YouTube — Mozilla Firefox$''; } + { app-id = ''^blender$''; } + { app-id = ''^rawtherapee$''; } + ]; + opacity = 0.95; + } + { + matches = [ { app-id = "om.saivert.pwvucontrol"; } ]; + open-floating = true; + default-window-height.proportion = 0.2; + } + { + matches = [ { app-id = "blueberry.py"; } ]; + open-floating = true; + } + { # kicad pop-ups + matches = [ + { title = ''^Choose (Footprint|Symbol|Power Symbol) ''; } # add menus + { title = ''^Edit \w+ Field$''; } + { title = ''^(\w|\s)+ Properties$''; } + ]; + open-floating = true; + default-window-height.fixed = 400; + } + { # gimp pop-ups + matches = [ + { app-id = ''^Gimp-\d+\.\d+$''; } + ]; + excludes = [ # exclude the main window + { title = ''– GIMP$''; } + { title = ''^GNU Image Manipulation Program$''; } + ]; + open-floating = true; + default-window-height.fixed = 400; + } + ]; + + cursor.size = 48; + + prefer-no-csd = true; + + layout = { + empty-workspace-above-first = true; + + preset-column-widths = [ + { proportion = 2.0 / 3.0; } + { proportion = 1.0 / 3.0; } + { proportion = 1.0; } + ]; + tab-indicator = { + place-within-column = true; + width = 8; + gap = 10; + corner-radius = 5; + gaps-between-tabs = 5; + length.total-proportion = 1.0; + active.gradient = { + from = "red"; + to = "yellow"; + angle = 0; + "in'" = "oklch longer hue"; + relative-to = "workspace-view"; + }; + inactive.gradient = { + from = "#222"; + to = "#666"; + angle = 0; + "in'" = "oklch longer hue"; + relative-to = "workspace-view"; + }; + }; + focus-ring = { + enable = true; + width = 5; + active.gradient = { + # from = "#a9fff5"; + # to = "#ffb185"; + from = "#83c6be"; + to = "#a87458"; + angle = 30; + "in'" = "oklch longer hue"; + relative-to = "workspace-view"; + }; + }; + shadow = { + enable = true; + color = "#00000070"; + }; + }; + }; + + programs.waybar = { + enable = true; + systemd = { + enable = true; + }; + + settings.mainBar = { + height = 40; + spacing = 20; + + layer = "top"; + modules-left = [ + "tray" + ]; + modules-center = [ + "niri/window" + ]; + modules-right = [ + "pulseaudio" "pulseaudio/slider" + "network" + "bluetooth" + "power-profiles-daemon" + "temperature" + "battery" + "clock" + ]; + "niri/window" = { + icon = true; + icon-size = 24; + rewrite = { + "(.*) (:?— Mozilla (Firefox|Thunderbird)|- Quod Libet)" = "$1"; # remove some titles + "• Discord \\| ([^|]*) \\| (.*)" = "$2 ⟩ $1"; # discord formats things as Discord | Channel | Server + "• Discord \\| ([^|]*)" = "$1"; # sometimes there's no server to show + # pinged versions + "\\((\\d+)\\) Discord \\| ([^|]*) \\| (.*)" = "$3 ⟩ $2 ($1)"; + "\\((\\d+)\\) Discord \\| ([^|]*)" = "$2 ($1)"; + }; + }; + + pulseaudio = { + format = "{icon}"; + format-alt = "{volume} {icon}"; + format-alt-click = "click-right"; + format-muted = "🔇"; + format-icons = { + default = ["" "" ""]; + }; + scroll-step = 10; + on-click = "pwvucontrol"; + tooltip = false; + }; + network = { + format = "{icon}"; + format-alt = "{ipaddr}/{cidr} {icon}"; + format-alt-click = "click-right"; + format-icons = { + wifi = [" "]; + ethernet = ["󰛳"]; + }; + on-click = "alacritty -e nmtui"; + tooltip = false; + }; + bluetooth = { + on-click = "blueberry"; + format = ""; + }; + clock = { + format = "{:%a %d %b %H:%M}"; + tooltip = false; + }; + battery = { + format = "{capacity}% {icon}"; + format-alt = "{time} {icon}"; + format-icons = ["" "" "" "" ""]; + format-charging = "{capacity}% 󰂅"; + interval = 30; + states = { + warning = 25; + critical = 1; + }; + tooltip = false; + }; + tray = { + icon-size = 24; + spacing = 10; + }; + }; + style = '' + window { + color: rgba(35, 31, 32, 1); + background: rgba(197, 196, 196, 0.5); + } + + .modules-left { + padding-left: 20px; + } + .modules-right { + padding-right: 20px; + } + + #pulseaudio-slider { + min-width: 100px; + } + + /* don't show the grabbable thing in the slider */ + #pulseaudio-slider slider { + min-height: 0px; + min-width: 0px; + opacity: 0; + background-image: none; + border: none; + box-shadow: none; + } + #pulseaudio-slider trough { + min-height: 15px; + min-width: 10px; + border-radius: 5px; + background-color: rgba(35, 31, 32, 1); + } + #pulseaudio-slider highlight { + min-width: 10px; + border-radius: 5px; + background-color: rgba(247, 246, 246, 1); + } + + * { + border: none; + border-radius: 0; + font-family: Roboto Mono; + font-size: 20px; + box-shadow: none; + text-shadow: none; + transition-duration: 0s; + } + + /* battery icon gets cut off otherwise for some reason */ + #battery { + padding-right: 10px; + } + + #battery.warning { + color: rgba(255, 210, 4, 1); + } + + #battery.critical { + color: rgba(238, 46, 36, 1); + } + ''; + }; + + programs.rofi = { + enable = true; + package = pkgs.rofi-wayland; + theme = "${./rofi-theme.rasi}"; + extraConfig = { + matching = "fuzzy"; + }; + }; + + services.mako = { + enable = true; + backgroundColor = "#23201fda"; + borderColor = "#0f0c0aff"; + borderRadius = 10; + borderSize = 2; + margin = "30"; + font = "Manrope 16"; + width = 600; + }; + + programs.swaylock = { + enable = true; + package = pkgs.swaylock-effects; + settings = { + # screenshot = true; + image = "${../backgrounds/eta-tak.jpg}"; + effect-blur = "20x3"; + effect-vignette = "0.4:0.2"; + fade-in = 0.5; + + indicator = true; + indicator-radius = 400; + indicator-image = "${../images/profile-picture.png}"; + ring-color = "22222280"; + key-hl-color = "ee44ee80"; + line-color = "00000020"; + inside-color = "00000000"; + }; + }; + + # TODO: make this work + # systemd.user.services.x-wayland-clipboard-daemon = + # { + # Unit = { + # After = "graphical-session.target"; + # }; + # Service = { + # ExecStart = "${x-wayland-clipboard-daemon}/bin/x-wayland-clipboard-daemon"; + # Restart = "on-failure"; + # Environment = [ "DISPLAY=${XWAYLAND_DISPLAY}" ]; + # }; + # }; } diff --git a/home/niri-action.sh b/home/niri-action.sh new file mode 100644 index 00000000..bf1a2d2c --- /dev/null +++ b/home/niri-action.sh @@ -0,0 +1,133 @@ +#!/bin/sh + +# from niri msg action help +NIRI_ACTIONS=$(cat < : "🐱 " + <2> : "🐈" +

: "🥺 " +

: "🥺" +

: "🥺" + : "😳 " + : "😳" + : "🦀" + : "🦎" + : "ඩ" + : "⚥" + <1> : "⚧" + <2> : "☄" + <3> : "☿" + : "·" # same as multi-cdot + : "∙" + : "ꙮ" +

: "🥺👉👈" +

: "👉👈" + : "⁻¹" + +# Supersripts and subscripts + <0> : "⁰" + <1> : "¹" + <2> : "²" + <3> : "³" + <4> : "⁴" + <5> : "⁵" + <6> : "⁶" + <7> : "⁷" + <8> : "⁸" + <9> : "⁹" + : "ᵃ" + : "ᵇ" + : "ᶜ" + : "ᵈ" + : "ᵉ" + : "ᶠ" + : "ᵍ" + : "ʰ" + : "ⁱ" + : "ʲ" + : "ᵏ" + : "ˡ" + : "ᵐ" + : "ⁿ" + : "ᵒ" +

: "ᵖ" + : "ʳ" + : "ˢ" + : "ᵗ" + : "ᵘ" + : "ᵛ" + : "ʷ" + : "ˣ" + : "ʸ" + : "ᶻ" + : "⁺" + : "⁻" + + <0> : "₀" + <1> : "₁" + <2> : "₂" + <3> : "₃" + <4> : "₄" + <5> : "₅" + <6> : "₆" + <7> : "₇" + <8> : "₈" + <9> : "₉" + : "ₐ" + : "ₑ" + : "ₕ" + : "ᵢ" + : "ⱼ" + : "ₖ" + : "ₗ" + : "ₘ" + : "ₙ" + : "ₒ" +

: "ₚ" + : "ᵣ" + : "ₛ" + : "ₜ" + : "ᵤ" + : "ᵥ" + : "ₓ" + : "₊" + : "₋" + : "₌" + : "₍" + : "₎" + +# Written prose fancy characters + : "…" # latex \ldots + : "…" # latex \ldots + : "–" # en dash + : "—" # em dash + : "✓" + : "♪" + : "Ⓥ" + : "Ⓐ" + : " ¯\\_(ツ)_/ " + + + +# Arrows + : "→ " # latex \to +

: "↦" # latex \mapsto +

: "⊸" # latex \multimap + : "←" # latex \gets +

: "⇒" # latex \Rightarrow +

: "⇐" # latex \Leftarrow +

: "⇔" # latex \Leftrightarrow +

: "↑ " # latex \uparrow + : "↓" # latex \downarrow + : "↓" # latex \downarrow + : "↝" # latex \leadsto + : "⇄" # latex \leadsto + +# Mathy stuffs + : "∇" + : "⨯" + : "⃗" # combining vector above last symbol + : "∂" + : "∀" # latex \forall + : "∃" # latex \exists +

: "⊤" # latex \top + : "⊥" # latex \bot + : "★" # latex \star + : "∈ " # latex \in + : "∋" # latex \ni + : "∉" #latex \notin +

: "∅" # latex \empty +

: "∏" # latex \prod + : "∑" # latex \sum + : "≤ " # latex \le + : "≥" # latex \ge +

: "≺" # latex \prec +

: "⪯" # latex \preceq +

: "±" # latex \pm + : "⧺" # latex ? + : "⊂" # latex \subset + : "⊆" # latex \subseteq +

: "⊃" # latex \supset +

: "⊇" # latex \supseteq + : "∖" # latex \setminus +

: "∩" # latex \cap +

: "∪" # latex \cup +

: "⊔" # latex \sqcup +

: "⊓" # latex \sqcap +

: "⊎" # latex \uplus +

: "⨄" # latex \Uplus + :"⊳" # latex \rhd + :"⊲" # latex \lhd + : "∴" # latex \therefore + : "∎" # latex \qed + : "ℕ" # latex \N + : "𝔹" # latex \N + : "ℤ" # latex \Z + : "ℂ" # latex \C + : "ℚ" # latex \Q + : "ℝ" # latex \R + : "𝔼" # latex \E + : "𝔽" # latex \F + : "∞" # latex \infty + : "≡" # latex \equiv + : "≢" # latex \not\equiv + : "≃" # latex \simeq +

: "≈" # latex \approx + : "≅" # latex \cong + : "≠ " # latex \ne + : "≟" # latex \? +

: "⊢" # latex \vdash +

: "⊬" # latex \nvdash + : "⊨" # latex \vDash + : "⊩" # latex \Vdash +

: "⊥" # latex \perp + : "∨" # latex \vee + : "∧" # latex \wedge + : "¬" # latex \not + : "⋅" # latex \cdot + : "﹕ " # latex \? + : "•" # latex \bullet + : "∘" # latex \circ + : "×" # latex \times +#

: "⊕" # latex \oplus +# : "⊛" # latex \circledast + : "⊕" # latex \oplus + : "⊗" # latex \otimes + : "⊛" # latex \circledast + : "⟨" # latex \langle + : "⟩" # latex \rangle + : "⟦" # latex \llbracket + : "⟧" # latex \rrbracket + : "⌊" # latex ? + : "⌋" # latex ? + : "⌈" # latex ? + : "⌉" # latex ? + : "∷" # latex ? + : "◊" # latex \lozenge + : "°" + : "⹁" # latex ? + : "⁏" # latex ? + : "□" # latex ? + : "♭" # latex ? +

: "♯" # latex ? + +# Greek alphabet +

: "α" # latex \alpha + : "β" # latex \beta + : "γ" # latex \gamma + : "δ" # latex \delta +

: "ε" # latex \epsilon + : "ζ" # latex \zeta + : "η" # latex \eta + : "θ" # latex \theta + : "ι" # latex \iota +

: "κ" # latex \kappa + : "λ" # latex \lambda + : "ƛ" # latex \lambda + : "μ " # latex \mu + : "μ" # latex \mu + : "ν" # latex \nu + : "ξ" # latex \xi + : "ο" # latex \omicron +

: "π" # latex \pi + : "ρ" # latex \rho + : "ς" # latex \stigma + : "σ" # latex \sigma + : "τ" # latex \tau +

: "υ" # latex \upsilon +

: "ϕ" # latex \phi +

: "φ" # latex \varphi + : "χ" # latex \chi +

: "ψ" # latex \psi + : "ω" # latex \omega +

: "Α" # latex \Alpha + : "Β" # latex \Beta + : "Γ" # latex \Gamma + : "Δ" # latex \Delta +

: "Ε" # latex \Epsilon + : "Ζ" # latex \Zeta + : "Η" # latex \Eta + : "Θ" # latex \Theta + : "Ι" # latex \Iota +

: "Κ" # latex \Kappa + : "Λ" # latex \Lambda + : "Μ" # latex \Mu + : "Ν" # latex \Nu + : "Ξ" # latex \Xi + : "Ο" # latex \Omicron +

: "Π" # latex \Pi + : "Ρ" # latex \Rho + : "Σ" # latex \Sigma + : "Τ" # latex \Tau +

: "Υ" # latex \Upsilon +

: "Φ" # latex \Phi + : "Χ" # latex \Chi +

: "Ψ" # latex \Psi + : "Ω" # latex \Omega + +# Caligraphy + : "𝓐" + : "𝓑" + : "𝓒" + : "𝓓" + : "𝓔" + : "𝓕" + : "𝓖" + : "𝓗" + : "𝓘" + : "𝓙" + : "𝓚" + : "𝓛" + : "𝓜" + : "𝓝" + : "𝓞" +

: "𝓟" + : "𝓠" + : "𝓡" + : "𝓢" + : "𝓣" + : "𝓤" + : "𝓥" + : "𝓦" + : "𝓧" + : "𝓨" + : "𝓩" + <0> : "𝟎" + <1> : "𝟏" + <2> : "𝟐" + <3> : "𝟑" + <4> : "𝟒" + <5> : "𝟓" + <6> : "𝟔" + <7> : "𝟕" + <8> : "𝟖" + <9> : "𝟗" + +# Fraktur + : "𝖆" + : "𝖇" + : "𝖈" + : "𝖉" + : "𝖊" + : "𝖋" + : "𝖌" + : "𝖍" + : "𝖎" + : "𝖏" + : "𝖐" + : "𝖑" + : "𝖒" + : "𝖓" + : "𝖔" +

: "𝖕" + : "𝖖" + : "𝖗" + : "𝖘" + : "𝖙" + : "𝖚" + : "𝖛" + : "𝖜" + : "𝖝" + : "𝖞" + : "𝖟" + : "𝕬" + : "𝕭" + : "𝕮" + : "𝕯" + : "𝕰" + : "𝕱" + : "𝕲" + : "𝕳" + : "𝕴" + : "𝕵" + : "𝕶" + : "𝕷" + : "𝕸" + : "𝕹" + : "𝕺" +

: "𝕻" + : "𝕼" + : "𝕽" + : "𝕾" + : "𝕿" + : "𝖀" + : "𝖁" + : "𝖂" + : "𝖃" + : "𝖄" + : "𝖅" diff --git a/nixos/kblayouts/fox-symbols.xkb b/nixos/kblayouts/fox-symbols.xkb index 1d349348..896469f1 100644 --- a/nixos/kblayouts/fox-symbols.xkb +++ b/nixos/kblayouts/fox-symbols.xkb @@ -1,6 +1,6 @@ default xkb_symbols "fox" { - key {[ comma, semicolon, VoidSymbol, VoidSymbol ]}; + key {[ Multi_key ]}; key {[ 1, ampersand, VoidSymbol, VoidSymbol ]}; key {[ 2, quotedbl, at, VoidSymbol ]}; key {[ 3, numbersign, VoidSymbol, VoidSymbol ]}; diff --git a/nixos/networking.nix b/nixos/networking.nix index 269e27d8..6b9d8abc 100644 --- a/nixos/networking.nix +++ b/nixos/networking.nix @@ -11,4 +11,6 @@ networking.nameservers = [ "8.8.8.8" ]; networking.resolvconf.enable = true; networking.resolvconf.dnsExtensionMechanism = false; # edns seems to be fucky with this enabled + + programs.nm-applet.enable = true; } diff --git a/nixos/tiny-dfr.nix b/nixos/tiny-dfr.nix index 3df598dd..78934853 100644 --- a/nixos/tiny-dfr.nix +++ b/nixos/tiny-dfr.nix @@ -66,7 +66,7 @@ in MediaLayerKeys = [ { Icon = "prev-workspace"; Action = "Finance"; } # bind me in the window manager! - { Icon = "next-workspace"; Action = "Sport"; } # bind me in the window manager! + { Icon = "next-workspace"; Action = "Sport"; } # bind me in the window manager! { Icon = "brightness_low"; Action = "BrightnessDown"; } { Icon = "brightness_high"; Action = "BrightnessUp"; } { Icon = "backlight_low"; Action = "IllumDown"; } diff --git a/x-wayland-clipboard-daemon/x-wayland-clipboard-daemon.py b/x-wayland-clipboard-daemon/x-wayland-clipboard-daemon.py new file mode 100644 index 00000000..a01edac9 --- /dev/null +++ b/x-wayland-clipboard-daemon/x-wayland-clipboard-daemon.py @@ -0,0 +1,137 @@ +# very simple daemon that monitors the clipboard for wayland (using wl-clipboard) and X (using xclip) +# whenever one changes, clipboard-daemon ensures the other changes as well +# +# currently supports syncing text/plain and image/png mimetypes between clipboards +# +# needs wl-paste and xclip in $PATH + +from typing import Optional +import subprocess +from abc import ABC +from dataclasses import dataclass +import time + +INTERVAL_MS = 100 + +class ClipboardContents(ABC): + pass + +@dataclass +class TextContents(ClipboardContents): + text: bytes + + def __repr__(self): + if len(self.text) < 20: + return f"TextContents(text={self.text!r})" + else: + return f"TextContents(text={self.text[:20]!r}..)" + + def __str__(self): + return repr(self) + +@dataclass +class ImageContents(ClipboardContents): + png_data: bytes + + def __repr__(self): + return "ImageContents(png_data=...)" + + def __str__(self): + return repr(self) + + +def get_wayland_clipboard() -> Optional[ClipboardContents]: + formats = subprocess.run(["wl-paste", "-l"], capture_output=True).stdout.strip(b"\n") + + if b"image/png" in formats: + contents = subprocess.run(["wl-paste", "-t", "image/png"], capture_output=True).stdout + return ImageContents(png_data=contents) + + if b"text/plain" in formats: + contents = subprocess.run(["wl-paste", "-n", "-t", "text/plain"], capture_output=True).stdout + return TextContents(text=contents) + + return None + +def set_wayland_clipboard(data: ClipboardContents): + if isinstance(data, TextContents): + subprocess.run( + ["wl-copy", "-t", "text/plain", "-n"], + input=data.text, + ) + elif isinstance(data, ImageContents): + subprocess.run( + ["wl-copy", "-t", "image/png"], + input=data.png_data, + ) + else: + raise ValueError(f"Unknown clipboard data: {data}") + +def get_x_clipboard() -> Optional[ClipboardContents]: + formats = subprocess.run( + ["xclip", "-out", "-selection", "clipboard", "-target", "TARGETS"], + capture_output=True, + ).stdout.strip(b"\n") + + if b"image/png" in formats: + contents = subprocess.run( + ["xclip", "-out", "-selection", "clipboard", "-target", "image/png"], + capture_output=True, + ).stdout + return ImageContents(png_data=contents) + + if b"text/plain" in formats: + contents = subprocess.run( + ["xclip", "-out", "-selection", "clipboard", "-target", "text/plain"], + capture_output=True, + ).stdout + return TextContents(text=contents) + + return None + +def set_x_clipboard(data: ClipboardContents): + if isinstance(data, TextContents): + subprocess.run( + ["xclip", "-in", "-selection", "clipboard", "-target", "text/plain"], + input=data.text, + ) + elif isinstance(data, ImageContents): + subprocess.run( + ["xclip", "-in", "-selection", "clipboard", "-target", "image/png"], + input=data.png_data, + ) + else: + raise ValueError(f"Unknown clipboard data: {data}") + +def clipboard_sync(): + last_wayland_contents = get_wayland_clipboard() + last_x_contents = get_x_clipboard() + + while True: + time.sleep(INTERVAL_MS/1000) + + wayland_contents = get_wayland_clipboard() + x_contents = get_x_clipboard() + + if wayland_contents != last_wayland_contents: + print(f"wayland clipborad updated from {last_wayland_contents} to {wayland_contents}") + if wayland_contents is not None: + print(" sycning wayland -> x") + set_x_clipboard(wayland_contents) + x_contents = wayland_contents + else: + print(" unknown type, not syncing") + elif x_contents != last_x_contents: + print(f"x clipborad updated from {last_x_contents} to {x_contents}") + if x_contents is not None: + print(" sycning x -> wayland") + set_wayland_clipboard(x_contents) + wayland_contents_contents = x_contents + else: + print(" unknown type, not syncing") + + last_wayland_contents = wayland_contents + last_x_contents = x_contents + +print("starting x-wayland-clipboard-daemon") +clipboard_sync()