diff options
Diffstat (limited to 'users/tazjin')
45 files changed, 2030 insertions, 136 deletions
diff --git a/users/tazjin/cursed/default.nix b/users/tazjin/cursed/default.nix new file mode 100644 index 000000000000..336a746db79d --- /dev/null +++ b/users/tazjin/cursed/default.nix @@ -0,0 +1,9 @@ +{ depot, ... }: + +let + inherit (depot.web) bubblegum; +in +(bubblegum.writeCGI + { + name = "cursed"; + } ./responder.nix) // { meta.ci.skip = true; } diff --git a/users/tazjin/cursed/responder.nix b/users/tazjin/cursed/responder.nix new file mode 100644 index 000000000000..9aa6a2d55807 --- /dev/null +++ b/users/tazjin/cursed/responder.nix @@ -0,0 +1,76 @@ +{ depot, ... }: + +let + inherit (depot.users.sterni.nix.html) + __findFile + esc + withDoctype + ; + + # CGI envvars: https://www.instanet.com/cgi/env.html + method = builtins.getEnv "REQUEST_METHOD"; + path = builtins.getEnv "PATH_INFO"; + + rawQuery = builtins.getEnv "QUERY_STRING"; + query = with builtins; let + pairs = (filter (s: isString s && s != "") (split "&" rawQuery)); + tuples = filter (l: length l > 0) (map (p: filter (s: isString s) (split "=" p)) pairs); + mkAttr = t: { + name = elemAt t 0; + value = elemAt t 1; + }; + in + listToAttrs (map mkAttr tuples); + + default = let { + hasQuery = if builtins.length (builtins.attrNames query) > 0 then "?" else ""; + body = (withDoctype (<html> { lang = "en"; } [ + (<head> { } [ + (<title> { } "some cursed nix") + ]) + (<body> { } [ + (<p> { } "hello volgasprint") + (<p> { } [ method " " path hasQuery rawQuery ]) + (<p> { } (builtins.toJSON query)) + ]) + ])); + }; + + greeter = withDoctype (<html> { lang = "en"; } [ + (<head> { } [ + (<title> { } "hello there") + ]) + (<body> { } [ + (<p> { } "hello ${query.name or "unknown"}") + ]) + ]); + + weather = let { + town = query.town or "Kazan"; + w = builtins.fetchurl "https://wttr.in/${town}?"; + rendered = with depot.third_party.nixpkgs; runCommand "weather-${town}" { } '' + cat ${w} | ${ansi2html}/bin/ansi2html > $out + ''; + + body = builtins.readFile "${rendered}"; + }; + + routes = { + "/other" = (withDoctype (<html> { lang = "en"; } [ + (<head> { } [ + (<title> { } "other endpoint") + ]) + (<body> { } [ + (<p> { } "this is another route") + ]) + ])); + "/greeter" = greeter; + "/weather" = weather; + }."${path}" or default; + +in +depot.web.bubblegum.respond "OK" +{ + "Content-Type" = "text/html"; +} + routes diff --git a/users/tazjin/dotfiles/.skip-subtree b/users/tazjin/dotfiles/.skip-subtree new file mode 100644 index 000000000000..954981f436ee --- /dev/null +++ b/users/tazjin/dotfiles/.skip-subtree @@ -0,0 +1 @@ +Stuff below here is managed manually, without readTree. diff --git a/users/tazjin/dotfiles/default.nix b/users/tazjin/dotfiles/default.nix index 9b783a9c857c..6291a10303e7 100644 --- a/users/tazjin/dotfiles/default.nix +++ b/users/tazjin/dotfiles/default.nix @@ -1,3 +1,23 @@ -_: { +{ depot, pkgs, ... }@args: + +rec { dunstrc = ./dunstrc; + niri = ./niri.config.kdl; + fuzzel = ./fuzzel.ini; + waybar = { + config = import ./waybar/config.nix args; + style = pkgs.runCommandNoCC "waybar-style.css" + { + CHICAGO95 = depot.third_party.chicago95; + } '' + cat ${./waybar/style.css} | ${pkgs.envsubst}/bin/envsubst > $out + ''; + }; + + # Helper derivation for iterating on waybar config. + waybarTest = pkgs.runCommandNoCC "waybar-conf" { } '' + mkdir -p $out + cat ${pkgs.writeText "waybar-conf.json" (builtins.toJSON(builtins.attrValues waybar.config))} > $out/config + cp ${waybar.style} $out/style.css + ''; } diff --git a/users/tazjin/dotfiles/dunstrc b/users/tazjin/dotfiles/dunstrc index 2aa1141b6ec2..d984ff94ecaa 100644 --- a/users/tazjin/dotfiles/dunstrc +++ b/users/tazjin/dotfiles/dunstrc @@ -1,54 +1,15 @@ [global] -font = Iosevka Term 11 -origin = top-left -markup = yes -plain_text = no -format = "<b>%s</b>\n%b" -sort = no -indicate_hidden = yes -alignment = center -bounce_freq = 0 -show_age_threshold = -1 -word_wrap = yes -ignore_newline = no -stack_duplicates = yes -hide_duplicate_count = yes -geometry = "300x50-15+49" -shrink = no -transparency = 5 -idle_threshold = 0 -monitor = 0 +origin = bottom-right +offset = 5x5 # takes into account menu bar! +corner_radius = 5 +frame_width = 1 +frame_color = "#000000" +foreground = "#000000" +background = "#ffffe1" +font = Arial 12 follow = keyboard -sticky_history = yes -history_length = 15 -show_indicators = no -line_height = 3 -separator_height = 2 -padding = 6 -horizontal_padding = 6 -separator_color = frame -startup_notification = false -dmenu = /usr/bin/dmenu -p dunst: -browser = /usr/bin/firefox -new-tab -icon_position = off -max_icon_size = 80 -frame_width = 3 -frame_color = "#8EC07C" - -[urgency_low] -frame_color = "#3B7C87" -foreground = "#3B7C87" -background = "#191311" -timeout = 4 - -[urgency_normal] -frame_color = "#5B8234" -foreground = "#5B8234" -background = "#191311" -timeout = 6 - -[urgency_critical] -frame_color = "#B7472A" -foreground = "#B7472A" -background = "#191311" -timeout = 8 +vertical_alignment = top +format = "<b>%s</b>\n<i>from %a</i>\n\n%b" +icon_theme = "Chicago95-tux" +enable_recursive_icon_lookup = true +icon_position = left diff --git a/users/tazjin/dotfiles/fuzzel.ini b/users/tazjin/dotfiles/fuzzel.ini new file mode 100644 index 000000000000..92d46c0b6d88 --- /dev/null +++ b/users/tazjin/dotfiles/fuzzel.ini @@ -0,0 +1,17 @@ +[main] +font=Arial + +[colors] +background=c0c0c0ff +text=000000ff +input=000000ff +prompt=000000ff +match=808080ff +selection=000080ff +selection-match=ffffffff +selection-text=ffffffff +border=080808ff + +[border] +width=2 +radius=5 diff --git a/users/tazjin/dotfiles/niri.config.kdl b/users/tazjin/dotfiles/niri.config.kdl new file mode 100644 index 000000000000..a5926d5a8a8d --- /dev/null +++ b/users/tazjin/dotfiles/niri.config.kdl @@ -0,0 +1,133 @@ +// https://github.com/YaLTeR/niri/wiki/Configuration:-Overview + +input { + keyboard { + xkb { + layout "us,ru" + variant "hyper" + options "grp:win_space_toggle,compose:ralt,caps:hyper" + } + } + + touchpad { + tap + } +} + +layout { + gaps 12 + center-focused-column "never" + + preset-column-widths { + proportion 0.33333 + proportion 0.5 + proportion 0.66667 + } + + default-column-width {} + + focus-ring { + off + } + + border { + off + } +} + +spawn-at-startup "xwayland-satellite" +spawn-at-startup "xrandr --output eDP-1 --primary" +spawn-at-startup "wpaperd" "-d" +spawn-at-startup "systemctl --user start xss-lock" + +environment { + QT_QPA_PLATFORM "wayland" + DISPLAY ":0" + EDITOR "emacsclient" +} + +hotkey-overlay { + skip-at-startup +} + +screenshot-path "~/screenshots/screenshot-%Y-%m-%d_%H-%M-%S.png" + +animations { + slowdown 0.3 +} + +binds { + Mod+Shift+Slash { show-hotkey-overlay; } + + Mod+T { spawn "emacsclient" "--no-wait" "--create-frame" "--eval" "(vterm)"; } + Mod+Shift+T { spawn "alacritty"; } // fallback terminal + Mod+D { spawn "xfce4-appfinder" "--disable-server"; } + Super+Alt+L { spawn "swaylock" "-fFkl" "-c" "#008080"; } + Super+B { spawn "emacsclient" "-e" "(niri-go-anywhere-external)"; } + + // Volume control + XF86AudioRaiseVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"; } + XF86AudioLowerVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"; } + XF86AudioMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; } + XF86AudioMicMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; } + + // Brightness control + XF86MonBrightnessUp allow-when-locked=true { spawn "light" "-A" "5"; } + Shift+XF86MonBrightnessUp allow-when-locked=true { spawn "light" "-A" "1"; } + XF86MonBrightnessDown allow-when-locked=true { spawn "light" "-U" "5"; } + Shift+XF86MonBrightnessDown allow-when-locked=true { spawn "light" "-U" "1"; } + + Mod+Q { close-window; } + + Mod+Left { focus-column-or-monitor-left; } + Mod+Right { focus-column-or-monitor-right; } + Mod+Down { focus-column-or-monitor-right; } + Mod+Up { focus-column-or-monitor-left; } + Mod+J { focus-column-or-monitor-left; } + Mod+K { focus-column-or-monitor-right; } + Mod+L { focus-window-up; } + Mod+Semicolon { focus-window-down; } + + Mod+Ctrl+Left { move-column-left-or-to-monitor-left; } + Mod+Ctrl+Right { move-column-right-or-to-monitor-right; } + Mod+Ctrl+J { move-column-left-or-to-monitor-left; } + Mod+Ctrl+K { move-column-right-or-to-monitor-right; } + + Mod+Home { focus-column-first; } + Mod+End { focus-column-last; } + + Mod+Ctrl+Home { move-column-to-first; } + Mod+Ctrl+End { move-column-to-last; } + + // Scroll (or move windows) between columns when holding the modifier down. + Mod+WheelScrollDown cooldown-ms=150 { focus-column-or-monitor-right; } + Mod+WheelScrollUp cooldown-ms=150 { focus-column-or-monitor-left; } + Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-right-or-to-monitor-right; } + Mod+Ctrl+WheelScrollUp cooldown-ms=150 { move-column-left-or-to-monitor-left; } + + Mod+Comma { consume-window-into-column; } + Mod+Period { expel-window-from-column; } + + // There are also commands that consume or expel a single window to the side. + // Mod+BracketLeft { consume-or-expel-window-left; } + // Mod+BracketRight { consume-or-expel-window-right; } + + Mod+R { switch-preset-column-width; } + Mod+Shift+R { reset-window-height; } + Mod+F { maximize-column; } + Mod+Shift+F { fullscreen-window; } + Mod+C { center-column; } + + Mod+Minus { set-column-width "-10%"; } + Mod+Equal { set-column-width "+10%"; } + + // Finer height adjustments when in column with other windows. + Mod+Shift+Minus { set-window-height "-2%"; } + Mod+Shift+Equal { set-window-height "+2%"; } + + Print { screenshot; } + Ctrl+Print { screenshot-screen; } + Alt+Print { screenshot-window; } + + Mod+Shift+E { quit; } +} diff --git a/users/tazjin/dotfiles/waybar/config.nix b/users/tazjin/dotfiles/waybar/config.nix new file mode 100644 index 000000000000..ba5281f02e07 --- /dev/null +++ b/users/tazjin/dotfiles/waybar/config.nix @@ -0,0 +1,64 @@ +{ depot, pkgs, ... }: + +let + launcher = "${pkgs.xfce4-appfinder}/bin/xfce4-appfinder --disable-server"; +in +{ + mainBar = { + layer = "top"; + position = "bottom"; + modules-left = [ "custom/start" "wlr/taskbar" ]; + + "custom/start" = { + format = " Start"; + on-click = "xfce4-appfinder --disable-server"; + }; + + modules-right = [ "tray" "backlight" "battery" "pulseaudio" "clock" ]; + + pulseaudio = { + on-click = "pavucontrol"; + format = " "; #styling only + states = { + low = 1; + medium = 40; + high = 75; + }; + }; + + battery = { + format = " "; # styling only + interval = 10; + states = { + full = 100; + good = 85; + medium = 60; + low = 40; + warning = 20; + critical = 10; + }; + }; + + backlight = { + format = "{percent}%"; # styling only + on-scroll-up = "light -A 1"; + on-scroll-down = "light -U 1"; + }; + + clock.format-alt = "{:%a, %d. %b %H:%M}"; + + tray = { + icon-size = 20; + spacing = 10; + }; + + "wlr/taskbar" = { + format = "{icon} {title}"; + on-click = "activate"; + rewrite = { + # Truncate any format over 16 characters. + "^(.{16}).+$" = "$1…"; + }; + }; + }; +} diff --git a/users/tazjin/dotfiles/waybar/style.css b/users/tazjin/dotfiles/waybar/style.css new file mode 100644 index 000000000000..52f5759cc796 --- /dev/null +++ b/users/tazjin/dotfiles/waybar/style.css @@ -0,0 +1,254 @@ +* { + /* `otf-font-awesome` is required to be installed for icons */ + font-family: FontAwesome, MS Sans Serif; + font-size: 14px; +} + +window#waybar { + background-color: #c0c0c0; + border-top: 0.1875em solid #dfdfdf; + color: #000000; + transition-property: background-color; + transition-duration: .5s; +} + +window#waybar.hidden { + opacity: 0.2; +} + +window#waybar.termite { + background-color: #3F3F3F; +} + +window#waybar.chromium { + background-color: #000000; + border: none; +} + +button { + /* Use box-shadow instead of border so the text isn't offset */ + box-shadow: inset 0 -0.1875em transparent; + /* Avoid rounded borders under each button name */ + border: none; + border-radius: 0; +} + +/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ +button:hover { + background: inherit; + box-shadow: inset 0 -0.1875em #ffffff; +} + +#mode { + background-color: #64727D; + box-shadow: inset 0 -0.1875em #ffffff; +} + +#clock, +#battery, +#cpu, +#memory, +#disk, +#temperature, +#backlight, +#network, +#pulseaudio, +#wireplumber, +#custom-media, +#tray, +#mode, +#idle_inhibitor, +#scratchpad, +#power-profiles-daemon, +#mpd { + padding: 0 0.3125em; + padding-top: 0em; + padding-bottom: 0em; + /* color: #ffffff; */ +} + +#window, +#workspaces { + margin: 0 0.25em; +} + +/* faithful-ish recreation of the old Windows start button ... */ +#custom-start { + /* general positioning to keep the spacing approximately correct */ + color: @button_text_color; + font-weight: bold; + margin: 0.2em; + margin-top: 0.35em; + padding: 0.2em; + padding-left: 1.25em; + + /* raised button look, as per the Chicago95 GTK button style */ + border: 0.1em solid; + border-radius: 0em; + color: @button_text_color; + outline-color: @outline_color; + border-top-color: @border_bright; + border-right-color: @border_dark; + border-left-color: @border_bright; + border-bottom-color: @border_dark; + background-color: @button_bg_color; + box-shadow: inset -0.1em -0.1em @border_shade, inset 0.1em 0.1em @border_light; + + /* the actual image! */ + background-image: url("${CHICAGO95}/share/icons/Chicago95/categories/scalable/xfdesktop-menu.svg"); + background-position: 0.15em center; + background-repeat: no-repeat; + background-size: 1.4em; +} + +.modules-right { + margin: 0.2em; + margin-top: 0.35em; +} + +#clock { + border-top: 0.1em solid gray; + border-left: 0.1em solid gray; + border-right: 0.1em solid white; + border-bottom: 0.1em solid white; +} + +/* base setup for classes that have a Chicago95 icon as the display */ +#battery, #pulseaudio, #backlight { + background-position: center; + background-repeat: no-repeat; + background-size: 24px; + min-width: 24px; + color: transparent; /* because the tooltips are still desirable */ +} + +#backlight { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/32/xfpm-brightness-lcd.png"); + font-size: 0px; +} + +/* battery levels matching Chicago95 icons */ + +#battery.charging.critical { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-000-charging.png"); +} + +#battery.charging.warning { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-020-charging.png"); +} + +#battery.charging.low { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-040-charging.png"); +} + +#battery.charging.medium { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-060-charging.png"); +} + +#battery.charging.good { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-080-charging.png"); +} + +#battery.charging.full { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-100-charging.png"); +} + +#battery.critical { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-000.png"); +} + +#battery.warning { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-020.png"); +} + +#battery.low { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-040.png"); +} + +#battery.medium { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-060.png"); +} + +#battery.good { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-080.png"); +} + +#battery.full { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-100.png"); +} + +/* volume levels matching Chicago95 icons */ + +#pulseaudio.muted { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/32/audio-volume-muted.png"); +} + +#pulseaudio.low { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/32/audio-volume-low.png"); +} + +#pulseaudio.medium { + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/32/audio-volume-medium.png"); +} + +#pulseaudio { /* default, if no lower volume state is set */ + background-image: url("${CHICAGO95}/share/icons/Chicago95/status/32/audio-volume-high.png"); +} + +@keyframes blink { + to { + background-color: #ffffff; + color: #000000; + } +} + +label:focus { + background-color: #000000; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #e35f5f; +} + +#idle_inhibitor { + background-color: #2d3436; +} + +#idle_inhibitor.activated { + background-color: #ecf0f1; + color: #2d3436; +} + +#taskbar { + color: @button_text_color; + margin: 0.2em; + margin-top: 0.35em; +} + +#taskbar button { + padding: 0.2em; + margin-right: 0.3em; + border: 0.1em solid; + border-radius: 0em; + color: @button_text_color; + outline-color: @outline_color; + border-top-color: @border_bright; + border-right-color: @border_dark; + border-left-color: @border_bright; + border-bottom-color: @border_dark; + background-color: @button_bg_color; + box-shadow: inset -0.1em -0.1em @border_shade, inset 0.1em 0.1em @border_light; +} + +#taskbar button.active { + border-top-color: @border_dark; + border-right-color: @border_bright; + border-left-color: @border_dark; + border-bottom-color: @border_bright; + box-shadow: inset 1px 1px @border_shade; +} diff --git a/users/tazjin/eaglemode/default.nix b/users/tazjin/eaglemode/default.nix new file mode 100644 index 000000000000..9f59695a40b8 --- /dev/null +++ b/users/tazjin/eaglemode/default.nix @@ -0,0 +1,16 @@ +# Derivation for my fully configured Eagle Mode. +{ depot, ... }: + +with depot.tools.eaglemode; + +withConfig { + config = etcDir { + extraPaths = [ + commands.emacsclient + plugins.example + plugins.yatracker + plugins.qoi + plugins.avif + ]; + }; +} diff --git a/users/tazjin/emacs/config/init.el b/users/tazjin/emacs/config/init.el index ced3bf2ff83b..a5b0e933d56f 100644 --- a/users/tazjin/emacs/config/init.el +++ b/users/tazjin/emacs/config/init.el @@ -72,7 +72,8 @@ (use-package rainbow-mode) (use-package s) (use-package string-edit-at-point) -(use-package term-switcher) +(use-package term-switcher + :bind (:map global-map ("C-c v" . #'ts/switch-to-terminal))) (use-package undo-tree :config (global-undo-tree-mode) @@ -126,12 +127,9 @@ (use-package f) -(use-package go-mode - :bind (:map go-mode-map ("C-c C-r" . recompile)) - :hook ((go-mode . (lambda () - (setq tab-width 2) - (setq-local compile-command - (concat "go build " buffer-file-name)))))) +(use-package go-ts-mode + :custom + (go-ts-mode-indent-offset 4)) (use-package haskell-mode) @@ -152,6 +150,7 @@ (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))) (use-package markdown-toc) +(use-package niri) (use-package nix-mode :hook ((nix-mode . (lambda () @@ -169,7 +168,7 @@ (setq common-lisp-hyperspec-root "file:///home/tazjin/docs/lisp/")) (use-package telega - :bind (:map global-map ("s-c" . (lambda (p) (interactive "P") + :bind (:map global-map ("C-x c" . (lambda (p) (interactive "P") (if p (call-interactively #'telega-chat-with) (telega)))) :map telega-chat-button-map ("a" . ignore)) @@ -241,8 +240,7 @@ ;; Load all other Emacs configuration. These configurations are ;; added to `load-path' by Nix. -(mapc 'require '(desktop - mail-setup +(mapc 'require '(mail-setup look-and-feel functions settings diff --git a/users/tazjin/emacs/default.nix b/users/tazjin/emacs/default.nix index 17973b8b4869..80a922043277 100644 --- a/users/tazjin/emacs/default.nix +++ b/users/tazjin/emacs/default.nix @@ -3,7 +3,7 @@ { depot, lib, pkgs, ... }: pkgs.makeOverridable - ({ emacs ? pkgs.emacs29 }: + ({ emacs ? pkgs.emacs29-pgtk }: let emacsPackages = (pkgs.emacsPackagesFor emacs); emacsWithPackages = emacsPackages.emacsWithPackages; @@ -41,6 +41,7 @@ pkgs.makeOverridable tree-sitter-rust tree-sitter-sql tree-sitter-toml + tree-sitter-typescript tree-sitter-yaml ]); @@ -112,6 +113,7 @@ pkgs.makeOverridable # Custom depot packages (either ours, or overridden ones) tvlPackages.dottime + tvlPackages.niri tvlPackages.nix-util tvlPackages.passively tvlPackages.rcirc diff --git a/users/tazjin/german-string/.gitignore b/users/tazjin/german-string/.gitignore new file mode 100644 index 000000000000..2f7896d1d136 --- /dev/null +++ b/users/tazjin/german-string/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/users/tazjin/german-string/Cargo.lock b/users/tazjin/german-string/Cargo.lock new file mode 100644 index 000000000000..ffd73ea32472 --- /dev/null +++ b/users/tazjin/german-string/Cargo.lock @@ -0,0 +1,399 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "german-string" +version = "0.1.0" +dependencies = [ + "proptest", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.156" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "syn" +version = "2.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/users/tazjin/german-string/Cargo.toml b/users/tazjin/german-string/Cargo.toml new file mode 100644 index 000000000000..8eec963f071a --- /dev/null +++ b/users/tazjin/german-string/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "german-string" +version = "0.1.0" +edition = "2021" + +[dev-dependencies] +proptest = "1.5.0" diff --git a/users/tazjin/german-string/default.nix b/users/tazjin/german-string/default.nix new file mode 100644 index 000000000000..c6cbc8c3c2d0 --- /dev/null +++ b/users/tazjin/german-string/default.nix @@ -0,0 +1,5 @@ +{ depot, pkgs, ... }: + +depot.third_party.naersk.buildPackage { + src = ./.; +} diff --git a/users/tazjin/german-string/src/lib.rs b/users/tazjin/german-string/src/lib.rs new file mode 100644 index 000000000000..328eca309f38 --- /dev/null +++ b/users/tazjin/german-string/src/lib.rs @@ -0,0 +1,435 @@ +use std::alloc::Layout; +use std::cmp::Ordering; +use std::fmt::{Debug, Formatter}; + +#[derive(Clone, Copy)] +#[repr(C)] +struct GSSmall { + len: u32, + data: [u8; 12], +} + +#[derive(Clone, Copy)] +#[repr(transparent)] +struct StorageClassPtr(usize); + +impl StorageClassPtr { + fn transient(ptr: *const u8) -> Self { + debug_assert!( + (ptr as usize & 0b1) == 0, + "pointer must be at least 2-byte aligned" + ); + Self(ptr as usize) + } + + fn persistent(ptr: *const u8) -> Self { + debug_assert!( + (ptr as usize & 0b1) == 0, + "pointer must be at least 2-byte aligned" + ); + Self((ptr as usize) | 0b1) + } + + fn as_ptr(&self) -> *const u8 { + (self.0 & !0b1) as *const u8 + } + + unsafe fn as_mut_ptr(&self) -> *mut u8 { + (self.0 & !0b1) as *mut u8 + } + + fn is_transient(&self) -> bool { + (self.0 & 0b1) == 0 + } +} + +#[derive(Clone, Copy)] +#[repr(C)] +struct GSLarge { + len: u32, + prefix: [u8; 4], + data: StorageClassPtr, +} + +const _ASSERT_VARIANTS_SIZE: () = assert!( + std::mem::size_of::<GSSmall>() == std::mem::size_of::<GSLarge>(), + "German String variants must have the same size" +); + +union GSRepr { + small: GSSmall, + large: GSLarge, +} + +#[repr(transparent)] +pub struct GermanString(GSRepr); + +const _ASSERT_GSTRING_SIZE: () = assert!( + std::mem::size_of::<GermanString>() == 16, + "German String should be 16 bytes in size", +); + +impl GermanString { + /// Creates a new transient German String from the given slice, copying the + /// data in the process. + pub fn transient(bytes: &[u8]) -> GermanString { + if bytes.len() > u32::MAX as usize { + panic!("GermanString maximum length is {} bytes", u32::MAX); + } + + if bytes.len() <= 12 { + let mut s = GSSmall { + len: bytes.len() as u32, + data: [0u8; 12], + }; + s.data[..bytes.len()].copy_from_slice(bytes); + GermanString(GSRepr { small: s }) + } else { + let layout = Layout::array::<u8>(bytes.len()).unwrap(); + let mut large = GSLarge { + len: bytes.len() as u32, + prefix: [0u8; 4], + data: unsafe { + let ptr = std::alloc::alloc(layout); + if ptr.is_null() { + std::alloc::handle_alloc_error(layout); + } + std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len()); + StorageClassPtr::transient(ptr) + }, + }; + + large.prefix.copy_from_slice(&bytes[..4]); + + GermanString(GSRepr { large }) + } + } + + /// Creates a new transient German String from the given owned bytes. Short + /// strings will be copied into the string representation, long strings will + /// be moved out of the given vector without additional allocations. + pub fn transient_from_owned(bytes: Vec<u8>) -> GermanString { + if bytes.len() > u32::MAX as usize { + panic!("GermanString maximum length is {} bytes", u32::MAX); + } + + if bytes.len() <= 12 { + let mut s = GSSmall { + len: bytes.len() as u32, + data: [0u8; 12], + }; + + s.data[..bytes.len()].copy_from_slice(&bytes); + GermanString(GSRepr { small: s }) + } else { + let md = std::mem::ManuallyDrop::new(bytes); + let mut large = GSLarge { + len: md.len() as u32, + prefix: [0u8; 4], + data: StorageClassPtr::transient(md.as_ptr()), + }; + + large.prefix.copy_from_slice(&md[..4]); + GermanString(GSRepr { large }) + } + } + + /// Creates a persistent German String from a static data buffer. + pub fn persistent(bytes: &'static [u8]) -> GermanString { + if bytes.len() > u32::MAX as usize { + panic!("GermanString maximum length is {} bytes", u32::MAX); + } + + if bytes.len() <= 12 { + let mut s = GSSmall { + len: bytes.len() as u32, + data: [0u8; 12], + }; + + s.data[..bytes.len()].copy_from_slice(&bytes); + GermanString(GSRepr { small: s }) + } else { + let mut large = GSLarge { + len: bytes.len() as u32, + prefix: [0u8; 4], + data: StorageClassPtr::persistent(bytes.as_ptr()), + }; + + large.prefix.copy_from_slice(&bytes[..4]); + GermanString(GSRepr { large }) + } + } + + /// Creates a persistent German String by leaking the provided data. + pub fn persistent_leak(bytes: Vec<u8>) -> GermanString { + if bytes.len() > u32::MAX as usize { + panic!("GermanString maximum length is {} bytes", u32::MAX); + } + + if bytes.len() <= 12 { + let mut s = GSSmall { + len: bytes.len() as u32, + data: [0u8; 12], + }; + + s.data[..bytes.len()].copy_from_slice(&bytes); + GermanString(GSRepr { small: s }) + } else { + let md = std::mem::ManuallyDrop::new(bytes); + let mut large = GSLarge { + len: md.len() as u32, + prefix: [0u8; 4], + data: StorageClassPtr::persistent(md.as_ptr()), + }; + + large.prefix.copy_from_slice(&md[..4]); + GermanString(GSRepr { large }) + } + } + + /// Creates a persistent German String from a static data buffer. + pub fn persistent_from_str(s: &'static str) -> GermanString { + GermanString::persistent(s.as_bytes()) + } + + pub fn len(&self) -> usize { + // SAFETY: The length field is located in the same location for both + // variants, reading it from either is safe. + unsafe { self.0.small.len as usize } + } + + pub fn as_bytes(&self) -> &[u8] { + if self.len() > 12 { + unsafe { std::slice::from_raw_parts(self.0.large.data.as_ptr(), self.len()) } + } else { + unsafe { &self.0.small.data.as_ref()[..self.len()] } + } + } + + pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> { + std::str::from_utf8(self.as_bytes()) + } +} + +impl Drop for GermanString { + fn drop(&mut self) { + unsafe { + if self.len() > 12 && self.0.large.data.is_transient() { + let layout = Layout::array::<u8>(self.len()).unwrap(); + std::alloc::dealloc(self.0.large.data.as_mut_ptr(), layout); + } + } + } +} + +impl PartialEq for GermanString { + fn eq(&self, other: &GermanString) -> bool { + if self.len() != other.len() { + return false; + } + + unsafe { + if self.len() <= 12 { + return self.0.small.data[..self.len()] == other.0.small.data[..other.len()]; + } + return self.0.large.data.as_ptr() == other.0.large.data.as_ptr() + || (self.0.large.prefix == other.0.large.prefix + && self.as_bytes() == other.as_bytes()); + } + } +} + +impl Eq for GermanString {} + +impl Ord for GermanString { + fn cmp(&self, other: &GermanString) -> Ordering { + match (self.len().cmp(&12), other.len().cmp(&12)) { + // two small strings + (Ordering::Less | Ordering::Equal, Ordering::Less | Ordering::Equal) => unsafe { + self.0.small.data[..self.len()].cmp(&other.0.small.data[..other.len()]) + }, + // two large strings + (Ordering::Greater, Ordering::Greater) => unsafe { + match self.0.large.prefix.cmp(&other.0.large.prefix) { + Ordering::Equal => self.as_bytes().cmp(other.as_bytes()), + ordering => ordering, + } + }, + + // LHS large, RHS small + (Ordering::Greater, _) => { + let prefix_ordering = + unsafe { self.0.large.prefix.as_slice().cmp(&other.0.small.data[..4]) }; + + if prefix_ordering != Ordering::Equal { + return prefix_ordering; + } + + self.as_bytes().cmp(other.as_bytes()) + } + + // LHS small, RHS large + (_, Ordering::Greater) => { + let prefix_ordering = + unsafe { self.0.small.data[..4].cmp(other.0.large.prefix.as_slice()) }; + + if prefix_ordering != Ordering::Equal { + return prefix_ordering; + } + + self.as_bytes().cmp(other.as_bytes()) + } + } + } +} + +impl PartialOrd for GermanString { + fn partial_cmp(&self, other: &GermanString) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Debug for GermanString { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + String::from_utf8_lossy(self.as_bytes()).fmt(f) + } +} + +impl Clone for GermanString { + fn clone(&self) -> Self { + unsafe { + if self.len() <= 12 { + return GermanString(GSRepr { + small: self.0.small.clone(), + }); + } + + if self.0.large.data.is_transient() { + return GermanString::transient(self.as_bytes()); + } + + return GermanString(GSRepr { + large: self.0.large.clone(), + }); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use proptest::prelude::*; + + impl Arbitrary for GermanString { + type Parameters = <String as Arbitrary>::Parameters; + type Strategy = BoxedStrategy<Self>; + + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { + any_with::<String>(args) + .prop_map(|s| GermanString::transient(s.as_bytes())) + .boxed() + } + } + + #[test] + fn test_empty_string() { + let empty = GermanString::transient(b""); + + assert_eq!(empty.len(), 0, "empty string should be empty"); + assert_eq!(empty.as_bytes(), b"", "empty string should contain nothing"); + assert_eq!( + empty.as_str().expect("empty string is valid UTF-8"), + "", + "empty string should contain empty string" + ); + } + + #[test] + fn test_short_string() { + let short = GermanString::transient(b"meow"); + + assert_eq!(short.len(), 4, "'meow' is four characters"); + assert_eq!( + short.as_bytes(), + b"meow", + "short string returns correct bytes" + ); + assert_eq!( + short.as_str().expect("'meow' is valid UTF-8"), + "meow", + "short string returns correct string" + ); + } + + #[test] + fn test_long_string() { + let input: &str = "This code was written at https://signal.live"; + let long = GermanString::transient(input.as_bytes()); + + assert_eq!(long.len(), 44, "long string has correct length"); + assert_eq!( + long.as_bytes(), + input.as_bytes(), + "long string returns correct bytes" + ); + + assert_eq!( + long.as_str().expect("input is valid UTF-8"), + input, + "long string returns correct string" + ); + } + + proptest! { + #[test] + fn test_roundtrip_vec(input: Vec<u8>) { + let gs = GermanString::transient_from_owned(input.clone()); + assert_eq!(input.len(), gs.len(), "length should match"); + + let out = gs.as_bytes().to_owned(); + assert_eq!(input, out, "roundtrip should yield same bytes"); + } + + #[test] + fn test_roundtrip_string(input: String) { + let gs = GermanString::transient_from_owned(input.clone().into_bytes()); + assert_eq!(input.len(), gs.len(), "length should match"); + + let out = String::from_utf8(gs.as_bytes().to_owned()) + .expect("string should be valid after roundtrip"); + + assert_eq!(input, out, "roundtrip should yield same string"); + } + + // Test [`Eq`] implementation. + #[test] + fn test_eq(lhs: Vec<u8>, rhs: Vec<u8>) { + let lhs_gs = GermanString::transient(lhs.as_slice()); + let rhs_gs = GermanString::transient(rhs.as_slice()); + + assert_eq!( + (lhs == rhs), + (lhs_gs == rhs_gs), + "Eq should match between std::String and GermanString ({:?} == {:?})", + lhs, rhs, + ); + } + + #[test] + fn test_reflexivity(x: GermanString) { + prop_assert!(x == x); + } + + #[test] + fn test_symmetry(x: GermanString, y: GermanString) { + prop_assert_eq!(x == y, y == x); + } + + #[test] + fn test_transitivity(x: GermanString, y: GermanString, z: GermanString) { + if x == y && y == z { + assert!(x == z); + } + } + } +} diff --git a/users/tazjin/home/shared.nix b/users/tazjin/home/shared.nix index 38d8add4ac2f..51ff1ac94af5 100644 --- a/users/tazjin/home/shared.nix +++ b/users/tazjin/home/shared.nix @@ -5,6 +5,8 @@ let + inherit (depot.third_party) chicago95; + # URL handler to open `tg://` URLs in telega.el telega-launcher = pkgs.writeShellScriptBin "telega-launcher" '' echo "Opening ''${1} in telega.el ..." @@ -38,12 +40,6 @@ in ''; }; - services.screen-locker = { - enable = true; - inactiveInterval = 10; # minutes - lockCmd = "${depot.users.tazjin.screenLock}/bin/tazjin-screen-lock"; - }; - home.packages = [ telega-launcher ]; xdg.desktopEntries.telega-launcher = { @@ -65,13 +61,40 @@ in }; }; - services.picom = { + # put Niri (& related tools) configuration in place + xdg.configFile."niri/config.kdl".source = depot.users.tazjin.dotfiles.niri; + xdg.configFile."fuzzel/fuzzel.ini".source = depot.users.tazjin.dotfiles.fuzzel; + + programs.wpaperd = { + enable = true; + settings = { + default = { + duration = "1d"; + mode = "center"; + sorting = "random"; + }; + + any.path = ../wallpapers; + }; + }; + + programs.waybar = { enable = true; - vSync = true; - backend = "glx"; + settings = depot.users.tazjin.dotfiles.waybar.config; + style = depot.users.tazjin.dotfiles.waybar.style; + systemd.enable = true; }; + systemd.user.services.waybar.Unit.After = lib.mkForce [ "niri.service" ]; - services.syncthing.enable = true; + + services.swayidle = let cmd = "${pkgs.swaylock}/bin/swaylock -fFkl -c 008080"; in { + enable = true; + events = [ + { event = "before-sleep"; command = cmd; } + { event = "lock"; command = cmd; } + ]; + }; + systemd.user.services.swayidle.Unit.After = lib.mkForce [ "niri.service" ]; # Enable the dunst notification daemon, but force the # configuration file separately instead of going via the strange @@ -84,6 +107,18 @@ in ''; }; + gtk = { + enable = true; + theme.name = "Chicago95"; + theme.package = chicago95; + + iconTheme.name = "Chicago95-tux"; + iconTheme.package = chicago95; + + cursorTheme.name = lib.mkDefault "Chicago95_Animated_Hourglass_Cursors"; + cursorTheme.package = chicago95; + }; + systemd.user.startServices = true; # Previous default version, see https://github.com/nix-community/home-manager/blob/master/docs/release-notes/rl-2211.adoc diff --git a/users/tazjin/home/zamalek.nix b/users/tazjin/home/zamalek.nix index d24de945bb28..98da5e6b233e 100644 --- a/users/tazjin/home/zamalek.nix +++ b/users/tazjin/home/zamalek.nix @@ -8,4 +8,6 @@ depot.users.tazjin.home.shared depot.users.tazjin.home.persistence ]; + + gtk.cursorTheme.name = lib.mkForce "Chicago95_Animated_Hourglass_Cursors_HiDPI"; } diff --git a/users/tazjin/niri-reap/.gitignore b/users/tazjin/niri-reap/.gitignore new file mode 100644 index 000000000000..2f7896d1d136 --- /dev/null +++ b/users/tazjin/niri-reap/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/users/tazjin/niri-reap/Cargo.lock b/users/tazjin/niri-reap/Cargo.lock new file mode 100644 index 000000000000..e7916c5b3acd --- /dev/null +++ b/users/tazjin/niri-reap/Cargo.lock @@ -0,0 +1,104 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "niri-ipc" +version = "0.1.9" +source = "git+https://github.com/YaLTeR/niri.git#6a48728ffb1e638839b07f9ab2f06b2adb41dc61" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "niri-reap" +version = "0.1.0" +dependencies = [ + "niri-ipc", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/users/tazjin/niri-reap/Cargo.toml b/users/tazjin/niri-reap/Cargo.toml new file mode 100644 index 000000000000..5f6677196333 --- /dev/null +++ b/users/tazjin/niri-reap/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "niri-reap" +version = "0.1.0" +edition = "2021" + +[dependencies] +niri-ipc = { git = "https://github.com/YaLTeR/niri.git", version = "0.1.9" } diff --git a/users/tazjin/niri-reap/README.md b/users/tazjin/niri-reap/README.md new file mode 100644 index 000000000000..207a087657fa --- /dev/null +++ b/users/tazjin/niri-reap/README.md @@ -0,0 +1,20 @@ +niri-reap +========= + +Tiny, MIT-licensed companion program for [niri](https://github.com/YaLTeR/niri). + +I don't use workspaces in my workflow, but when disconnecting an external +screen, the workspaces that it was displaying are moved to the remaining screen. + +This program "reaps" all windows on workspaces except the currently active one, +and moves them all to the current workspace. + +## Usage + +If you have the full TVL monorepo, just `mg run //users/tazjin/niri-reap`. There +is no configuration, and there are no flags. + +If you don't have the TVL monorepo and just want `niri-reap`, do this: + +1. Get the code: `git clone https://code.tvl.fyi/depot.git:/users/tazjin/niri-reap.git` +2. Run the code: `cargo run` diff --git a/users/tazjin/niri-reap/default.nix b/users/tazjin/niri-reap/default.nix new file mode 100644 index 000000000000..80c82f475a2f --- /dev/null +++ b/users/tazjin/niri-reap/default.nix @@ -0,0 +1,13 @@ +{ depot, pkgs, ... }: + +pkgs.rustPlatform.buildRustPackage { + name = "niri-reap"; + src = depot.third_party.gitignoreSource ./.; + + cargoLock = { + lockFile = ./Cargo.lock; + outputHashes = { + "niri-ipc-0.1.9" = "sha256:1s294bw62mmckq9xyfzgw4p2nvkzday4k276j60m668prhlfp071"; + }; + }; +} diff --git a/users/tazjin/niri-reap/src/main.rs b/users/tazjin/niri-reap/src/main.rs new file mode 100644 index 000000000000..315a5015d413 --- /dev/null +++ b/users/tazjin/niri-reap/src/main.rs @@ -0,0 +1,93 @@ +use niri_ipc::socket::Socket; +use niri_ipc::{Action, Reply, Request, Response, Window, Workspace}; + +fn sock() -> Socket { + Socket::connect().expect("could not connect to Niri socket") +} + +fn list_workspaces() -> Vec<Workspace> { + let (reply, _) = sock() + .send(Request::Workspaces) + .expect("failed to send workspace request"); + + match reply { + Reply::Err(err) => panic!("failed to list workspaces: {}", err), + Reply::Ok(Response::Workspaces(w)) => w, + Reply::Ok(other) => panic!("unexpected reply from Niri: {:#?}", other), + } +} + +fn list_windows() -> Vec<Window> { + let (reply, _) = sock() + .send(Request::Windows) + .expect("failed to send window request"); + + match reply { + Reply::Err(err) => panic!("failed to list windows: {}", err), + Reply::Ok(Response::Windows(w)) => w, + Reply::Ok(other) => panic!("unexpected reply from Niri: {:#?}", other), + } +} + +fn reap_window(window: u64, workspace: u64) { + let (reply, _) = sock() + .send(Request::Action(Action::MoveWindowToWorkspace { + window_id: Some(window), + reference: niri_ipc::WorkspaceReferenceArg::Id(workspace), + })) + .expect("failed to send window move request"); + + reply.expect("failed to move window to workspace"); +} + +fn get_active_workspace(workspaces: &[Workspace]) -> &Workspace { + workspaces + .iter() + .filter(|w| w.is_focused) + .next() + .expect("expected an active workspace") +} + +fn move_workspace_up() { + let (result, _) = sock() + .send(Request::Action(Action::MoveWorkspaceUp {})) + .expect("failed to send workspace move command"); + + result.expect("failed to move workspace up"); +} + +fn main() { + let mut workspaces = list_workspaces(); + let mut active_workspace = get_active_workspace(&workspaces); + + // Ensure that the current workspace is the first one, to avoid issues with + // indices changing during the window moves. + while active_workspace.idx > 1 { + move_workspace_up(); + workspaces = list_workspaces(); + active_workspace = get_active_workspace(&workspaces); + } + + let orphan_workspaces = workspaces + .iter() + .filter(|w| w.output == active_workspace.output) + .filter(|w| w.idx > 1) + .map(|w| w.id) + .collect::<Vec<_>>(); + + if orphan_workspaces.is_empty() { + return; + } + + let reapable = list_windows() + .into_iter() + .filter(|w| match w.workspace_id { + Some(id) => orphan_workspaces.contains(&id), + None => true, + }) + .collect::<Vec<_>>(); + + for window in reapable.iter().rev() { + reap_window(window.id, active_workspace.id); + } +} diff --git a/users/tazjin/nixos/arbat/default.nix b/users/tazjin/nixos/arbat/default.nix index 73bed4573ac1..c87aa445c29c 100644 --- a/users/tazjin/nixos/arbat/default.nix +++ b/users/tazjin/nixos/arbat/default.nix @@ -55,7 +55,7 @@ in hardware = { enableRedistributableFirmware = true; - opengl.enable = true; + graphics.enable = true; bluetooth.enable = true; }; @@ -66,8 +66,6 @@ in mouse.naturalScrolling = false; mouse.disableWhileTyping = true; }; - # services.xserver.libinput.touchpad.clickMethod = "clickfinger"; - # services.xserver.libinput.touchpad.tapping = false; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; system.stateVersion = "24.11"; diff --git a/users/tazjin/nixos/default.nix b/users/tazjin/nixos/default.nix index 29b6a0e83b10..6bca09d8f129 100644 --- a/users/tazjin/nixos/default.nix +++ b/users/tazjin/nixos/default.nix @@ -4,10 +4,12 @@ let systemFor = sys: (depot.ops.nixos.nixosFor sys).system; in depot.nix.readTree.drvTargets { arbatSystem = systemFor depot.users.tazjin.nixos.arbat; camdenSystem = systemFor depot.users.tazjin.nixos.camden; - frogSystem = systemFor depot.users.tazjin.nixos.frog; tverskoySystem = systemFor depot.users.tazjin.nixos.tverskoy; zamalekSystem = systemFor depot.users.tazjin.nixos.zamalek; koptevoRaw = depot.ops.nixos.nixosFor depot.users.tazjin.nixos.koptevo; koptevoSystem = systemFor depot.users.tazjin.nixos.koptevo; khamovnikSystem = systemFor depot.users.tazjin.nixos.khamovnik; + + # no need to build this while the machine is in storage + # frogSystem = systemFor depot.users.tazjin.nixos.frog; } diff --git a/users/tazjin/nixos/frog/default.nix b/users/tazjin/nixos/frog/default.nix index 349c5eb6d81a..ce82d749f408 100644 --- a/users/tazjin/nixos/frog/default.nix +++ b/users/tazjin/nixos/frog/default.nix @@ -41,9 +41,9 @@ lib.fix (self: { hardware = { cpu.amd.updateMicrocode = true; enableRedistributableFirmware = true; - opengl = { + graphics = { enable = true; - driSupport32Bit = true; + enable32Bit = true; }; pulseaudio = { diff --git a/users/tazjin/nixos/khamovnik/default.nix b/users/tazjin/nixos/khamovnik/default.nix index 8ea925c90dd0..dcb19be2ae9f 100644 --- a/users/tazjin/nixos/khamovnik/default.nix +++ b/users/tazjin/nixos/khamovnik/default.nix @@ -43,6 +43,8 @@ in "rtsx_pci_sdmmc" ]; kernelModules = [ "kvm-intel" ]; + + tmp.cleanOnBoot = true; }; fileSystems = { @@ -64,12 +66,13 @@ in tvl.cache.enable = true; networking.hostName = "khamovnik"; + networking.networkmanager.enable = true; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; hardware.cpu.intel.updateMicrocode = true; hardware.enableRedistributableFirmware = true; - hardware.opengl.extraPackages = with pkgs; [ + hardware.graphics.extraPackages = with pkgs; [ intel-compute-runtime intel-media-driver intel-vaapi-driver @@ -104,7 +107,6 @@ in }; # Enable sound with pipewire. - sound.enable = true; hardware.pulseaudio.enable = false; security.rtkit.enable = true; services.pipewire = { @@ -117,6 +119,13 @@ in # Try to work around Intel CPU throttling bugs services.throttled.enable = true; + # Try to get suspend to work more reliably + services.logind = { + lidSwitch = "suspend"; + lidSwitchDocked = "suspend"; + lidSwitchExternalPower = "suspend"; + }; + virtualisation.docker.enable = true; hardware.bluetooth.enable = true; @@ -129,5 +138,7 @@ in protobuf ]; + programs.adb.enable = true; + system.stateVersion = "23.05"; # Did you read the comment? } diff --git a/users/tazjin/nixos/koptevo/default.nix b/users/tazjin/nixos/koptevo/default.nix index ea8dfd4bd809..187f7b92fbb0 100644 --- a/users/tazjin/nixos/koptevo/default.nix +++ b/users/tazjin/nixos/koptevo/default.nix @@ -11,12 +11,12 @@ in imports = [ (mod "quassel.nix") (mod "www/base.nix") - (mod "www/tazj.in.nix") (usermod "airsonic.nix") (usermod "geesefs.nix") + (usermod "homepage.nix") + (usermod "miniflux.nix") (usermod "predlozhnik.nix") (usermod "tgsa.nix") - (usermod "miniflux.nix") (depot.third_party.agenix.src + "/modules/age.nix") ]; @@ -62,7 +62,7 @@ in domain = "tazj.in"; useDHCP = true; firewall.enable = true; - firewall.allowedTCPPorts = [ 22 80 443 ]; + firewall.allowedTCPPorts = [ 22 80 443 8776 9443 ]; wireless.enable = true; wireless.networks."How do I computer fast?" = { @@ -72,8 +72,22 @@ in time.timeZone = "UTC"; - security.acme.acceptTerms = true; - security.acme.defaults.email = lib.mkForce "acme@tazj.in"; + security.acme = { + acceptTerms = true; + defaults.email = lib.mkForce "acme@tazj.in"; + + # wildcard cert for usage with Yggdrasil services + certs."y.tazj.in" = { + dnsProvider = "yandexcloud"; + credentialFiles.YANDEX_CLOUD_IAM_TOKEN_FILE = "/run/agenix/lego-yandex"; + extraDomainNames = [ "*.y.tazj.in" ]; + + # folder tvl/tazjin-private/default + environmentFile = builtins.toFile "lego-yandex-env" '' + YANDEX_CLOUD_FOLDER_ID=b1gq41rsbggeum4qafnh + ''; + }; + }; programs.fish.enable = true; @@ -84,11 +98,14 @@ in openssh.authorizedKeys.keys = depot.users.tazjin.keys.all; }; + users.users.nginx.extraGroups = [ "acme" ]; + age.secrets = let secretFile = name: depot.users.tazjin.secrets."${name}.age"; in { + lego-yandex.file = secretFile "lego-yandex"; tgsa-yandex.file = secretFile "tgsa-yandex"; }; @@ -101,6 +118,7 @@ in acmeHost = "koptevo.tazj.in"; bindAddresses = [ "0.0.0.0" + "::" ]; }; @@ -169,16 +187,120 @@ in # List packages installed in system profile. To search, run: # $ nix search wget environment.systemPackages = with pkgs; [ + bat curl + emacs-nox htop jq - nmap - bat - emacs-nox nano + nmap + radicle-node wget ]; + # configure Yggdrasil network + services.yggdrasil = { + enable = true; + persistentKeys = true; + openMulticastPort = true; + + settings = { + Listen = [ "tls://[::]:9443" ]; # yggd + IfName = "ygg0"; + Peers = [ + "quic://ygg-msk-1.averyan.ru:8364" + "tls://ekb.itrus.su:7992" + "tls://s-mow-1.sergeysedoy97.ru:65534" + ]; + + MulticastInterfaces = [{ + Regex = "enp.*"; + Beacon = true; + Listen = true; + Port = 0; + }]; + + AllowedPublicKeys = [ + "573fd89392e2741ead4edd85034c91c88f1e560d991bbdbf1fccb6233db4d325" # khamovnik + "a56300c3af1ad54840f4b38b9438e3c108a0aa0fd72793dc7d6bd57325c6d691" # zamalek + "301e98e68522f55b3d9fb7a37817eb0e1aeb6478ef1ac124b9915080e9be205f" # tverskoy + "152b658f8a3e0cd6d1486c3cb984795ec7c9a02274c9f096bd2045cabf8bfa92" # A9 + "550f4920592d2831d013fd1c83ba9ad174ec352273260fd5d7c2627dbe60d097" # matepad + ]; + }; + }; + + # TODO(tazjin): move this to a module for radicle stuff + services.radicle = { + enable = true; + publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILHs6jSvMdtu9oJCt48etEs8ExjfGY5PmWQsRzFleogS"; + privateKeyFile = "/etc/secrets/radicle"; # TODO: to manage, or not to manage ... + + settings = { + web.pinned.repositories = [ + "rad:z3r5zMi9U3az3i4cPKxMcA3K7xx9L" # depot + "rad:z2mdnBK1tX6pibdBfRct3ThCgheHu" # tvix-go + ]; + + node = { + alias = "rad.tazj.in"; + seedingPolicy.default = "block"; + }; + }; + + node = { + openFirewall = true; + listenAddress = "[::]"; + }; + + httpd = { + enable = true; + listenAddress = "127.0.0.1"; + listenPort = 7235; # radl + }; + }; + + services.nginx.virtualHosts."rad.tazj.in" = { + enableACME = true; + forceSSL = true; + locations."/".proxyPass = "http://127.0.0.1:7235"; + }; + + services.nginx.virtualHosts."rad.y.tazj.in" = { + enableSSL = true; + useACMEHost = "y.tazj.in"; + locations = config.services.nginx.virtualHosts."rad.tazj.in".locations; + }; + + services.nginx.virtualHosts."src.tazj.in" = { + enableACME = true; + forceSSL = true; + root = depot.third_party.radicle-explorer.withPreferredSeeds [{ + hostname = "rad.tazj.in"; + port = 443; + scheme = "https"; + }]; + + locations."/" = { + index = "index.html"; + extraConfig = '' + try_files $uri $uri/ /index.html; + ''; + }; + }; + + services.nginx.virtualHosts."src.y.tazj.in" = { + enableSSL = true; + useACMEHost = "y.tazj.in"; + root = depot.third_party.radicle-explorer.withPreferredSeeds [{ + hostname = "rad.y.tazj.in"; + port = 443; + scheme = "https"; + }]; + + locations = config.services.nginx.virtualHosts."src.tazj.in".locations; + }; + programs.mtr.enable = true; programs.mosh.enable = true; zramSwap.enable = true; diff --git a/users/tazjin/nixos/modules/desktop.nix b/users/tazjin/nixos/modules/desktop.nix index 296960a44345..d2cdb37646f3 100644 --- a/users/tazjin/nixos/modules/desktop.nix +++ b/users/tazjin/nixos/modules/desktop.nix @@ -10,43 +10,67 @@ pulse.enable = true; }; - redshift.enable = true; blueman.enable = true; libinput.enable = true; xserver = { - enable = true; - xkb.layout = "us"; - xkb.options = "caps:super"; - - displayManager = { - # Give EXWM permission to control the session. - sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localuser:$USER"; - lightdm.enable = true; - # lightdm.greeters.gtk.clock-format = "%H:%M"; # TODO(tazjin): TZ? - }; - - windowManager.session = lib.singleton { - name = "exwm"; - start = "${config.tazjin.emacs}/bin/tazjins-emacs --internal-border=0 --border-width=0"; + enable = true; # wayland doesn't work otherwise ...?! + displayManager.gdm = { + enable = true; + wayland = true; }; - desktopManager.xfce.enable = true; }; }; - # Set variables to enable EXWM-XIM and other Emacs features. - environment.sessionVariables = { - XMODIFIERS = "@im=exwm-xim"; - GTK_IM_MODULE = "xim"; - QT_IM_MODULE = "xim"; - CLUTTER_IM_MODULE = "xim"; - EDITOR = "emacsclient"; - _JAVA_AWT_WM_NONREPARENTING = "1"; - }; + services.displayManager.sessionPackages = [ pkgs.niri ]; + + programs.xwayland.enable = true; + + environment.systemPackages = with pkgs; [ + # core packages + niri + xwayland-satellite + swaylock + + # support tooling + alacritty + fuzzel + kanshi + qt5.qtwayland + swayidle + waybar + wdisplays + wl-clipboard + wl-mirror + wlr-randr + xfce.xfce4-appfinder + depot.users.tazjin.niri-reap + ]; # Do not restart the display manager automatically systemd.services.display-manager.restartIfChanged = lib.mkForce false; + # pipewire MUST start before niri, otherwise screen sharing doesn't work + systemd.user.services.pipewire.wantedBy = [ "niri.service" ]; + systemd.user.services.pipewire.before = [ "niri.service" ]; + + # enable "desktop portals", which are important somehow + xdg.portal = { + enable = true; + extraPortals = with pkgs; [ + xdg-desktop-portal-gtk + xdg-desktop-portal-gnome + ]; + config.common.default = "*"; + }; + + # swaylock needs an empty PAM configuration, otherwise it locks the user out + security.pam.services.swaylock = { }; + + # enable theming support for Qt that is compatible with Chicago95 theme + qt.enable = true; + qt.platformTheme = "qt5ct"; + # If something needs more than 10s to stop it should probably be # killed. systemd.extraConfig = '' diff --git a/users/tazjin/nixos/modules/fonts.nix b/users/tazjin/nixos/modules/fonts.nix index ee1b84e581f1..36b4cbe969ce 100644 --- a/users/tazjin/nixos/modules/fonts.nix +++ b/users/tazjin/nixos/modules/fonts.nix @@ -1,15 +1,17 @@ # Attempt at configuring reasonable font-rendering. -{ pkgs, ... }: +{ depot, pkgs, ... }: { fonts = { packages = with pkgs; [ corefonts dejavu_fonts + font-awesome jetbrains-mono noto-fonts-cjk - noto-fonts-emoji + noto-fonts-color-emoji + noto-fonts-monochrome-emoji ]; fontconfig = { diff --git a/users/tazjin/nixos/modules/home-config.nix b/users/tazjin/nixos/modules/home-config.nix index 77fe3f69bcd2..9aa1cab46d66 100644 --- a/users/tazjin/nixos/modules/home-config.nix +++ b/users/tazjin/nixos/modules/home-config.nix @@ -6,7 +6,7 @@ users.users.tazjin = { isNormalUser = true; createHome = true; - extraGroups = [ "wheel" "networkmanager" "video" "adbusers" ]; + extraGroups = [ "wheel" "networkmanager" "video" "adbusers" "yggdrasil" ]; uid = 1000; shell = pkgs.fish; initialHashedPassword = "$2b$05$1eBPdoIgan/C/L8JFqIHBuVscQyTKw1L/4VBlzlLvLBEf6CXS3EW6"; diff --git a/users/tazjin/nixos/modules/homepage.nix b/users/tazjin/nixos/modules/homepage.nix new file mode 100644 index 000000000000..65191d6e7087 --- /dev/null +++ b/users/tazjin/nixos/modules/homepage.nix @@ -0,0 +1,59 @@ +# serve tazjin's website & blog +{ depot, config, lib, pkgs, ... }: + +let + extraConfig = '' + location = /en/rss.xml { + return 301 https://tazj.in/feed.atom; + } + + ${depot.users.tazjin.blog.oldRedirects} + location /blog/ { + alias ${depot.users.tazjin.blog.rendered}/; + + if ($request_uri ~ ^/(.*)\.html$) { + return 302 /$1; + } + + try_files $uri $uri.html $uri/ =404; + } + + location = /predlozhnik { + return 302 https://predlozhnik.ru; + } + + # redirect for easier entry on a TV + location = /tv { + return 302 https://tazj.in/blobs/play.html; + } + + # Temporary place for serving static files. + location /blobs/ { + alias /var/lib/tazjins-blobs/; + } + ''; +in +{ + config = { + services.nginx.virtualHosts."tazj.in" = { + enableACME = true; + forceSSL = true; + root = depot.users.tazjin.homepage; + serverAliases = [ "www.tazj.in" ]; + inherit extraConfig; + }; + + services.nginx.virtualHosts."y.tazj.in" = { + enableSSL = true; + useACMEHost = "y.tazj.in"; + root = depot.users.tazjin.homepage; + inherit extraConfig; + }; + + services.nginx.virtualHosts."git.tazj.in" = { + enableACME = true; + forceSSL = true; + extraConfig = "return 301 https://code.tvl.fyi$request_uri;"; + }; + }; +} diff --git a/users/tazjin/nixos/modules/persistence.nix b/users/tazjin/nixos/modules/persistence.nix index b864d13a8d70..d3a918025a50 100644 --- a/users/tazjin/nixos/modules/persistence.nix +++ b/users/tazjin/nixos/modules/persistence.nix @@ -14,6 +14,7 @@ "/var/lib/bluetooth" "/var/lib/systemd/coredump" "/var/lib/tailscale" + "/var/lib/private/yggdrasil" "/var/log" ]; diff --git a/users/tazjin/nixos/modules/physical.nix b/users/tazjin/nixos/modules/physical.nix index 5ec527fa7cf7..92b22112d4f4 100644 --- a/users/tazjin/nixos/modules/physical.nix +++ b/users/tazjin/nixos/modules/physical.nix @@ -20,11 +20,12 @@ in environment.systemPackages = # programs from the depot (with depot; [ - users.tazjin.screenLock - users.tazjin.chase-geese config.tazjin.emacs third_party.agenix.cli tools.when + users.tazjin.chase-geese + users.tazjin.eaglemode + users.tazjin.screenLock ]) ++ # programs from nixpkgs @@ -74,6 +75,7 @@ in pulseaudio # for pactl pwgen quasselClient + radicle-node rink ripgrep rustup @@ -100,6 +102,13 @@ in # run manually patchelfed binaries environment.stub-ld.enable = false; + # Enable yggdrasil network. + services.yggdrasil = { + enable = true; + persistentKeys = true; + settings.IfName = "ygg0"; + }; + programs = { fish.enable = true; mosh.enable = true; diff --git a/users/tazjin/nixos/tverskoy/default.nix b/users/tazjin/nixos/tverskoy/default.nix index 733929219a3a..006a27f9f20f 100644 --- a/users/tazjin/nixos/tverskoy/default.nix +++ b/users/tazjin/nixos/tverskoy/default.nix @@ -28,7 +28,7 @@ lib.fix (self: { tvl.cache.enable = true; - boot = rec { + boot = { initrd.availableKernelModules = [ "nvme" "ehci_pci" "xhci_pci" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ]; initrd.kernelModules = [ ]; @@ -38,10 +38,9 @@ lib.fix (self: { ''; # Install thinkpad modules for TLP - extraModulePackages = [ kernelPackages.acpi_call ]; + extraModulePackages = [ pkgs.linuxPackages.acpi_call ]; - kernelModules = [ "kvm-amd" "i2c_dev" ]; - kernelPackages = pkgs.zfsUnstable.latestCompatibleLinuxPackages; + kernelModules = [ "acpi_call" "kvm-amd" "i2c_dev" ]; loader.systemd-boot.enable = true; loader.efi.canTouchEfiVariables = true; }; @@ -93,9 +92,9 @@ lib.fix (self: { enableRedistributableFirmware = true; bluetooth.enable = true; - opengl = { + graphics = { enable = true; - driSupport32Bit = true; + enable32Bit = true; extraPackages = with pkgs; [ vaapiVdpau @@ -164,12 +163,5 @@ lib.fix (self: { # android stuff for hacking on Awful.apk programs.adb.enable = true; - # systemd-oomd seems to have been enabled by default around ~ - # December 2022, and it's really into killing my X session as soon - # as I do anything stressful to the machine - systemd.services.systemd-oomd.enable = lib.mkForce false; - - environment.systemPackages = [ pkgs.vulkan-tools ]; - system.stateVersion = "20.09"; }) diff --git a/users/tazjin/nixos/zamalek/default.nix b/users/tazjin/nixos/zamalek/default.nix index 29effaa9bdd7..ebaf2d2cfa6d 100644 --- a/users/tazjin/nixos/zamalek/default.nix +++ b/users/tazjin/nixos/zamalek/default.nix @@ -71,11 +71,11 @@ in cpu.intel.updateMicrocode = true; bluetooth.enable = true; enableRedistributableFirmware = true; - opengl.enable = true; + graphics.enable = true; }; - services.xserver.libinput.touchpad.clickMethod = "clickfinger"; - services.xserver.libinput.touchpad.tapping = false; + services.libinput.touchpad.clickMethod = "clickfinger"; + services.libinput.touchpad.tapping = false; services.avahi.enable = true; services.tailscale.enable = true; diff --git a/users/tazjin/secrets/lego-yandex.age b/users/tazjin/secrets/lego-yandex.age new file mode 100644 index 000000000000..10524a9577c2 --- /dev/null +++ b/users/tazjin/secrets/lego-yandex.age Binary files differdiff --git a/users/tazjin/secrets/secrets.nix b/users/tazjin/secrets/secrets.nix index 12f12f721c6c..a29bd30b7766 100644 --- a/users/tazjin/secrets/secrets.nix +++ b/users/tazjin/secrets/secrets.nix @@ -13,4 +13,5 @@ in "geesefs-tazjins-files.age".publicKeys = allKeys; "miniflux.age".publicKeys = allKeys; "tgsa-yandex.age".publicKeys = allKeys; + "lego-yandex.age".publicKeys = allKeys; } diff --git a/users/tazjin/wallpapers/alphasoft.webp b/users/tazjin/wallpapers/alphasoft.webp new file mode 100644 index 000000000000..10c404eff0ab --- /dev/null +++ b/users/tazjin/wallpapers/alphasoft.webp Binary files differdiff --git a/users/tazjin/wallpapers/svema_02_big.webp b/users/tazjin/wallpapers/svema_02_big.webp new file mode 100644 index 000000000000..5b7f18715c9d --- /dev/null +++ b/users/tazjin/wallpapers/svema_02_big.webp Binary files differdiff --git a/users/tazjin/wallpapers/svema_07_big.webp b/users/tazjin/wallpapers/svema_07_big.webp new file mode 100644 index 000000000000..0706543473e5 --- /dev/null +++ b/users/tazjin/wallpapers/svema_07_big.webp Binary files differdiff --git a/users/tazjin/wallpapers/svema_09_big.webp b/users/tazjin/wallpapers/svema_09_big.webp new file mode 100644 index 000000000000..4983efef29b8 --- /dev/null +++ b/users/tazjin/wallpapers/svema_09_big.webp Binary files differdiff --git a/users/tazjin/wallpapers/svema_14_big.webp b/users/tazjin/wallpapers/svema_14_big.webp new file mode 100644 index 000000000000..c74542807c29 --- /dev/null +++ b/users/tazjin/wallpapers/svema_14_big.webp Binary files differ |