diff options
Diffstat (limited to 'modules/workstation')
42 files changed, 1350 insertions, 0 deletions
diff --git a/modules/workstation/default.nix b/modules/workstation/default.nix new file mode 100644 index 000000000000..3f4e3f1c8ce7 --- /dev/null +++ b/modules/workstation/default.nix @@ -0,0 +1,16 @@ +{ lib, pkgs, ... }: + +{ + imports = [ + ../nix ../shell ../users ../ssh + ./documentation ./windowing ./fonts ./hardware ./locale + ./dict ./dino ./emacs ./gnupg ./lorri ./mail ./podman ./weechat + ]; + + environment.systemPackages = with pkgs; [ mosh mpv youtube-dl ]; + + services.mingetty.autologinUser = "qyliss"; + services.mingetty.loginOptions = "-- \u"; + services.locate.enable = true; + +} diff --git a/modules/workstation/dict/default.nix b/modules/workstation/dict/default.nix new file mode 100644 index 000000000000..c21e20e947a4 --- /dev/null +++ b/modules/workstation/dict/default.nix @@ -0,0 +1,6 @@ +{ pkgs, ... }: + +{ + services.dictd.enable = true; + services.dictd.DBs = with pkgs.dictdDBs; [ epo2eng ]; +} diff --git a/modules/workstation/dino/default.nix b/modules/workstation/dino/default.nix new file mode 100644 index 000000000000..1c483d4fc473 --- /dev/null +++ b/modules/workstation/dino/default.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: + +{ + environment.systemPackages = with pkgs; [ dino ]; + + home.qyliss.dirs."state/dino" = {}; +} diff --git a/modules/workstation/documentation/default.nix b/modules/workstation/documentation/default.nix new file mode 100644 index 000000000000..ecaad504dda4 --- /dev/null +++ b/modules/workstation/documentation/default.nix @@ -0,0 +1,6 @@ +{ pkgs, ... }: + +{ + environment.systemPackages = with pkgs; + [ glibcInfo gnumake.info man-pages (lowPrio mandoc) ]; +} diff --git a/modules/workstation/emacs/default.el b/modules/workstation/emacs/default.el new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/modules/workstation/emacs/default.el diff --git a/modules/workstation/emacs/default.nix b/modules/workstation/emacs/default.nix new file mode 100644 index 000000000000..a0432bd25ba5 --- /dev/null +++ b/modules/workstation/emacs/default.nix @@ -0,0 +1,14 @@ +{ pkgs, ... }: + +{ + environment.systemPackages = with pkgs; [ + (emacsWithPackages (epkgs: [ + (runCommandNoCC "default.el" {} '' + mkdir -p $out/share/emacs/site-lisp + cp ${./default.el} $out/share/emacs/site-lisp/default.el + '') + ] ++ (with epkgs; [ + magit + ]))) + ]; +} diff --git a/modules/workstation/fonts/default.nix b/modules/workstation/fonts/default.nix new file mode 100644 index 000000000000..ae590859897f --- /dev/null +++ b/modules/workstation/fonts/default.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: + +{ + fonts.fonts = with pkgs; [ iosevka twemoji-color-font noto-fonts-cjk ]; + fonts.fontconfig.allowBitmaps = false; + fonts.fontconfig.defaultFonts.monospace = [ "Iosevka" ]; +} diff --git a/modules/workstation/gnupg/default.nix b/modules/workstation/gnupg/default.nix new file mode 100644 index 000000000000..a93a31411d08 --- /dev/null +++ b/modules/workstation/gnupg/default.nix @@ -0,0 +1,30 @@ +{ pkgs, ... }: + +{ + home.qyliss.dirs."state/gnupg".activationScripts.config = + let + pinentry = if pkgs.stdenv.isDarwin then + "/Applications/pinentry-mac.app/Contents/MacOS/pinentry-mac" + else + "${pkgs.pinentry.qt}/bin/pinentry"; + + gpg-agent-conf = pkgs.writeText "gpg-agent.conf" '' + pinentry-program ${pinentry} + ''; + in '' + ln -sf ${./dirmngr.conf} dirmngr.conf + ln -sf ${./gpg.conf} gpg.conf + ln -sf ${gpg-agent-conf} gpg-agent.conf + ''; + + environment.systemPackages = with pkgs; [ gnupg ]; + + environment.extraInit = '' + export GNUPGHOME="$HOME/state/gnupg" + export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)" + ''; + + programs.sway.extraConfig = '' + exec gpg-connect-agent /bye + ''; +} diff --git a/modules/workstation/gnupg/dirmngr.conf b/modules/workstation/gnupg/dirmngr.conf new file mode 100644 index 000000000000..14114144cc9d --- /dev/null +++ b/modules/workstation/gnupg/dirmngr.conf @@ -0,0 +1 @@ +keyserver hkps://keys.openpgp.org diff --git a/modules/workstation/gnupg/gpg.conf b/modules/workstation/gnupg/gpg.conf new file mode 100644 index 000000000000..ab999bfbc32a --- /dev/null +++ b/modules/workstation/gnupg/gpg.conf @@ -0,0 +1,3 @@ +ask-cert-level +auto-key-retrieve +trust-model tofu+pgp diff --git a/modules/workstation/hardware/default.nix b/modules/workstation/hardware/default.nix new file mode 100644 index 000000000000..3c33ee2b4a1b --- /dev/null +++ b/modules/workstation/hardware/default.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: + +{ + imports = [ ./keyboard ./yubikey ]; + + environment.systemPackages = with pkgs; [ ddrescue ]; + + sound.enable = true; +} diff --git a/modules/workstation/hardware/keyboard/MAPPINGS b/modules/workstation/hardware/keyboard/MAPPINGS new file mode 100644 index 000000000000..60ded39c8cf1 --- /dev/null +++ b/modules/workstation/hardware/keyboard/MAPPINGS @@ -0,0 +1,7 @@ +Key remappings are spread across several different places, because they +have to be done differently depending on the remapping. + +Here is an overview of remapped keys: + +Caps Lock: Escape if pressed, Ctrl if held +Tab: Super-L if pressed, Tab if held diff --git a/modules/workstation/hardware/keyboard/default.nix b/modules/workstation/hardware/keyboard/default.nix new file mode 100644 index 000000000000..092854d535b4 --- /dev/null +++ b/modules/workstation/hardware/keyboard/default.nix @@ -0,0 +1,22 @@ +{ pkgs, config, ... }: + +let + xcfg = config.services.xserver; + +in +{ + console.useXkbConfig = true; + services.xserver.layout = "dvorak"; + services.xserver.xkbOptions = "ctrl:nocaps,compose:menu"; + + environment.variables.XKB_DEFAULT_LAYOUT = xcfg.layout; + environment.variables.XKB_DEFAULT_OPTIONS = xcfg.xkbOptions; + + services.evscript.enable = true; + services.evscript.script = ./events.dyon; + + boot.postBootCommands = '' + # Remap tab to left super + /run/current-system/sw/bin/setkeycodes 0f 125 + ''; +} diff --git a/modules/workstation/hardware/keyboard/events.dyon b/modules/workstation/hardware/keyboard/events.dyon new file mode 100644 index 000000000000..96cc15450e46 --- /dev/null +++ b/modules/workstation/hardware/keyboard/events.dyon @@ -0,0 +1,14 @@ +//! [events] +//! keys = ['ESC', 'TAB'] +fn main() ~ evdevs, uinput { + should_esc := false + should_tab := false + loop { + evts := next_events(evdevs) + for i { + evt := evts[i] + xcape(mut should_esc, evt, KEY_CAPSLOCK(), [KEY_ESC()]) + xcape(mut should_tab, evt, KEY_LEFTMETA(), [KEY_TAB()]) + } + } +} diff --git a/modules/workstation/hardware/yubikey/default.nix b/modules/workstation/hardware/yubikey/default.nix new file mode 100644 index 000000000000..d047246bb20d --- /dev/null +++ b/modules/workstation/hardware/yubikey/default.nix @@ -0,0 +1,13 @@ +{ pkgs, ... }: + +{ + services.udev.packages = with pkgs; [ yubikey-personalization ]; + + security.pam.services.sudo.u2fAuth = true; + security.sudo.extraConfig = '' + Defaults timestamp_timeout=0 + ''; + + security.pam.u2f.cue = true; + security.pam.u2f.authFile = pkgs.copyPathToStore ./u2f_keys; +} diff --git a/modules/workstation/hardware/yubikey/u2f_keys b/modules/workstation/hardware/yubikey/u2f_keys new file mode 100644 index 000000000000..c5601f5f0703 --- /dev/null +++ b/modules/workstation/hardware/yubikey/u2f_keys @@ -0,0 +1 @@ +qyliss:HIWnn91xwo-f14WjdDSdiX2Rs9NdJr2QF4_5_gYUpTkennbpR8AOXHmfEzj5llyLyb-_WEaVUQU59ieCamq9SA,044196971044cff2724ea9ab4624ef860fde32337acc6c3a323899f36a50bf87d06f535c146111f96925455f8c07addff769dfd502c216a9683c70898bb521ada5 diff --git a/modules/workstation/locale/default.nix b/modules/workstation/locale/default.nix new file mode 100644 index 000000000000..8028bd36e271 --- /dev/null +++ b/modules/workstation/locale/default.nix @@ -0,0 +1,6 @@ +{ ... }: + +{ + i18n.defaultLocale = "eo.utf8"; + environment.sessionVariables.LC_CTYPE = "en_GB.utf8"; +} diff --git a/modules/workstation/lorri/default.nix b/modules/workstation/lorri/default.nix new file mode 100644 index 000000000000..4008934dab01 --- /dev/null +++ b/modules/workstation/lorri/default.nix @@ -0,0 +1,10 @@ +{ ... }: + +{ + home.qyliss.dirs."state/lorri" = {}; + + services.lorri.enable = true; + + # FIXME: systemd should have this set globally. + systemd.user.services.lorri.environment.XDG_CACHE_HOME = "/home/state/cache"; +} diff --git a/modules/workstation/mail/default.nix b/modules/workstation/mail/default.nix new file mode 100644 index 000000000000..c53128520812 --- /dev/null +++ b/modules/workstation/mail/default.nix @@ -0,0 +1,47 @@ +{ pkgs, config, ... }: + +let + maildir = "${config.users.users.qyliss.home}/mail"; + mbsyncrc = pkgs.substituteAll { inherit maildir; src = ./mbsyncrc.in; }; + +in + +{ + imports = [ ./mutt ./notmuch ./postfix ./rss2email ]; + + environment.systemPackages = with pkgs; [ isync ]; + + systemd.services.mail = { + path = with pkgs; [ coreutils findutils isync notmuch sudo ]; + serviceConfig.Type = "oneshot"; + after = [ "network-online.target" ]; + script = "sudo -u qyliss-mail mbsync -a -V -c ${mbsyncrc}"; + postStart = '' + find "${maildir}" \! -name .mbsyncstate* \ + \( \( \! -user qyliss -o \! -group qyliss \) , \ + -type f \! -perm 660 -exec chmod 0660 '{}' \; , \ + -type d \! -perm 770 -exec chmod 0770 '{}' \; \) + sudo -u qyliss \ + env NOTMUCH_CONFIG=/etc/xdg/nixos/per-user/qyliss/notmuch/config \ + notmuch new + ''; + }; + + systemd.timers.mail = { + timerConfig.OnCalendar = "*:0/5"; + timerConfig.Persistent = true; + after = [ "network-online.target" ]; + wantedBy = [ "timers.target" ]; + }; + + users.users.qyliss-mail = { + home = "/var/home/qyliss-mail"; + group = "qyliss"; + createHome = true; + }; + + home.qyliss.dirs.mail = { + group = "qyliss"; + permissions = "0770"; + }; +} diff --git a/modules/workstation/mail/mbsyncrc.in b/modules/workstation/mail/mbsyncrc.in new file mode 100644 index 000000000000..987646dd9e66 --- /dev/null +++ b/modules/workstation/mail/mbsyncrc.in @@ -0,0 +1,22 @@ +Create Both + +MaildirStore local + Path @maildir@/ + Inbox @maildir@/INBOX + Subfolders Verbatim + +IMAPAccount fastmail + Host imap.fastmail.com + User alyssa@fastmail.com + PassCmd "cat ~/imappass" + SSLType IMAPS + SSLVersions TLSv1.2 + +IMAPStore fastmail-remote + Account fastmail + +Channel fastmail + Master :fastmail-remote: + Slave :local: + Patterns * + SyncState * diff --git a/modules/workstation/mail/mutt/default.nix b/modules/workstation/mail/mutt/default.nix new file mode 100644 index 000000000000..00ca6e86f4fc --- /dev/null +++ b/modules/workstation/mail/mutt/default.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: + +{ + environment.systemPackages = with pkgs; [ neomutt ]; + + users.users.qyliss.xdg.config.paths."mutt/muttrc" = pkgs.copyPathToStore ./muttrc; + + home.qyliss.dirs."state/mutt/header_cache" = {}; +} diff --git a/modules/workstation/mail/mutt/muttrc b/modules/workstation/mail/mutt/muttrc new file mode 100644 index 000000000000..ed10e60507f8 --- /dev/null +++ b/modules/workstation/mail/mutt/muttrc @@ -0,0 +1,56 @@ +color index red default ~P + +unignore List-Id: +ignore User-Agent: + +set beep = no +set beep_new = yes +set edit_headers = yes +set fast_reply = yes +set folder = ~/mail +set header_cache = ~/state/mutt/header_cache +set help = no +set mark_old = no +set mime_forward = ask-no +set pager = "less -+S" +set quit = ask-yes +set sort = threads +set sort_browser = new +set user_agent = no + +set newsrc = $XDG_DATA_HOME/mutt/newsrc + +unset prompt_after + +set spoolfile = +INBOX + +# set record = "=[Gmail]/Sent Mail" +# set postponed = "=[Gmail]/Drafts" +mailboxes `cd ~/mail; find . -name cur -print0 | sed -z -e 's|^\./||' -e 's|/cur$||' -e 's/\\/\\\\/' -e 's/"/\\"/g' -e 's/^/"=/' -e 's/$/"/' | xargs -0` +set record = "=Sent" +set trash = "=Archive" +set postponed = "=Drafts" +set sendmail = "msmtp" + +set pgp_use_gpg_agent = yes +set crypt_autosign = yes +set crypt_opportunistic_encrypt = yes +set postpone_encrypt = yes + +# Required for postpone_encrypt to work +set pgp_default_key = 757356D779BBB888773E415E736CCDF9EF51BD97 + +set pgp_decode_command = "gpg --status-fd=2 %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --quiet --batch --output - %f" +set pgp_verify_command = "gpg --status-fd=2 --no-verbose --quiet --batch --output - --verify %s %f" +set pgp_decrypt_command = "gpg --status-fd=2 %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --quiet --batch --output - --decrypt %f" +set pgp_sign_command = "gpg %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --batch --quiet --output - --armor --textmode %?a?--local-user %a? --detach-sign %f" +set pgp_clearsign_command = "gpg %?p?--pinentry-mode loopback --passphrase-fd 0? --no-verbose --batch --quiet --output - --armor --textmode %?a?--local-user %a? --clearsign %f" +set pgp_encrypt_only_command = "pgpewrap gpg --trust-model always --batch --quiet --no-verbose --output - --textmode --armor --encrypt -- --recipient %r -- %f" +set pgp_encrypt_sign_command = "pgpewrap gpg %?p?--pinentry-mode loopback --passphrase-fd 0? --trust-model always --batch --quiet --no-verbose --textmode --output - %?a?--local-user %a? --armor --sign --encrypt -- --recipient %r -- %f" +set pgp_import_command = "gpg --no-verbose --import %f" +set pgp_export_command = "gpg --no-verbose --armor --export %r" +set pgp_verify_key_command = "gpg --verbose --batch --fingerprint --check-sigs %r" +set pgp_list_pubring_command = "gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-keys %r" +set pgp_list_secring_command = "gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-secret-keys %r" +set pgp_good_sign = "^\\[GNUPG:\\] GOODSIG +set pgp_decryption_okay = "^\\[GNUPG:\\] DECRYPTION_OKAY" diff --git a/modules/workstation/mail/notmuch/config b/modules/workstation/mail/notmuch/config new file mode 100644 index 000000000000..64c056fa3528 --- /dev/null +++ b/modules/workstation/mail/notmuch/config @@ -0,0 +1,15 @@ +[database] +path=/home/mail + +[user] +other_email=alyssa.ross@freeagent.com; + +[new] +tags=unread;inbox; +ignore=.uidvalidity;.mbsyncstate;.mbsyncstate.new;.mbsyncstate.journal; + +[search] +exclude_tags= + +[maildir] +synchronize_flags=true \ No newline at end of file diff --git a/modules/workstation/mail/notmuch/default.nix b/modules/workstation/mail/notmuch/default.nix new file mode 100644 index 000000000000..46aa17374a0e --- /dev/null +++ b/modules/workstation/mail/notmuch/default.nix @@ -0,0 +1,12 @@ +{ pkgs, ... }: + +{ + environment.extraInit = '' + export NOTMUCH_CONFIG="/etc/xdg/nixos/per-user/$USER/notmuch/config" + ''; + + environment.systemPackages = with pkgs; [ notmuch ]; + + users.users.qyliss.xdg.config.paths."notmuch/config" = + pkgs.copyPathToStore ./config; +} diff --git a/modules/workstation/mail/postfix/default.nix b/modules/workstation/mail/postfix/default.nix new file mode 100644 index 000000000000..0da8936cf1b3 --- /dev/null +++ b/modules/workstation/mail/postfix/default.nix @@ -0,0 +1,32 @@ +{ pkgs, lib, config, ... }: + +{ + services.postfix.enable = true; + + services.postfix.hostname = with lib; with config.networking; + concatStringsSep "." (filter (x: x != null) [ hostName domain ]); + + services.postfix.relayHost = "smtp.fastmail.com"; + services.postfix.relayPort = 465; + + services.postfix.recipientDelimiter = "+"; + services.postfix.config.home_mailbox = "mail/INBOX/"; + services.postfix.virtual = '' + hi@alyssa.is qyliss + ''; + + # NixOS links /var/lib/postfix/conf to /etc/postfix, but + # postfix.service deletes /var/lib/postfix in an ExecStartPre, so we + # can't keep files there without adding them to the store. + # + # Work around this with a layer of symlink indirection. + services.postfix.mapFiles.sasl_passwd = pkgs.runCommand "sasl_passwd" {} '' + ln -s /var/lib/postfix/sasl_passwd $out + ''; + services.postfix.config.smtp_sasl_password_maps = "hash:/etc/postfix/sasl_passwd"; + + services.postfix.config.smtp_sasl_auth_enable = true; + services.postfix.config.smtp_sasl_tls_security_options = "noanonymous"; + services.postfix.config.smtp_tls_security_level = "encrypt"; + services.postfix.config.smtp_tls_wrappermode = true; +} diff --git a/modules/workstation/mail/rss2email/default.nix b/modules/workstation/mail/rss2email/default.nix new file mode 100644 index 000000000000..2e754679fdb2 --- /dev/null +++ b/modules/workstation/mail/rss2email/default.nix @@ -0,0 +1,16 @@ +{ config, ... }: + +{ + services.rss2email.enable = true; + services.rss2email.to = "hi+rss2email@alyssa.is"; + services.rss2email.config.date-header = true; + services.rss2email.config.from = + "rss2email@${config.services.postfix.hostname}"; + + services.rss2email.feeds = { + fading-memories = { url = "https://valdyas.org/fading/feed/"; }; + flak = { url = "https://flak.tedunangst.com/rss"; }; + wandering-thoughts = + { url = "https://utcc.utoronto.ca/~cks/space/blog/?atom"; }; + }; +} diff --git a/modules/workstation/networking/default.nix b/modules/workstation/networking/default.nix new file mode 100644 index 000000000000..3176e8d061c0 --- /dev/null +++ b/modules/workstation/networking/default.nix @@ -0,0 +1,46 @@ +{ pkgs, config, ... }: + +{ + services.resolved.enable = true; + + networking.domain = "qyliss.net"; + networking.hosts = with config.networking; + { "127.0.1.1" = [ "${hostName}.${domain}" ]; }; + + networking.networkmanager.enable = true; + + users.users.qyliss.extraGroups = [ "networkmanager" ]; + + # Plausible MAC randomization + networking.networkmanager.ethernet.macAddress = "random"; + networking.networkmanager.wifi.macAddress = "random"; + networking.networkmanager.extraConfig = '' + [connection-extra] + ethernet.generate-mac-address-mask=FE:FF:FF:00:00:00 + wifi.generate-mac-address-mask=FE:FF:FF:00:00:00 + ''; + + networking.nameservers = [ "::1" ]; + + networking.networkmanager.dispatcherScripts = [ + { + source = pkgs.writeText "doh-stub" '' + if [ "$2" = up ] + then systemctl restart doh-stub.service + fi + ''; + type = "basic"; + } + ]; + + systemd.services.doh-stub = { + script = '' + exec ${pkgs.doh-proxy}/bin/doh-stub \ + --level INFO \ + --domain qyliss.net \ + --remote-address 85.119.82.108 + ''; + }; + + programs.mtr.enable = true; +} diff --git a/modules/workstation/physical/default.nix b/modules/workstation/physical/default.nix new file mode 100644 index 000000000000..173dbbbb517f --- /dev/null +++ b/modules/workstation/physical/default.nix @@ -0,0 +1,7 @@ +{ ... }: + +{ + imports = [ ../. ../networking ]; + + programs.swayidle.enable = true; +} diff --git a/modules/workstation/podman/default.nix b/modules/workstation/podman/default.nix new file mode 100644 index 000000000000..aefad1f9d97f --- /dev/null +++ b/modules/workstation/podman/default.nix @@ -0,0 +1,47 @@ +{ pkgs, ... }: + +{ + environment.etc."containers/libpod.conf".text = '' + runtime_path = ["${pkgs.runc}/bin/runc"] + conmon_path = ["${pkgs.conmon}/bin/conmon"] + ''; + + environment.etc."containers/policy.json".text = builtins.toJSON { + # Not insecure when I'm manually pulling images on a workstation. + default = [ { type = "insecureAcceptAnything"; } ]; + }; + + environment.etc."containers/registries.conf".text = '' + [registries.search] + registries = ['docker.io'] + ''; + + environment.systemPackages = with pkgs; + let + podman-bin = writeShellScriptBin "podman" '' + HOME="$XDG_CONFIG_HOME/podman" + exec ${podman}/bin/podman "$@" + ''; + in + [ podman-bin podman.man runc conmon slirp4netns ]; + + users.users.qyliss.xdg.config.paths."podman/.config/containers/libpod.conf" = + pkgs.writeText "libpod.conf" '' + runtime_path = ["${pkgs.runc}/bin/runc"] + conmon_path = ["${pkgs.conmon}/bin/conmon"] + ''; + + users.users.qyliss.xdg.config.paths."podman/.config/containers/storage.conf" = + pkgs.writeText "storage.conf" '' + [storage] + driver = "overlay" + runroot = "/tmp/1000" + graphroot = "/home/state/podman/containers/storage" + + [storage.options] + mount_program = "${pkgs.fuse-overlayfs}/bin/fuse-overlayfs" + ''; + + home.qyliss.dirs."state/containers" = {}; + home.qyliss.dirs."state/podman" = {}; +} diff --git a/modules/workstation/weechat/default.nix b/modules/workstation/weechat/default.nix new file mode 100644 index 000000000000..4298122a4e0b --- /dev/null +++ b/modules/workstation/weechat/default.nix @@ -0,0 +1,121 @@ +{ pkgs, lib, ... }: + +with lib; + +let + networks = [ "freenode" "hackint" "indymedia" "oftc" ]; + + toWeeChat = value: + /**/ if value == true then "on" + else if value == false then "off" + else if isList value then concatStringsSep "," value + else toString value; + + sec = [ "znc.username" "znc.password" "slack.api_tokens" ]; + + cfgin = { + alias.cmd.B = "buffer"; + irc.look.buffer_switch_autojoin = false; + irc.look.display_join_message = false; + irc.look.server_buffer = "independent"; + irc.look.temporary_servers = true; + + irc.server_default = { + addresses = "znc.qyliss.net/6697"; + autoconnect = true; + capabilities = [ "account-notify" "away-notify" "cap-notify" "multi-prefix" "server-time" "znc.in/server-time-iso" "znc.in/self-message" "znc.in/playback" ]; + nicks = "qyliss"; + password = "\\\${sec.data.znc.password}"; + ssl = true; + username = "\\\${sec.data.znc.username}"; + }; + + irc.server = genAttrs networks (s: + { username = "'\\\${sec.data.znc.username}/${s}'"; }); + plugins.var.python.zncplayback.servers = networks; + + logger.color.backlog_end = "*default"; + logger.look.backlog = 200; + + plugins.var.python.slack.slack_api_token = "\\\${sec.data.slack.api_tokens}"; + plugins.var.python.slack.auto_open_threads = true; + plugins.var.python.slack.background_load_all_history = true; + plugins.var.python.slack.short_buffer_names = true; + plugins.var.python.slack.show_reaction_nicks = true; + plugins.var.python.vimode.no_warn = true; + + script.look.sort = "p,n"; + + weechat.bar.buflist.hidden = true; + weechat.bar.fset.items = "fset"; + weechat.bar.input.items = "[mode_indicator]+ [input_prompt]+(away),[input_search],[input_paste],input_text,[vi_buffer]"; + weechat.bar.status.color_bg = 53; + weechat.bar.status.items = "[otr],[buffer_plugin],buffer_name+(buffer_modes)+{buffer_nicklist_count}+buffer_zoom+buffer_filter,scroll,[lag],[hotlist],completion"; + weechat.bar.title.color_bg = 53; + + weechat.color.chat_nick_colors = [ "cyan" "magenta" "green" "brown" "lightblue" "lightcyan" "lightmagenta" "lightgreen" "blue" ]; + weechat.color.chat_nick_self = "default"; + weechat.color.status_count_other = "white"; + weechat.color.status_data_other = "white"; + weechat.color.status_nicklist_count = "white"; + weechat.color.status_time = "white"; + + weechat.completion.default_template = "%(nicks)|%(irc_channels)|%(emoji)"; + weechat.completion.nick_completer = ":"; + + weechat.look.buffer_notify_default = "message"; + weechat.look.highlight = [ "spectrum" "crosvm" "qyliss" "alyssa*" "*dntmissher" "*@alyssa" ]; + weechat.look.hotlist_names_count = 10; + weechat.look.hotlist_names_level = 14; + weechat.look.mouse = true; + weechat.look.prefix_align_max = 12; + weechat.look.save_config_on_exit = false; + weechat.look.window_title = "WeeChat \\\${info:version}"; + }; + + commands = + map (d: ''/set sec.data.${d} test'') sec ++ [ "/save" ] ++ + map (n: "/server add ${n} ${cfgin.irc.server_default.addresses}") networks ++ + [ "/ignore add osmbot-test oftc #osm-gb" ] ++ + mapAttrsToList (name: value: "/set ${name} ${toWeeChat value}") + (flattenAttrs cfgin); + + flattenAttrs' = sep: attrs: + listToAttrs (concatLists (flip mapAttrsToList attrs (k: v: + if isAttrs v then mapAttrsToList (k': nameValuePair "${k}${sep}${k'}") (flattenAttrs' sep v) + else [ (nameValuePair k v) ]))); + + flattenAttrs = flattenAttrs' "."; + + cfg = pkgs.runCommand "weechat-config" {} '' + ${pkgs.weechat}/bin/weechat-headless -d $out \ + ${concatMapStringsSep " " (cmd: "-r ${escapeShellArg cmd}") commands} \ + -r /save \ + -r /exit + ''; + +in + +{ + environment.extraInit = '' + export WEECHAT_HOME="$HOME/state/weechat" + ''; + + environment.systemPackages = with pkgs; [ + (weechat.override { + configure = { availablePlugins, ... }: { + scripts = with weechatScripts; + [ colorize_nicks go wee-slack zncplayback ]; + }; + }) + ]; + + home.qyliss.dirs."state/weechat".activationScripts.config = '' + for file in ${cfg}/*.conf + do + if [ "$file" != ${cfg}/sec.conf ] + then ln -sf $file $(basename $file) + fi + done + ''; +} diff --git a/modules/workstation/windowing/alacritty/config.yml b/modules/workstation/windowing/alacritty/config.yml new file mode 100644 index 000000000000..86aa7ed5622c --- /dev/null +++ b/modules/workstation/windowing/alacritty/config.yml @@ -0,0 +1,340 @@ +# Any items in the `env` entry below will be added as +# environment variables. Some entries may override variables +# set by alacritty itself. +#env: + # TERM variable + # + # This value is used to set the `$TERM` environment variable for + # each instance of Alacritty. If it is not present, alacritty will + # check the local terminfo database and use 'alacritty' if it is + # available, otherwise 'xterm-256color' is used. + #TERM: xterm-256color + +window: + # If both are `0`, this setting is ignored. + dimensions: + columns: 80 + lines: 24 + + padding: + x: 2 + y: 2 + + dynamic_padding: true + decorations: none + startup_mode: Windowed + +scrolling: + history: 0 # disabled + +font: + normal: + family: monospace + #style: Regular + + bold: + family: monospace + #style: Bold + + italic: + family: monospace + #style: Italic + + offset: + x: 0 + y: 0 + + glyph_offset: + x: 0 + y: 0 + +draw_bold_text_with_bright_colors: false + +colors: + primary: + background: '0x000000' + foreground: '0xffffff' + # dim_foreground: auto + # bright_foreground: normal foreground color + + cursor: + text: '0x161616' + cursor: '0xfcdc07' + + normal: + black: '0x2e3436' + red: '0xb40000' + green: '0x307000' + yellow: '0xce5c00' + blue: '0x3465a4' + magenta: '0x75507b' + cyan: '0x06989a' + white: '0xd3d7cf' + + bright: + black: '0x555753' + red: '0xcc0000' + green: '0x4e9a06' + yellow: '0xedd400' + blue: '0x729fcf' + magenta: '0xad7fa8' + cyan: '0x34e2e2' + white: '0xeeeeec' + + # dim: default + # indexed_colors: default + +# Values for `animation`: +# - Ease +# - EaseOut +# - EaseOutSine +# - EaseOutQuad +# - EaseOutCubic +# - EaseOutQuart +# - EaseOutQuint +# - EaseOutExpo +# - EaseOutCirc +# - Linear +# +# Specifying a `duration` of `0` will disable the visual bell. +visual_bell: + animation: EaseOutExpo + duration: 1 + +background_opacity: 0.8 + +# Available fields: +# - mouse +# - action +# - mods (optional) +# +# Values for `mouse`: +# - Middle +# - Left +# - Right +# - Numeric identifier such as `5` +# +# All available `mods` and `action` values are documented in the key binding +# section. +mouse_bindings: + - { mouse: Middle, action: PasteSelection } + +mouse: + double_click: { threshold: 300 } + triple_click: { threshold: 300 } + hide_when_typing: false + + url: + launcher: None + +selection: + semantic_escape_chars: ",│`|:\"' ()[]{}<>" + + # When set to `true`, selected text will be copied to both the primary and + # the selection clipboard. Otherwise, it will only be copied to the selection + # clipboard. + save_to_clipboard: false + +dynamic_title: true + +cursor: + style: Block + unfocused_hollow: true + +live_config_reload: true + +# Shell +# +# You can set `shell.program` to the path of your favorite shell, e.g. `/bin/fish`. +# Entries in `shell.args` are passed unmodified as arguments to the shell. +#shell: +# program: /bin/bash +# args: +# - --login + +# Key bindings +# +# Key bindings are specified as a list of objects. Each binding will specify +# a key and modifiers required to trigger it, terminal modes where the binding +# is applicable, and what should be done when the key binding fires. It can +# either send a byte sequnce to the running application (`chars`), execute +# a predefined action (`action`) or fork and execute a specified command plus +# arguments (`command`). +# +# Example: +# `- { key: V, mods: Command, action: Paste }` +# +# Available fields: +# - key +# - mods (optional) +# - chars | action | command (exactly one required) +# - mode (optional) +# +# Values for `key`: +# - `A` -> `Z` +# - `F1` -> `F12` +# - `Key1` -> `Key0` +# +# A full list with available key codes can be found here: +# https://docs.rs/glutin/*/glutin/enum.VirtualKeyCode.html#variants +# +# Instead of using the name of the keys, the `key` field also supports using +# the scancode of the desired key. Scancodes have to be specified as a +# decimal number. +# This command will allow you to display the hex scancodes for certain keys: +# `showkey --scancodes` +# +# Values for `mods`: +# - Command +# - Control +# - Shift +# - Alt +# +# Multiple `mods` can be combined using `|` like this: `mods: Control|Shift`. +# Whitespace and capitalization is relevant and must match the example. +# +# Values for `chars`: +# The `chars` field writes the specified string to the terminal. This makes +# it possible to pass escape sequences. +# To find escape codes for bindings like `PageUp` ("\x1b[5~"), you can run +# the command `showkey -a` outside of tmux. +# Note that applications use terminfo to map escape sequences back to +# keys. It is therefore required to update the terminfo when +# changing an escape sequence. +# +# Values for `action`: +# - Paste +# - PasteSelection +# - Copy +# - IncreaseFontSize +# - DecreaseFontSize +# - ResetFontSize +# - ScrollPageUp +# - ScrollPageDown +# - ScrollToTop +# - ScrollToBottom +# - ClearHistory +# - Hide +# - Quit +# - ClearLogNotice +# +# Values for `command`: +# The `command` field must be a map containing a `program` string and +# an `args` array of command line parameter strings. +# +# Example: +# `command: { program: "alacritty", args: ["-e", "vttest"] }` +# +# Values for `mode`: +# - ~AppCursor +# - AppCursor +# - ~AppKeypad +# - AppKeypad +key_bindings: + - { key: V, mods: Control|Shift, action: Paste } + - { key: C, mods: Control|Shift, action: Copy } + - { key: Paste, action: Paste } + - { key: Copy, action: Copy } + - { key: Q, mods: Command, action: Quit } + - { key: W, mods: Command, action: Quit } + - { key: Insert, mods: Shift, action: PasteSelection } + - { key: Key0, mods: Control, action: ResetFontSize } + - { key: Equals, mods: Control, action: IncreaseFontSize } + - { key: Subtract, mods: Control, action: DecreaseFontSize } + - { key: L, mods: Control, action: ClearLogNotice } + - { key: L, mods: Control, chars: "\x0c" } + - { key: Home, chars: "\x1bOH", mode: AppCursor } + - { key: Home, chars: "\x1b[H", mode: ~AppCursor } + - { key: End, chars: "\x1bOF", mode: AppCursor } + - { key: End, chars: "\x1b[F", mode: ~AppCursor } + - { key: PageUp, mods: Shift, chars: "\x1b[5;2~" } + - { key: PageUp, mods: Control, chars: "\x1b[5;5~" } + - { key: PageUp, chars: "\x1b[5~" } + - { key: PageDown, mods: Shift, chars: "\x1b[6;2~" } + - { key: PageDown, mods: Control, chars: "\x1b[6;5~" } + - { key: PageDown, chars: "\x1b[6~" } + - { key: Tab, mods: Shift, chars: "\x1b[Z" } + - { key: Back, chars: "\x7f" } + - { key: Back, mods: Alt, chars: "\x1b\x7f" } + - { key: Insert, chars: "\x1b[2~" } + - { key: Delete, chars: "\x1b[3~" } + - { key: Left, mods: Shift, chars: "\x1b[1;2D" } + - { key: Left, mods: Control, chars: "\x1b[1;5D" } + - { key: Left, mods: Alt, chars: "\x1b[1;3D" } + - { key: Left, chars: "\x1b[D", mode: ~AppCursor } + - { key: Left, chars: "\x1bOD", mode: AppCursor } + - { key: Right, mods: Shift, chars: "\x1b[1;2C" } + - { key: Right, mods: Control, chars: "\x1b[1;5C" } + - { key: Right, mods: Alt, chars: "\x1b[1;3C" } + - { key: Right, chars: "\x1b[C", mode: ~AppCursor } + - { key: Right, chars: "\x1bOC", mode: AppCursor } + - { key: Up, mods: Shift, chars: "\x1b[1;2A" } + - { key: Up, mods: Control, chars: "\x1b[1;5A" } + - { key: Up, mods: Alt, chars: "\x1b[1;3A" } + - { key: Up, chars: "\x1b[A", mode: ~AppCursor } + - { key: Up, chars: "\x1bOA", mode: AppCursor } + - { key: Down, mods: Shift, chars: "\x1b[1;2B" } + - { key: Down, mods: Control, chars: "\x1b[1;5B" } + - { key: Down, mods: Alt, chars: "\x1b[1;3B" } + - { key: Down, chars: "\x1b[B", mode: ~AppCursor } + - { key: Down, chars: "\x1bOB", mode: AppCursor } + - { key: F1, chars: "\x1bOP" } + - { key: F2, chars: "\x1bOQ" } + - { key: F3, chars: "\x1bOR" } + - { key: F4, chars: "\x1bOS" } + - { key: F5, chars: "\x1b[15~" } + - { key: F6, chars: "\x1b[17~" } + - { key: F7, chars: "\x1b[18~" } + - { key: F8, chars: "\x1b[19~" } + - { key: F9, chars: "\x1b[20~" } + - { key: F10, chars: "\x1b[21~" } + - { key: F11, chars: "\x1b[23~" } + - { key: F12, chars: "\x1b[24~" } + - { key: F1, mods: Shift, chars: "\x1b[1;2P" } + - { key: F2, mods: Shift, chars: "\x1b[1;2Q" } + - { key: F3, mods: Shift, chars: "\x1b[1;2R" } + - { key: F4, mods: Shift, chars: "\x1b[1;2S" } + - { key: F5, mods: Shift, chars: "\x1b[15;2~" } + - { key: F6, mods: Shift, chars: "\x1b[17;2~" } + - { key: F7, mods: Shift, chars: "\x1b[18;2~" } + - { key: F8, mods: Shift, chars: "\x1b[19;2~" } + - { key: F9, mods: Shift, chars: "\x1b[20;2~" } + - { key: F10, mods: Shift, chars: "\x1b[21;2~" } + - { key: F11, mods: Shift, chars: "\x1b[23;2~" } + - { key: F12, mods: Shift, chars: "\x1b[24;2~" } + - { key: F1, mods: Control, chars: "\x1b[1;5P" } + - { key: F2, mods: Control, chars: "\x1b[1;5Q" } + - { key: F3, mods: Control, chars: "\x1b[1;5R" } + - { key: F4, mods: Control, chars: "\x1b[1;5S" } + - { key: F5, mods: Control, chars: "\x1b[15;5~" } + - { key: F6, mods: Control, chars: "\x1b[17;5~" } + - { key: F7, mods: Control, chars: "\x1b[18;5~" } + - { key: F8, mods: Control, chars: "\x1b[19;5~" } + - { key: F9, mods: Control, chars: "\x1b[20;5~" } + - { key: F10, mods: Control, chars: "\x1b[21;5~" } + - { key: F11, mods: Control, chars: "\x1b[23;5~" } + - { key: F12, mods: Control, chars: "\x1b[24;5~" } + - { key: F1, mods: Alt, chars: "\x1b[1;6P" } + - { key: F2, mods: Alt, chars: "\x1b[1;6Q" } + - { key: F3, mods: Alt, chars: "\x1b[1;6R" } + - { key: F4, mods: Alt, chars: "\x1b[1;6S" } + - { key: F5, mods: Alt, chars: "\x1b[15;6~" } + - { key: F6, mods: Alt, chars: "\x1b[17;6~" } + - { key: F7, mods: Alt, chars: "\x1b[18;6~" } + - { key: F8, mods: Alt, chars: "\x1b[19;6~" } + - { key: F9, mods: Alt, chars: "\x1b[20;6~" } + - { key: F10, mods: Alt, chars: "\x1b[21;6~" } + - { key: F11, mods: Alt, chars: "\x1b[23;6~" } + - { key: F12, mods: Alt, chars: "\x1b[24;6~" } + - { key: F1, mods: Super, chars: "\x1b[1;3P" } + - { key: F2, mods: Super, chars: "\x1b[1;3Q" } + - { key: F3, mods: Super, chars: "\x1b[1;3R" } + - { key: F4, mods: Super, chars: "\x1b[1;3S" } + - { key: F5, mods: Super, chars: "\x1b[15;3~" } + - { key: F6, mods: Super, chars: "\x1b[17;3~" } + - { key: F7, mods: Super, chars: "\x1b[18;3~" } + - { key: F8, mods: Super, chars: "\x1b[19;3~" } + - { key: F9, mods: Super, chars: "\x1b[20;3~" } + - { key: F10, mods: Super, chars: "\x1b[21;3~" } + - { key: F11, mods: Super, chars: "\x1b[23;3~" } + - { key: F12, mods: Super, chars: "\x1b[24;3~" } diff --git a/modules/workstation/windowing/alacritty/default.nix b/modules/workstation/windowing/alacritty/default.nix new file mode 100644 index 000000000000..e8d086f54d8c --- /dev/null +++ b/modules/workstation/windowing/alacritty/default.nix @@ -0,0 +1,11 @@ +{ pkgs, ... }: + +{ + imports = [ ../../../xdg ]; + + environment.systemPackages = with pkgs; + lib.optional (!stdenv.isDarwin) alacritty; + + users.users.qyliss.xdg.config.paths."alacritty/alacritty.yml" = + pkgs.copyPathToStore ./config.yml; +} diff --git a/modules/workstation/windowing/default.nix b/modules/workstation/windowing/default.nix new file mode 100644 index 000000000000..8387f1981ac4 --- /dev/null +++ b/modules/workstation/windowing/default.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: + +{ + imports = [ ./alacritty ./firefox ./gnome-mines ./sway ]; + + environment.systemPackages = with pkgs; [ imv wf-recorder ]; +} diff --git a/modules/workstation/windowing/firefox/default.nix b/modules/workstation/windowing/firefox/default.nix new file mode 100644 index 000000000000..877f0ef0892c --- /dev/null +++ b/modules/workstation/windowing/firefox/default.nix @@ -0,0 +1,13 @@ +{ pkgs, ... }: + +{ + home.qyliss.dirs.state.activationScripts.profile = '' + install -o qyliss -g users -d mozilla{,/firefox{,/default}} + ln -sf ${./profiles.ini} mozilla/firefox/profiles.ini + ln -sf ${./user.js} mozilla/firefox/default/user.js + ''; + + environment.systemPackages = with pkgs; [ firefox-wayland ]; + + environment.variables.BROWSER = "firefox"; +} diff --git a/modules/workstation/windowing/firefox/profiles.ini b/modules/workstation/windowing/firefox/profiles.ini new file mode 100644 index 000000000000..becf53354e76 --- /dev/null +++ b/modules/workstation/windowing/firefox/profiles.ini @@ -0,0 +1,8 @@ +[General] +StartWithLastProfile=1 + +[Profile0] +Name=default +IsRelative=1 +Path=default +Default=1 diff --git a/modules/workstation/windowing/firefox/user.js b/modules/workstation/windowing/firefox/user.js new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/modules/workstation/windowing/firefox/user.js @@ -0,0 +1 @@ + diff --git a/modules/workstation/windowing/gnome-mines/default.nix b/modules/workstation/windowing/gnome-mines/default.nix new file mode 100644 index 000000000000..a2376676d007 --- /dev/null +++ b/modules/workstation/windowing/gnome-mines/default.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: + +{ + environment.systemPackages = with pkgs; [ gnome3.gnome-mines ]; + + home.qyliss.dirs."state/gnome-mines" = {}; +} diff --git a/modules/workstation/windowing/sway/choose_workspace.sh.in b/modules/workstation/windowing/sway/choose_workspace.sh.in new file mode 100644 index 000000000000..963746e0c810 --- /dev/null +++ b/modules/workstation/windowing/sway/choose_workspace.sh.in @@ -0,0 +1,17 @@ +#! @shell@ -ue +swaymsg -t get_workspaces | + @jq@/bin/jq -r \ + '(to_entries | map(select(.value.focused)) | .[0].key), .[].name' | + ( + read index + exec @bemenu@/bin/bemenu \ + -p workspace \ + -I "$index" \ + -H 24 \ + --fn 'monospace 10' \ + --nf '#777777' \ + --hb '#285577' \ + --hf '#ffffff' \ + --tf '#777777' \ + --ff '#ffffff' + ) diff --git a/modules/workstation/windowing/sway/config.in b/modules/workstation/windowing/sway/config.in new file mode 100644 index 000000000000..6770080462af --- /dev/null +++ b/modules/workstation/windowing/sway/config.in @@ -0,0 +1,110 @@ +set $mod Mod4 +set $left h +set $down j +set $up k +set $right l + +default_border pixel +default_floating_border normal + +client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a00 +client.unfocused #333333 #222222 #888888 #292d2e #22222200 + +for_window [app_id="float"] floating enable +for_window [class="Tor Browser"] floating enable +for_window [class="XDvi"] floating enable +for_window [instance="xdvi"] floating enable +for_window [class="XDvi" instance="xdvi"] floating disable + +# Stop the Firefox sharing indicator (that appears when on an +# audio/video call) tiling, or appearing in the center of the display, +# or being focused. +no_focus [title="Firefox - Sharing Indicator"] +for_window [title="Firefox - Sharing Indicator"] { + floating enable + sticky enable + + # I'd really like this to be at the top, horizontally centered. + # Or maybe at the bottom right. But there's not really a way to + # do that in sway configuration, as far as I can tell. I think + # I'd need to exec a program that would measure the display size + # and compute the coordinates or something. That sounds horrible + # and fragile, so top left it is. + move position 0 0 +} + +input * natural_scroll enabled +output * bg @wallpaper@ fill + +bindsym $mod+Return exec alacritty +bindsym $mod+backslash exec firefox +bindsym $mod+Shift+q kill +bindsym $mod+d exec swaymsg exec "$(choosebin --tiebreak=begin,length,index)" + +# Drag floating windows by holding down $mod and left mouse button. +# Resize them with right mouse button + $mod. +# Despite the name, also works for non-floating windows. +# Change normal to inverse to use left mouse button for resizing and right +# mouse button for dragging. +floating_modifier $mod normal + +# reload the configuration file +bindsym $mod+Shift+c reload + +# exit sway (logs you out of your Wayland session) +bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit' + +bindsym $mod+$left focus left +bindsym $mod+$down focus down +bindsym $mod+$up focus up +bindsym $mod+$right focus right + +bindsym $mod+Shift+$left move left +bindsym $mod+Shift+$down move down +bindsym $mod+Shift+$up move up +bindsym $mod+Shift+$right move right + +bindsym $mod+g exec swaymsg workspace "$(@choose_workspace@)" +bindsym $mod+Shift+g exec swaymsg move container to workspace "$(@choose_workspace@)" + +bindsym $mod+b splith +bindsym $mod+v splitv + +bindsym $mod+s layout stacking +bindsym $mod+w layout tabbed +bindsym $mod+e layout toggle split + +bindsym $mod+f fullscreen + +bindsym $mod+Shift+space floating toggle +bindsym $mod+space focus mode_toggle + +bindsym $mod+a focus parent + +bindsym $mod+Shift+minus move scratchpad +bindsym $mod+minus scratchpad show + +mode "resize" { + bindsym $left resize shrink width 10px + bindsym $down resize grow height 10px + bindsym $up resize shrink height 10px + bindsym $right resize grow width 10px + + bindsym Return mode "default" + bindsym Escape mode "default" +} +bindsym $mod+r mode "resize" + +bar { + position top + + status_command @status_command@ + + colors { + statusline #ffffff + background #00000077 + inactive_workspace #33333377 #00000077 #FFFFFF77 + } +} + +@extraConfig@ diff --git a/modules/workstation/windowing/sway/default.nix b/modules/workstation/windowing/sway/default.nix new file mode 100644 index 000000000000..5901639d1baa --- /dev/null +++ b/modules/workstation/windowing/sway/default.nix @@ -0,0 +1,52 @@ +{ pkgs, lib, config, ... }: + +{ + imports = [ ./swayidle ]; + + options = { + programs.sway.extraConfig = with lib; mkOption { + type = types.lines; + description = "Lines to append to sway's config file"; + default = ""; + }; + }; + + config = let + cfg = config.programs.sway; + + wallpaper = pkgs.fetchurl { + url = https://mir-s3-cdn-cf.behance.net/project_modules/2800_opt_1/36731876964505.5c793fa788b5d.jpg; + sha256 = "1c6camdipng8ws41sgpcxzrxb96crgip3wirqjgf2ajn60qg3v64"; + + meta = with lib; { + homepage = https://www.behance.net/gallery/76964505/IQOO-style-frame-and-scene-design; + }; + }; + + configFile = pkgs.substituteAll { + src = ./config.in; + inherit choose_workspace status_command wallpaper; + inherit (cfg) extraConfig; + }; + + status_command = pkgs.runCommandCC "status" {} '' + c++ -std=c++17 -o $out ${./status.cpp} + ''; + + choose_workspace = pkgs.substituteAll { + src = ./choose_workspace.sh.in; + isExecutable = true; + inherit (pkgs) bemenu jq; + }; + + in + + { + environment.systemPackages = with pkgs; [ bemenu choose ]; + + programs.sway.enable = true; + programs.swayidle.enable = true; + + users.users.qyliss.xdg.config.paths."sway/config" = configFile; + }; +} diff --git a/modules/workstation/windowing/sway/status.cpp b/modules/workstation/windowing/sway/status.cpp new file mode 100644 index 000000000000..71acb38eeb70 --- /dev/null +++ b/modules/workstation/windowing/sway/status.cpp @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +// Copyright 2020 Alyssa Ross +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +#include <chrono> +#include <fcntl.h> +#include <filesystem> +#include <iostream> +#include <thread> +#include <unistd.h> + +namespace fs = std::filesystem; + +using fs::directory_iterator; +using std::chrono::seconds; +using std::generic_category; +using std::put_time; +using std::stoi; +using std::string; +using std::stringstream; +using std::system_error; +using std::this_thread::sleep_for; +using std::time; + +enum BatteryStatus { + Unknown, + Charging, + Discharging, + NotCharging, + Full, +}; + +class Battery { +public: + Battery(string name); + + string name() { return m_name; } + fs::path path(); + BatteryStatus status(); + int capacity(); + +private: + string m_name; + string attr(string name); +}; + +Battery::Battery(string name) +{ + m_name = name; +} + +fs::path Battery::path() +{ + static fs::path base = "/sys/class/power_supply"; + return base / name(); +} + +BatteryStatus Battery::status() +{ + auto status = attr("status"); + + if (status == "Charging") + return Charging; + if (status == "Discharging") + return Discharging; + if (status == "Not charging") + return NotCharging; + if (status == "Full") + return Full; + + return Unknown; +} + +int Battery::capacity() +{ + return stoi(attr("capacity")); +} + +string Battery::attr(string name) +{ + // Use read() to make sure this is done in a single read syscall, + // because sysfs doesn't like multiple reads. + int fd = open((path() / name).c_str(), O_RDONLY); + if (fd == -1) + throw system_error(errno, generic_category()); + char buf[13]; + int len = read(fd, &buf, 13); + if (len == -1) + throw system_error(errno, generic_category()); + close(fd); + string capacity (buf, len); + if (capacity.back() == '\n') + capacity.pop_back(); + return capacity; +} + +int main(void) +{ + while (true) { + // Buffer output so it can be done all at once in a single write(), + // because of <https://github.com/swaywm/sway/issues/3857>. + stringstream out; + + for (const auto& entry : directory_iterator("/sys/class/power_supply")) { + auto name = entry.path().filename().string(); + if (name.find("BAT") != 0) + continue; + + Battery battery (name); + BatteryStatus status; + int capacity; + + try { + status = battery.status(); + capacity = battery.capacity(); + } catch (const system_error& ex) { + if (ex.code().value() == ENOENT) + continue; + + throw ex; + } + + switch (status) { + case Charging: + out << "↑"; + break; + case Discharging: + out << "↓"; + break; + default: + out << " "; + } + + out << capacity << "% "; + } + + auto t = time(nullptr); + out << put_time(localtime(&t), "%F %T") << "\n"; + + auto buf = out.str(); + if (write(STDOUT_FILENO, buf.c_str(), buf.length()) == -1) + perror("write"); + + sleep_for(seconds(1)); + } +} diff --git a/modules/workstation/windowing/sway/swayidle/default.nix b/modules/workstation/windowing/sway/swayidle/default.nix new file mode 100644 index 000000000000..8e5f264b5038 --- /dev/null +++ b/modules/workstation/windowing/sway/swayidle/default.nix @@ -0,0 +1,23 @@ +{ lib, config, ... }: + +let + cfg = config.programs.swayidle; +in + +with lib; + +{ + options = { + programs.swayidle.enable = mkEnableOption "swayidle"; + }; + + config = mkIf cfg.enable { + programs.sway.extraConfig = '' + exec swayidle \ + timeout 300 'swaylock -c 000000' \ + timeout 600 'swaymsg "output * dpms off"' \ + resume 'swaymsg "output * dpms on"' \ + before-sleep 'swaylock -c 000000' + ''; + }; +} |