diff --git a/nixos-config/config/gtk-2.0/gtkrc b/nixos-config/config/gtk-2.0/gtkrc new file mode 100644 index 00000000..5e485ae3 --- /dev/null +++ b/nixos-config/config/gtk-2.0/gtkrc @@ -0,0 +1,15 @@ +gtk-theme-name="Sweet-Dark" +#gtk-icon-theme-name="Sweet-Rainbow" +gtk-font-name="Fira Sans 12" +gtk-cursor-theme-name="capitaine-cursors-light" +gtk-cursor-theme-size=0 +gtk-toolbar-style=GTK_TOOLBAR_ICONS +gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR +gtk-button-images=1 +gtk-menu-images=1 +gtk-enable-event-sounds=0 +gtk-enable-input-feedback-sounds=0 +gtk-xft-antialias=1 +gtk-xft-hinting=1 +gtk-xft-hintstyle="hintslight" +gtk-xft-rgba="rgb" diff --git a/nixos-config/config/gtk-3.0/settings.ini b/nixos-config/config/gtk-3.0/settings.ini new file mode 100644 index 00000000..6e343fa3 --- /dev/null +++ b/nixos-config/config/gtk-3.0/settings.ini @@ -0,0 +1,16 @@ +[Settings] +gtk-theme-name=Sweet-Dark +#gtk-icon-theme-name=Sweet-Rainbow +gtk-font-name=Fira Sans 12 +gtk-cursor-theme-name=capitaine-cursors-light +gtk-cursor-theme-size=0 +gtk-toolbar-style=GTK_TOOLBAR_ICONS +gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR +gtk-button-images=1 +gtk-menu-images=1 +gtk-enable-event-sounds=0 +gtk-enable-input-feedback-sounds=0 +gtk-xft-antialias=1 +gtk-xft-hinting=1 +gtk-xft-hintstyle=hintslight +gtk-xft-rgba=rgb diff --git a/nixos-config/config/gtk-4.0/settings.ini b/nixos-config/config/gtk-4.0/settings.ini new file mode 100644 index 00000000..6e343fa3 --- /dev/null +++ b/nixos-config/config/gtk-4.0/settings.ini @@ -0,0 +1,16 @@ +[Settings] +gtk-theme-name=Sweet-Dark +#gtk-icon-theme-name=Sweet-Rainbow +gtk-font-name=Fira Sans 12 +gtk-cursor-theme-name=capitaine-cursors-light +gtk-cursor-theme-size=0 +gtk-toolbar-style=GTK_TOOLBAR_ICONS +gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR +gtk-button-images=1 +gtk-menu-images=1 +gtk-enable-event-sounds=0 +gtk-enable-input-feedback-sounds=0 +gtk-xft-antialias=1 +gtk-xft-hinting=1 +gtk-xft-hintstyle=hintslight +gtk-xft-rgba=rgb diff --git a/nixos-config/config/sudoers b/nixos-config/config/sudoers new file mode 100644 index 00000000..a1844f36 --- /dev/null +++ b/nixos-config/config/sudoers @@ -0,0 +1,19 @@ +# Don't edit this file. Set the NixOS options ‘security.sudo.configFile’ +# or ‘security.sudo.extraRules’ instead. + +# Set editor to nvim +Defaults editor=/run/current-system/sw/bin/nvim + +# Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic. +Defaults env_keep+=SSH_AUTH_SOCK + +# "root" is allowed to do anything. +root ALL=(ALL:ALL) SETENV: ALL + +# extraRules +%wheel ALL=(ALL:ALL) SETENV: ALL + + +# Keep terminfo database for root and %wheel. +Defaults:root,%wheel env_keep+=TERMINFO_DIRS +Defaults:root,%wheel env_keep+=TERMINFO diff --git a/nixos-config/configuration.nix b/nixos-config/configuration.nix new file mode 100644 index 00000000..c1725ef0 --- /dev/null +++ b/nixos-config/configuration.nix @@ -0,0 +1,203 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page +# and in the NixOS manual (accessible by running `nixos-help`). + +{ config, pkgs, ... }: + +let + user = "exu"; + hostname = "nixos"; +in +{ + imports = [ + ./hardware-configuration.nix + ./system-packages.nix + ./home-manager.nix + ]; + + # Use the systemd-boot EFI boot loader. + boot.loader = { + systemd-boot = { + enable = true; + configurationLimit = 10; + }; + efi = { + canTouchEfiVariables = true; + }; + }; + + # Enable completions by nix + programs.fish.enable = true; + + # set neovim as default + programs.neovim.enable = true; + programs.neovim.defaultEditor = true; + + environment = { + shells = [ pkgs.fish ]; + variables = { + EDITOR = "nvim"; + VISUAL = "nvim"; + SUDO_EDITOR = "nvim"; + }; + # remove nano from default packages + defaultPackages = [ pkgs.perl pkgs.rsync pkgs.strace ]; + etc = { + # gtk theme configuration + # src: https://unix.stackexchange.com/questions/632879/how-to-set-a-system-wide-gtk-theme-in-nixos + "gtk-2.0/gtkrc".source = ./config/gtk-2.0/gtkrc; + "gtk-3.0/settings.ini".source = ./config/gtk-3.0/settings.ini; + "gtk-4.0/settings.ini".source = ./config/gtk-4.0/settings.ini; + }; + }; + + networking.hostName = "${hostname}"; # Define your hostname. + # Pick only one of the below networking options. + # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. + networking.networkmanager.enable = true; # Easiest to use and most distros use this by default. + + # disable global firewall for the time being + networking.firewall.enable = false; + + # Set your time zone. + time.timeZone = "Europe/Zurich"; + + # Configure network proxy if necessary + # networking.proxy.default = "http://user:password@proxy:port/"; + # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; + + # Select internationalisation properties. + i18n.defaultLocale = "de_CH.UTF-8"; + console = { + keyMap = "de_CH-latin1"; + }; + + # Enable the X11 windowing system. + services.xserver = { + enable = true; + displayManager.defaultSession = "hyprland"; + #displayManager.lightdm = { + # enable = true; + # greeters.gtk.enable = true; + #}; + displayManager.gdm = { + enable = true; + wayland = true; + }; + }; + + # enable gvfs service + services.gvfs.enable = true; + + # Hyprland + security.polkit.enable = true; + programs.hyprland = { + enable = true; + }; + + # sudoers file + security.sudo.configFile = (builtins.readFile ./config/sudoers); + + # Configure keymap in X11 + services.xserver.layout = "ch"; + + # keyring + services.gnome.gnome-keyring.enable = true; + security.pam.services.${user}.enableGnomeKeyring = true; + programs.seahorse.enable = true; + + # Enable CUPS to print documents. + services.printing.enable = true; + + # Enable Pipewire + security.rtkit.enable = true; + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + # If you want to use JACK applications, uncomment this + #jack.enable = true; + }; + + # Enable touchpad support (enabled default in most desktopManager). + services.xserver.libinput.enable = true; + + # root config + users.users.root = { + shell = pkgs.fish; + }; + + # User config + users.users.${user} = { + isNormalUser = true; + extraGroups = [ "wheel" "video" "audio" "networkmanager" "lp" "scanner" ]; + initialPassword = "pass"; + shell = pkgs.fish; + }; + + # Allow unfree packages + nixpkgs.config.allowUnfree = true; + + # Some programs need SUID wrappers, can be configured further or are + # started in user sessions. + # programs.mtr.enable = true; + # programs.gnupg.agent = { + # enable = true; + # enableSSHSupport = true; + # }; + + # List services that you want to enable: + + # Enable the OpenSSH daemon. + services.openssh.enable = true; + + # Enable thumbnailer service + services.tumbler.enable = true; + + # Open ports in the firewall. + # networking.firewall.allowedTCPPorts = [ ... ]; + # networking.firewall.allowedUDPPorts = [ ... ]; + # Or disable the firewall altogether. + # networking.firewall.enable = false; + + # Copy the NixOS configuration file and link it from the resulting system + # (/run/current-system/configuration.nix). This is useful in case you + # accidentally delete configuration.nix. + # system.copySystemConfiguration = true; + + # BTRFS options + fileSystems = { + "/".options = [ "compress=zstd" ]; + "/home".options = [ "compress=zstd" ]; + "/nix".options = [ "compress=zstd" "noatime" ]; + "/swap".options = [ "noatime" ]; + }; + + # Swapfile + swapDevices = [ { device = "/swap/swapfile"; } ]; + + # Enable automatic package upgrades + #system.autoUpgrade = { + # enable = true; + # channel = "https://nixos.org/channels/nixos-unstable"; + #}; + + # Enable automatic garbage collection + nix = { + settings.auto-optimise-store = true; + gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 7d"; + }; + }; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It's perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "22.11"; # Did you read the comment? +} diff --git a/nixos-config/home-manager.nix b/nixos-config/home-manager.nix new file mode 100644 index 00000000..bdc8f342 --- /dev/null +++ b/nixos-config/home-manager.nix @@ -0,0 +1,141 @@ +{ config, pkgs, ... }: + +let + user = "exu"; + hostname = "nixos"; +in +{ + imports = [ + + ]; + + # root home + home-manager.users.root = { pkgs, ... }: { + home.username = "root"; + home.homeDirectory = "/root"; + home.stateVersion = "22.11"; + home.packages = with pkgs; [ + kitty # terminfo support + ]; + + imports = [ + ./home-manager/hyprland.nix + ./home-manager/fish.nix + ]; + + }; + + # keep everything using home manager within this block + home-manager.users.${user} = { pkgs, ... }: { + home.username = "${user}"; + home.homeDirectory = "/home/${user}"; + home.stateVersion = "22.11"; + home.packages = with pkgs; [ + firefox # browser + kitty # terminal + pciutils # lspci command + git # git + emacs # emacs editor + emacsPackages.doom # doom emacs configuration + acpilight # controlling laptop monitor backlight + networkmanagerapplet # network configuration + wofi # app launcher (wayland replacement for rofi) + fish # fish shell + libnotify # notifications + mako # notification daemon + xdg-desktop-portal-hyprland # desktop portal (hyprland fork) + #neovim # text editor + yt-dlp # video downloader + #sweet # gtk theme + waybar # status bar + playerctl # mpris + xfce.thunar # file manager + xfce.thunar-archive-plugin # manage archives in thunar + #xfce.xfce4-settings # xfce settings manager + xfce.xfconf # xfce config storage + ]; + + imports = [ + ./home-manager/hyprland.nix + ./home-manager/fish.nix + ]; + + systemd.user = { + # user services + services = { + # ssh-agent user service + ssh-agent = { + Unit = { + Description = "SSH key agent"; + }; + Service = { + Type = "simple"; + Environment = "SSH_AUTH_SOCK=%t/ssh-agent.socket"; + ExecStart = "/run/current-system/sw/bin/ssh-agent -D -a $SSH_AUTH_SOCK"; + }; + Install = { + WantedBy = [ "default.target" ]; + }; + }; + }; + # user environment variables + sessionVariables = { + SSH_AUTH_SOCK = "/run/user/1000/ssh-agent.socket"; + }; + }; + + # git configuration + programs.git = { + enable = true; + extraConfig = { + init = { + defaultBranch = "main"; + }; + user = { + name = "RealStickman"; + email = "mrc@frm01.net"; + }; + gitlab = { + user = "RealStickman"; + }; + github = { + user = "RealStickman"; + }; + }; + }; + + programs.waybar = { + enable = true; + settings = { + mainBar = (builtins.fromJSON (builtins.readFile ./home-manager/config/waybar/config.json)); + }; + style = (builtins.readFile ./home-manager/config/waybar/style.css); + }; + + home.file = { + # Scripts + ".scripts/waybar/player-mpris-tail.py" = { + source = ./home-manager/scripts/waybar/player-mpris-tail.py; + executable = true; + }; + # Thunar configuration + ".config/Thunar" = { + source = ./home-manager/config/Thunar; + #recursive = true; + }; + # templates + ".config/Vorlagen" = { + source = ./home-manager/config/Vorlagen; + #recursive = true; + }; + # xfce4 settings + ".config/xfce4".source = ./home-manager/config/xfce4; + # xdg user dirs + ".config/user-dirs.dirs".source = ./home-manager/config/user-dirs.dirs; + # xdg user locales + ".config/user-dirs.locale".source = ./home-manager/config/user-dirs.locale; + }; + + services.mako.enable = true; + }; +} diff --git a/nixos-config/home-manager/config/Thunar/accels.scm b/nixos-config/home-manager/config/Thunar/accels.scm new file mode 100644 index 00000000..5b69c38b --- /dev/null +++ b/nixos-config/home-manager/config/Thunar/accels.scm @@ -0,0 +1,132 @@ +; thunar GtkAccelMap rc-file -*- scheme -*- +; this file is an automated accelerator map dump +; +; (gtk_accel_path "/ThunarBookmarks/fab0a703c3165b9ea6d4a2e40b570272" "") +; (gtk_accel_path "/ThunarStandardView/sort-by-type" "") +; (gtk_accel_path "/ThunarBookmarks/134cf305a61c72f784680835c93c28fd" "") +; (gtk_accel_path "/ThunarStatusBar/toggle-last-modified" "") +; (gtk_accel_path "/ThunarActionManager/cut" "x") +; (gtk_accel_path "/ThunarStandardView/sort-by-size" "") +; (gtk_accel_path "/ThunarWindow/file-menu" "") +(gtk_accel_path "/ThunarWindow/close-tab" "q") +; (gtk_accel_path "/ThunarStatusBar/toggle-size" "") +(gtk_accel_path "/ThunarWindow/new-window" "") +; (gtk_accel_path "/ThunarWindow/clear-directory-specific-settings" "") +(gtk_accel_path "/ThunarWindow/close-window" "") +; (gtk_accel_path "/ThunarBookmarks/ac3bbb1429c4180d34b90e935eaeaa7b" "") +(gtk_accel_path "/ThunarWindow/open-parent" "") +; (gtk_accel_path "/ThunarWindow/view-side-pane-menu" "") +; (gtk_accel_path "/ThunarStatusBar/toggle-size-in-bytes" "") +; (gtk_accel_path "/ThunarWindow/switch-previous-tab" "Page_Up") +; (gtk_accel_path "/ThunarBookmarks/3cb76eced52269e016bf542f6d20d431" "") +(gtk_accel_path "/ThunarActionManager/open" "") +; (gtk_accel_path "/ThunarStandardView/sort-ascending" "") +; (gtk_accel_path "/ThunarWindow/toggle-split-view" "F3") +; (gtk_accel_path "/ThunarActionManager/copy-2" "Insert") +; (gtk_accel_path "/ThunarActionManager/trash-delete" "Delete") +; (gtk_accel_path "/ThunarWindow/open-recent" "") +; (gtk_accel_path "/ThunarWindow/view-configure-toolbar" "") +; (gtk_accel_path "/ThunarStandardView/forward" "Right") +; (gtk_accel_path "/ThunarBookmarks/9f9804d3a8acb92d2796c0e9ee791891" "") +; (gtk_accel_path "/ThunarActionManager/restore" "") +(gtk_accel_path "/ThunarWindow/open-location-alt" "") +; (gtk_accel_path "/ThunarBookmarks/0d63283611773acfd219c84ae028d009" "") +(gtk_accel_path "/ThunarStandardView/select-by-pattern" "") +; (gtk_accel_path "/ThunarWindow/zoom-out-alt" "minus") +; (gtk_accel_path "/ThunarWindow/open-file-menu" "F10") +(gtk_accel_path "/ThunarWindow/contents" "") +; (gtk_accel_path "/ThunarWindow/show-highlight" "") +; (gtk_accel_path "/ThunarStandardView/sort-descending" "") +; (gtk_accel_path "/ThunarStandardView/sort-by-name" "") +; (gtk_accel_path "/ThunarStandardView/select-all-files" "a") +; (gtk_accel_path "/ThunarActionManager/execute" "") +(gtk_accel_path "/ThunarStandardView/properties" "") +; (gtk_accel_path "/ThunarActionManager/cut-2" "") +; (gtk_accel_path "/ThunarStandardView/sort-by-dtime" "") +; (gtk_accel_path "/ThunarWindow/switch-next-tab" "Page_Down") +; (gtk_accel_path "/ThunarActionManager/paste-2" "Insert") +; (gtk_accel_path "/ThunarWindow/open-templates" "") +; (gtk_accel_path "/ThunarStatusBar/toggle-filetype" "") +(gtk_accel_path "/ThunarWindow/close-all-windows" "") +; (gtk_accel_path "/ThunarStandardView/create-document" "") +; (gtk_accel_path "/ThunarWindow/detach-tab" "") +; (gtk_accel_path "/ThunarBookmarks/310a68ed4e8d7e3153dbac6fd6f8509e" "") +; (gtk_accel_path "/ThunarWindow/cancel-search" "Escape") +; (gtk_accel_path "/ThunarWindow/zoom-in-alt2" "equal") +(gtk_accel_path "/ThunarShortcutsPane/sendto-shortcuts" "") +; (gtk_accel_path "/ThunarActionManager/undo" "z") +; (gtk_accel_path "/ThunarWindow/reload-alt" "F5") +(gtk_accel_path "/ThunarActions/uca-action-1666515885637912-1" "Return") +; (gtk_accel_path "/ThunarStandardView/toggle-sort-order" "") +; (gtk_accel_path "/ThunarWindow/view-location-selector-entry" "") +; (gtk_accel_path "/ThunarActionManager/paste" "v") +; (gtk_accel_path "/ThunarWindow/zoom-in-alt1" "plus") +(gtk_accel_path "/ThunarWindow/view-menubar" "") +; (gtk_accel_path "/ThunarStandardView/back" "Left") +; (gtk_accel_path "/ThunarWindow/open-desktop" "") +(gtk_accel_path "/ThunarWindow/view-as-detailed-list" "") +; (gtk_accel_path "/ThunarActionManager/restore-show" "") +; (gtk_accel_path "/ThunarStatusBar/toggle-display-name" "") +; (gtk_accel_path "/ThunarWindow/zoom-out" "KP_Subtract") +; (gtk_accel_path "/ThunarBookmarks/d560a9adc56f1eaa2739d7e989051c36" "") +; (gtk_accel_path "/ThunarWindow/sendto-menu" "") +; (gtk_accel_path "/ThunarWindow/go-menu" "") +; (gtk_accel_path "/ThunarWindow/remove-from-recent" "") +; (gtk_accel_path "/ThunarActionManager/open-with-other" "") +(gtk_accel_path "/ThunarStandardView/invert-selection" "i") +(gtk_accel_path "/ThunarWindow/view-side-pane-shortcuts" "") +; (gtk_accel_path "/ThunarWindow/view-location-selector-menu" "") +; (gtk_accel_path "/ThunarStandardView/sort-by-mtime" "") +; (gtk_accel_path "/ThunarWindow/edit-menu" "") +; (gtk_accel_path "/ThunarWindow/reload" "r") +; (gtk_accel_path "/ThunarActionManager/copy" "c") +; (gtk_accel_path "/ThunarActionManager/move-to-trash" "") +; (gtk_accel_path "/ThunarStandardView/unselect-all-files" "Escape") +; (gtk_accel_path "/ThunarActionManager/delete-3" "KP_Delete") +(gtk_accel_path "/ThunarWindow/toggle-side-pane" "") +; (gtk_accel_path "/ThunarBookmarks/bd09eece7395e751859c8153dca05324" "") +; (gtk_accel_path "/ThunarStandardView/arrange-items-menu" "") +; (gtk_accel_path "/ThunarBookmarks/ef49cad9a2b186bac59b8de045e0f5d4" "") +; (gtk_accel_path "/ThunarWindow/open-computer" "") +; (gtk_accel_path "/ThunarWindow/bookmarks-menu" "") +; (gtk_accel_path "/ThunarWindow/toggle-image-preview" "") +(gtk_accel_path "/ThunarWindow/view-as-icons" "") +; (gtk_accel_path "/ThunarActionManager/delete-2" "Delete") +; (gtk_accel_path "/ThunarWindow/zoom-in" "KP_Add") +; (gtk_accel_path "/ThunarStandardView/configure-columns" "") +; (gtk_accel_path "/ThunarStandardView/rename" "F2") +; (gtk_accel_path "/ThunarWindow/open-location" "l") +(gtk_accel_path "/ThunarWindow/view-as-compact-list" "") +; (gtk_accel_path "/ThunarWindow/view-menu" "") +; (gtk_accel_path "/ThunarWindow/search" "f") +; (gtk_accel_path "/ThunarWindow/new-tab" "t") +; (gtk_accel_path "/ThunarWindow/zoom-reset" "KP_0") +; (gtk_accel_path "/ThunarWindow/contents/help-menu" "") +(gtk_accel_path "/ThunarActionManager/open-in-new-tab" "") +; (gtk_accel_path "/ThunarWindow/view-location-selector-buttons" "") +; (gtk_accel_path "/ThunarActionManager/redo" "z") +; (gtk_accel_path "/ThunarWindow/open-trash" "") +; (gtk_accel_path "/ThunarBookmarks/7d2c0d8f2cb46dda0c77c334948613d9" "") +(gtk_accel_path "/ThunarActionManager/open-in-new-window" "") +; (gtk_accel_path "/ThunarWindow/view-statusbar" "") +; (gtk_accel_path "/ThunarBookmarks/356c14bf86880b16a82a896aac1ea75d" "") +; (gtk_accel_path "/ThunarActionManager/open-location" "") +; (gtk_accel_path "/ThunarStandardView/duplicate" "") +; (gtk_accel_path "/ThunarActionManager/trash-delete-2" "KP_Delete") +; (gtk_accel_path "/ThunarBookmarks/efbd1f6870be1a5a9ee9ba157935b388" "") +(gtk_accel_path "/ThunarActions/uca-action-1666516933235505-2" "f") +; (gtk_accel_path "/ThunarStandardView/create-folder" "n") +(gtk_accel_path "/ThunarWindow/open-home" "") +(gtk_accel_path "/ThunarWindow/show-hidden" "") +; (gtk_accel_path "/ThunarStandardView/set-default-app" "") +; (gtk_accel_path "/ThunarWindow/empty-trash" "") +; (gtk_accel_path "/ThunarStandardView/back-alt" "BackSpace") +; (gtk_accel_path "/ThunarWindow/preferences" "") +; (gtk_accel_path "/ThunarActionManager/delete" "") +(gtk_accel_path "/ThunarWindow/view-side-pane-tree" "") +; (gtk_accel_path "/ThunarWindow/open-file-system" "") +; (gtk_accel_path "/ThunarWindow/open-network" "") +; (gtk_accel_path "/ThunarActionManager/sendto-desktop" "") +; (gtk_accel_path "/ThunarStandardView/make-link" "") +; (gtk_accel_path "/ThunarWindow/zoom-reset-alt" "0") +; (gtk_accel_path "/ThunarWindow/about" "") diff --git a/nixos-config/home-manager/config/Thunar/uca.xml b/nixos-config/home-manager/config/Thunar/uca.xml new file mode 100644 index 00000000..5d38c9f7 --- /dev/null +++ b/nixos-config/home-manager/config/Thunar/uca.xml @@ -0,0 +1,59 @@ + + + + folder + Als root öffnen + + 1673031093097554-1 + gksu thunar %f + Verzeichnis als root-Nutzer öffnen + * + * + + + + utilities-terminal + Terminal öffnen + + 1666515885637912-1 + for f in %F; do if [ -d "$f" ]; then kitty "$f"; elif [ -z "$default" ]; then default=1; kitty; fi done + Terminal im gewählten Ordner öffnen + + * + + + + + + + + + + preferences-system-search + Suchen + + 1666516933235505-2 + catfish --path=%f + Dateien und Ordner suchen + + * + + + + + link + Symlink erstellen + + 1676990164646243-1 + ln -Ts %f %n" (symlink)" + Symbolischen Link erstellen + * + * + + + + + + + + diff --git a/nixos-config/home-manager/config/Vorlagen/Drawio Diagramm.drawio b/nixos-config/home-manager/config/Vorlagen/Drawio Diagramm.drawio new file mode 100644 index 00000000..b5654ddf --- /dev/null +++ b/nixos-config/home-manager/config/Vorlagen/Drawio Diagramm.drawio @@ -0,0 +1 @@ +dZFND8IgDIZ/DfcBRvE8vy6edvBMRh0kbF0YZtNf7xaYk0xPlOd9S2lLeF4PZydbfUUFlrBMDYQfCGN0Q8V4TOQZiBA0gMoZFU0LKMwLIswifRgFXWL0iNabNoUlNg2UPmHSOexT2x1tWrWVFaxAUUq7pjejvI6UbveLcAFT6VhasF0QajmbYyedlgr7L8SPhOcO0YeoHnKw0/DmuYS80x/18zEHjf+RMAbL2+Ml2RA/vgE= \ No newline at end of file diff --git a/nixos-config/home-manager/config/Vorlagen/Libreoffice Calc.ods b/nixos-config/home-manager/config/Vorlagen/Libreoffice Calc.ods new file mode 100644 index 00000000..f6e75a90 Binary files /dev/null and b/nixos-config/home-manager/config/Vorlagen/Libreoffice Calc.ods differ diff --git a/nixos-config/home-manager/config/Vorlagen/Libreoffice Impress.odp b/nixos-config/home-manager/config/Vorlagen/Libreoffice Impress.odp new file mode 100644 index 00000000..904301bf Binary files /dev/null and b/nixos-config/home-manager/config/Vorlagen/Libreoffice Impress.odp differ diff --git a/nixos-config/home-manager/config/Vorlagen/Libreoffice Writer.odt b/nixos-config/home-manager/config/Vorlagen/Libreoffice Writer.odt new file mode 100644 index 00000000..2494330b Binary files /dev/null and b/nixos-config/home-manager/config/Vorlagen/Libreoffice Writer.odt differ diff --git a/nixos-config/home-manager/config/Vorlagen/Markdown.md b/nixos-config/home-manager/config/Vorlagen/Markdown.md new file mode 100644 index 00000000..67bff78f --- /dev/null +++ b/nixos-config/home-manager/config/Vorlagen/Markdown.md @@ -0,0 +1,3 @@ + + +# Document Title diff --git a/nixos-config/home-manager/config/Vorlagen/Microsoft Excel.xlsx b/nixos-config/home-manager/config/Vorlagen/Microsoft Excel.xlsx new file mode 100644 index 00000000..22866fb6 Binary files /dev/null and b/nixos-config/home-manager/config/Vorlagen/Microsoft Excel.xlsx differ diff --git a/nixos-config/home-manager/config/Vorlagen/Microsoft PowerPoint.pptx b/nixos-config/home-manager/config/Vorlagen/Microsoft PowerPoint.pptx new file mode 100644 index 00000000..0dbcbfa1 Binary files /dev/null and b/nixos-config/home-manager/config/Vorlagen/Microsoft PowerPoint.pptx differ diff --git a/nixos-config/home-manager/config/Vorlagen/Microsoft Word.docx b/nixos-config/home-manager/config/Vorlagen/Microsoft Word.docx new file mode 100644 index 00000000..7d3f4fda Binary files /dev/null and b/nixos-config/home-manager/config/Vorlagen/Microsoft Word.docx differ diff --git a/nixos-config/home-manager/config/fish/conf.d/interactive.fish b/nixos-config/home-manager/config/fish/conf.d/interactive.fish new file mode 100644 index 00000000..f5a0c285 --- /dev/null +++ b/nixos-config/home-manager/config/fish/conf.d/interactive.fish @@ -0,0 +1,35 @@ +#vi mode for fish +#fish_vi_key_bindings +fish_default_key_bindings + +# change greeting +set fish_greeting "Good Morning! Nice day for fishing ain't it! Hu ha!" + +# Info: +# https://fishshell.com/docs/current/index.html#variables-for-changing-highlighting-colors +set fish_color_normal blue +set fish_color_command blue +set fish_color_quote bryellow +set fish_color_redirection +set fish_color_end brred +set fish_color_error red +set fish_color_param brblue +set fish_color_comment white +set fish_color_selection brcyan +set fish_color_search_match magenta +set fish_color_operator blue +set fish_color_escape green +set fish_color_autosuggestion brwhite +set fish_color_host_remote brwhite +set fish_color_cancel brred + +# used in prompt +set fish_color_user --bold red +set fish_color_separator --bold yellow +set fish_color_host cyan +set fish_color_cwd yellow + +# environment variables +# SSH Agent +set SSH_AUTH_SOCK "$XDG_RUNTIME_DIR/ssh-agent.socket" +export SSH_AUTH_SOCK diff --git a/nixos-config/home-manager/config/fish/functions/fish_prompt.fish b/nixos-config/home-manager/config/fish/functions/fish_prompt.fish new file mode 100644 index 00000000..13a97b16 --- /dev/null +++ b/nixos-config/home-manager/config/fish/functions/fish_prompt.fish @@ -0,0 +1,32 @@ +set -l last_pipestatus $pipestatus +set -lx __fish_last_status $status # Export for __fish_print_pipestatus. +set -l normal (set_color normal) + +# Color the prompt differently when we're root +set -l color_cwd $fish_color_cwd +set -l suffix '>' +#if functions -q fish_is_root_user; and fish_is_root_user +# if set -q fish_color_cwd_root +# set color_cwd $fish_color_cwd_root +# end +# set suffix '#' +#end + +# If we're running via SSH, change the host color. +set -l color_host $fish_color_host +#if set -q SSH_TTY +# set color_host $fish_color_host_remote +#end + +# Write pipestatus +# If the status was carried over (e.g. after `set`), don't bold it. +set -l bold_flag --bold +set -q __fish_prompt_status_generation; or set -g __fish_prompt_status_generation $status_generation +if test $__fish_prompt_status_generation = $status_generation + set bold_flag +end +set __fish_prompt_status_generation $status_generation +set -l prompt_status (__fish_print_pipestatus "[" "]" "|" (set_color $fish_color_status) (set_color $bold_flag $fish_color_status) $last_pipestatus) + +echo -n -s (set_color $fish_color_user) "$USER" $normal (set_color $fish_color_separator) @ $normal (set_color $color_host) (prompt_hostname) $normal ' ' (set_color $color_cwd) (prompt_pwd) $normal (fish_vcs_prompt) $normal " "$prompt_status $suffix " " +#echo -n -s (set_color --bold red) "$USER" $normal (set_color --bold yellow) @ $normal (set_color cyan) (prompt_hostname) $normal ' ' (set_color yellow) (prompt_pwd) $normal (fish_vcs_prompt) $normal " "$prompt_status $suffix " " diff --git a/nixos-config/home-manager/config/hypr/hyprland.conf b/nixos-config/home-manager/config/hypr/hyprland.conf new file mode 100644 index 00000000..d5e6fa86 --- /dev/null +++ b/nixos-config/home-manager/config/hypr/hyprland.conf @@ -0,0 +1,201 @@ +# +# Please note not all available settings / options are set here. +# For a full list, see the wiki +# + +# See https://wiki.hyprland.org/Configuring/Monitors/ +monitor=,preferred,auto,auto + + +# See https://wiki.hyprland.org/Configuring/Keywords/ for more + +# Execute your favorite apps at launch +# exec-once = waybar & hyprpaper & firefox + +# Source a file (multi-file configs) +# source = ~/.config/hypr/myColors.conf + +# Some default env vars. +env = XCURSOR_SIZE,24 + +# For all categories, see https://wiki.hyprland.org/Configuring/Variables/ +input { + #kb_layout = de_CH-latin1 + kb_layout = ch + kb_variant = + kb_model = + kb_options = + kb_rules = + + follow_mouse = 1 + + touchpad { + natural_scroll = false + } + + sensitivity = 0 # -1.0 - 1.0, 0 means no modification. +} + +general { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + + gaps_in = 0 + gaps_out = 0 + border_size = 2 + col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg + col.inactive_border = rgba(595959aa) + + layout = dwindle +} + +decoration { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + + #rounding = 10 + blur = yes + blur_size = 3 + blur_passes = 1 + blur_new_optimizations = on + + drop_shadow = yes + shadow_range = 4 + shadow_render_power = 3 + col.shadow = rgba(1a1a1aee) +} + +animations { + enabled = yes + + # Some default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more + + bezier = myBezier, 0.05, 0.9, 0.1, 1.05 + + animation = windows, 1, 7, myBezier + animation = windowsOut, 1, 7, default, popin 80% + animation = border, 1, 10, default + animation = borderangle, 1, 8, default + animation = fade, 1, 7, default + animation = workspaces, 1, 6, default +} + +dwindle { + # See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more + pseudotile = yes # master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below + preserve_split = yes # you probably want this +} + +master { + # See https://wiki.hyprland.org/Configuring/Master-Layout/ for more + new_is_master = true +} + +gestures { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + workspace_swipe = off +} + +# Example per-device config +# See https://wiki.hyprland.org/Configuring/Keywords/#executing for more +#device:epic-mouse-v1 { +# sensitivity = -0.5 +#} + +# Example windowrule v1 +# windowrule = float, ^(kitty)$ +# Example windowrule v2 +# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$ +# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more + + +# See https://wiki.hyprland.org/Configuring/Keywords/ for more +$mod = SUPER + +# Startup stuff +exec-once = mako +exec-once = waybar + +# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more +bind = CTRL_ALT, RETURN, exec, kitty # terminal +bind = CTRL_ALT, A, exec, wofi --show drun # app launcher +bind = CTRL_ALT, f, exec, firefox # browser +bind = CTRL_ALT, e, exec, emacs # editor +bind = CTRL_ALT, t, exec, thunar # file manager + +bind = $mod, Q, killactive, # close window + +# logout +bind = ALT_$mod, L, exit, + + +# bind power management submap +bind = $mod, x, submap, | (S)hutdown, (R)eboot, (E)xit | +# power management submap +submap = | (S)hutdown, (R)eboot, (E)xit | + # logout + bind = , e, exit, + # lock + # TODO + # poweroff + bind = , s, exec, systemctl poweroff + # reboot + bind = , r, exec, systemctl reboot + + # use reset to go back to the global submap + bind=,escape,submap,reset + +# will reset the submap, meaning end the current one and return to the global one +submap=reset + +#bind = $mod, Q, exec, kitty +#bind = $mod, C, killactive, +#bind = $mod, M, exit, +#bind = $mod, E, exec, dolphin +#bind = $mod, V, togglefloating, +#bind = $mod, R, exec, wofi --show drun +#bind = $mod, P, pseudo, # dwindle +#bind = $mod, J, togglesplit, # dwindle + +# Move focus with $mod + arrow keys +bind = $mod, left, movefocus, l +bind = $mod, right, movefocus, r +bind = $mod, up, movefocus, u +bind = $mod, down, movefocus, d + +# Move focus with $mod + hjkl +bind = $mod, H, movefocus, l +bind = $mod, L, movefocus, r +bind = $mod, K, movefocus, u +bind = $mod, J, movefocus, d + + +# Switch workspaces with mainMod + [0-9] +bind = $mod, 1, workspace, 1 +bind = $mod, 2, workspace, 2 +bind = $mod, 3, workspace, 3 +bind = $mod, 4, workspace, 4 +bind = $mod, 5, workspace, 5 +bind = $mod, 6, workspace, 6 +bind = $mod, 7, workspace, 7 +bind = $mod, 8, workspace, 8 +bind = $mod, 9, workspace, 9 +bind = $mod, 0, workspace, 10 + +# Move active window to a workspace with mainMod + SHIFT + [0-9] +bind = $mod SHIFT, 1, movetoworkspace, 1 +bind = $mod SHIFT, 2, movetoworkspace, 2 +bind = $mod SHIFT, 3, movetoworkspace, 3 +bind = $mod SHIFT, 4, movetoworkspace, 4 +bind = $mod SHIFT, 5, movetoworkspace, 5 +bind = $mod SHIFT, 6, movetoworkspace, 6 +bind = $mod SHIFT, 7, movetoworkspace, 7 +bind = $mod SHIFT, 8, movetoworkspace, 8 +bind = $mod SHIFT, 9, movetoworkspace, 9 +bind = $mod SHIFT, 0, movetoworkspace, 10 + +# Scroll through existing workspaces with $mod + scroll +bind = $mod, mouse_down, workspace, e+1 +bind = $mod, mouse_up, workspace, e-1 + +# Move/resize windows with mainMod + LMB/RMB and dragging +bindm = $mod, mouse:272, movewindow +bindm = $mod, mouse:273, resizewindow diff --git a/nixos-config/home-manager/config/user-dirs.dirs b/nixos-config/home-manager/config/user-dirs.dirs new file mode 100644 index 00000000..51849c9e --- /dev/null +++ b/nixos-config/home-manager/config/user-dirs.dirs @@ -0,0 +1,15 @@ +# This file is written by xdg-user-dirs-update +# If you want to change or add directories, just edit the line you're +# interested in. All local changes will be retained on the next run. +# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped +# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an +# absolute path. No other format is supported. +# +XDG_DESKTOP_DIR="$HOME/Schreibtisch" +XDG_DOWNLOAD_DIR="$HOME/Downloads" +XDG_TEMPLATES_DIR="$HOME/.config/Vorlagen" +XDG_PUBLICSHARE_DIR="$HOME/Öffentlich" +XDG_DOCUMENTS_DIR="$HOME/Dokumente" +XDG_MUSIC_DIR="$HOME/Musik" +XDG_PICTURES_DIR="$HOME/Bilder" +XDG_VIDEOS_DIR="$HOME/Videos" diff --git a/nixos-config/home-manager/config/user-dirs.locale b/nixos-config/home-manager/config/user-dirs.locale new file mode 100644 index 00000000..4dd13ef0 --- /dev/null +++ b/nixos-config/home-manager/config/user-dirs.locale @@ -0,0 +1 @@ +de_CH \ No newline at end of file diff --git a/nixos-config/home-manager/config/waybar/config.json b/nixos-config/home-manager/config/waybar/config.json new file mode 100644 index 00000000..d598115a --- /dev/null +++ b/nixos-config/home-manager/config/waybar/config.json @@ -0,0 +1,70 @@ +{ + "layer": "top", + "position": "bottom", + "height": 25, + "spacing": 10, + + "modules-left": ["wlr/workspaces", "hyprland/window", "hyprland/submap"], + "modules-center": ["clock"], + "modules-right": ["mpris", "idle_inhibitor", "battery", "tray"], + + "wlr/workspaces": { + "format": "{name}: {icon}", + "format-icons": { + "urgent": "", + "active": "", + "default": "" + }, + "sort-by-number": true + }, + "hyprland/window": { + "format": "{}", + "separate-outputs": true + }, + "hyprland/submap": { + "format": "{}", + "tooltip": false + }, + "mpris": { + "format": "{status_icon} {dynamic}", + "format-paused": "{status_icon} {dynamic}", + "status-icons": { + "playing": "▶", + "paused": "⏸" + } + }, + "battery": { + "bat": "BAT0", + "states": { + "warning": 30, + "critical": 15 + }, + "format": "{capacity}% {icon}", + "format-charging": "{capacity}% ", + "format-plugged": "{capacity}% ", + "format-alt": "{time} {icon}", + "format-icons": ["", "", "", "", ""] + }, + "clock": { + "timezone": "Europe/Zurich", + "tooltip-format": "{:%Y %B}\n{calendar}", + "format-alt": "{:%Y-%m-%d}" + }, + "idle_inhibitor": { + "format": "{icon}", + "format-icons": { + "activated": "", + "deactivated": "" + } + }, + "tray": { + "icon-size": 21, + "spacing": 10 + }, + "custom/mpris": { + "exec": "~/.scripts/polybar/player-mpris-tail.py -f '{icon} {:artist:t10:{artist}:}{:artist: - :}{:t25:{title}:}'", + "on-click": "~/.scripts/polybar/player-mpris-tail.py play-pause &", + "on-click-middle": "~/.scripts/polybar/player-mpris-tail.py next &", + "on-click-right": "~/.scripts/polybar/player-mpris-tail.py previous &" + } +} diff --git a/nixos-config/home-manager/config/waybar/style.css b/nixos-config/home-manager/config/waybar/style.css new file mode 100644 index 00000000..c4d1784d --- /dev/null +++ b/nixos-config/home-manager/config/waybar/style.css @@ -0,0 +1,145 @@ +* { + /* `otf-font-awesome` is required to be installed for icons */ + font-family: FontAwesome, Fira Sans, sans-serif; + font-size: 12px; + padding-left: 5px; + padding-right: 5px; +} + +window#waybar { + /*background-color: rgba(43, 48, 59, 0.5);*/ + background-color: rgba(51, 2, 47, 0.6); + border: 0px; + border-top: 2px solid rgba(93, 4, 86, 0.8); + /*border-bottom: 3px solid rgba(100, 114, 125, 0.5);*/ + color: #f2daf0; + /*color: #ffffff;*/ + transition-property: background-color; + transition-duration: 0.5s; +} + +window#waybar.hidden { + opacity: 0.2; +} + +/* +window#waybar.empty { + background-color: transparent; +} +window#waybar.solo { + background-color: #FFFFFF; +} +*/ + +button { + /* Use box-shadow instead of border so the text isn't offset */ + box-shadow: inset 0 -3px 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 -3px #ffffff; +} + +#workspaces button { + padding: 0 5px; + background-color: transparent; + color: #ffffff; +} + +#workspaces button:hover { + background: rgba(0, 0, 0, 0.2); +} + +#workspaces button.focused { + background-color: #64727d; + box-shadow: inset 0 -3px #ffffff; +} + +#workspaces button.urgent { + background-color: #eb4d4b; +} + +#mode { + background-color: #64727d; + border-bottom: 3px solid #ffffff; +} + +#clock, +#battery, +#tray, +#mode, +#idle_inhibitor, +#window, +#workspaces { + margin: 0 4px; +} + +/* If workspaces is the leftmost module, omit left margin */ +.modules-left > widget:first-child > #workspaces { + margin-left: 0; +} + +/* If workspaces is the rightmost module, omit right margin */ +.modules-right > widget:last-child > #workspaces { + margin-right: 0; +} + +#clock { + background-color: #5d0456; +} + +#battery { + background-color: #5d0456; + color: #f2daf0; + /*color: #000000;*/ +} + +@keyframes blink { + to { + background-color: #f2daf0; + color: #000000; + } +} + +#battery.critical:not(.charging) { + background-color: #ca3232; + color: #f2daf0; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; +} + +label:focus { + background-color: #000000; +} + +/* +#tray { + background-color: #2980b9; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #eb4d4b; +} +*/ + +#idle_inhibitor { + background-color: #5d0456; +} + +#idle_inhibitor.activated { + background-color: #f2daf0; + color: #5d0456; +} diff --git a/nixos-config/home-manager/config/xfce4/xfconf/xfce-perchannel-xml/thunar.xml b/nixos-config/home-manager/config/xfce4/xfconf/xfce-perchannel-xml/thunar.xml new file mode 100644 index 00000000..bf97961b --- /dev/null +++ b/nixos-config/home-manager/config/xfce4/xfconf/xfce-perchannel-xml/thunar.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nixos-config/home-manager/fish.nix b/nixos-config/home-manager/fish.nix new file mode 100644 index 00000000..520d51b7 --- /dev/null +++ b/nixos-config/home-manager/fish.nix @@ -0,0 +1,39 @@ +{ config, ... }: + +{ + programs.fish = { + enable = true; + interactiveShellInit = (builtins.readFile ./config/fish/conf.d/interactive.fish); + shellAliases = { + # open emacs in terminal + emacs = "emacs --no-window-system"; + # colored wget output + wget = "wget -c"; + # copy terminfo to remote server with kitty + sshkp = "kitty +kitten ssh use-python"; + sshk = "kitty +kitten ssh"; + # colored ls output + ls = "ls --color=auto"; + # kill all wine processes + killwine = "ls -l /proc/*/exe 2>/dev/null | grep -E 'wine(64)?-preloader|wineserver' | perl -pe 's;^.*/proc/(\d+)/exe.*$;$1;g;' | xargs -n 1 kill"; + # colored grep output + grep = "grep --color=auto"; + # yt-dlp aliases + # best audio + yta-best = "yt-dlp -f bestaudio --extract-audio "; + # best video + ytv-best = "yt-dlp -f bestvideo+bestaudio "; + # download video including metadata from youtube + ytv-metadata = "yt-dlp -f bestvideo+bestaudio --add-metadata --parse-metadata \"%(title)s:%(meta_title)s\" --parse-metadata \"%(uploader)s:%(meta_artist)s\" --write-info-json --write-thumbnail --embed-thumbnail --embed-subs --sub-langs \"en.*\" --merge-output-format mkv "; + # activate venv called "venv" in the local directory + activate = "source venv/bin/activate.fish"; + # lsblk including file system type + lsblkf = "lsblk -o NAME,FSTYPE,LABEL,MOUNTPOINT,SIZE,MODEL,UUID"; + # color ip command + ip = "ip -c"; + }; + functions = { + fish_prompt = (builtins.readFile ./config/fish/functions/fish_prompt.fish); + }; + }; +} diff --git a/nixos-config/home-manager/hyprland.nix b/nixos-config/home-manager/hyprland.nix new file mode 100644 index 00000000..4fd65a00 --- /dev/null +++ b/nixos-config/home-manager/hyprland.nix @@ -0,0 +1,20 @@ +{pkgs, ...}: + +let + flake-compat = builtins.fetchTarball "https://github.com/edolstra/flake-compat/archive/master.tar.gz"; + + hyprland = (import flake-compat { + src = builtins.fetchTarball "https://github.com/hyprwm/Hyprland/archive/master.tar.gz"; + }).defaultNix; +in +{ + imports = [ + hyprland.homeManagerModules.default + ]; + + wayland.windowManager.hyprland = { + enable = true; + + extraConfig = (builtins.readFile ./config/hypr/hyprland.conf); + }; +} diff --git a/nixos-config/home-manager/scripts/waybar/player-mpris-tail.py b/nixos-config/home-manager/scripts/waybar/player-mpris-tail.py new file mode 100755 index 00000000..8d395554 --- /dev/null +++ b/nixos-config/home-manager/scripts/waybar/player-mpris-tail.py @@ -0,0 +1,530 @@ +#!/usr/bin/env python3 + +import sys +import dbus +import os +from operator import itemgetter +import argparse +import re +from urllib.parse import unquote +import time +from dbus.mainloop.glib import DBusGMainLoop +from gi.repository import GLib +DBusGMainLoop(set_as_default=True) + + +FORMAT_STRING = '{icon} {artist} - {title}' +FORMAT_REGEX = re.compile(r'(\{:(?P.*?)(:(?P[wt])(?P\d+))?:(?P.*?):\})', re.I) +FORMAT_TAG_REGEX = re.compile(r'(?P[wt])(?P\d+)') +SAFE_TAG_REGEX = re.compile(r'[{}]') + +class PlayerManager: + def __init__(self, blacklist = [], connect = True): + self.blacklist = blacklist + self._connect = connect + self._session_bus = dbus.SessionBus() + self.players = {} + + self.print_queue = [] + self.connected = False + self.player_states = {} + + self.refreshPlayerList() + + if self._connect: + self.connect() + loop = GLib.MainLoop() + try: + loop.run() + except KeyboardInterrupt: + print("interrupt received, stopping…") + + def connect(self): + self._session_bus.add_signal_receiver(self.onOwnerChangedName, 'NameOwnerChanged') + self._session_bus.add_signal_receiver(self.onChangedProperties, 'PropertiesChanged', + path = '/org/mpris/MediaPlayer2', + sender_keyword='sender') + + def onChangedProperties(self, interface, properties, signature, sender = None): + if sender in self.players: + player = self.players[sender] + # If we know this player, but haven't been able to set up a signal handler + if 'properties_changed' not in player._signals: + # Then trigger the signal handler manually + player.onPropertiesChanged(interface, properties, signature) + else: + # If we don't know this player, get its name and add it + bus_name = self.getBusNameFromOwner(sender) + if bus_name is None: + return + self.addPlayer(bus_name, sender) + player = self.players[sender] + player.onPropertiesChanged(interface, properties, signature) + + def onOwnerChangedName(self, bus_name, old_owner, new_owner): + if self.busNameIsAPlayer(bus_name): + if new_owner and not old_owner: + self.addPlayer(bus_name, new_owner) + elif old_owner and not new_owner: + self.removePlayer(old_owner) + else: + self.changePlayerOwner(bus_name, old_owner, new_owner) + + def getBusNameFromOwner(self, owner): + player_bus_names = [ bus_name for bus_name in self._session_bus.list_names() if self.busNameIsAPlayer(bus_name) ] + for player_bus_name in player_bus_names: + player_bus_owner = self._session_bus.get_name_owner(player_bus_name) + if owner == player_bus_owner: + return player_bus_name + + def busNameIsAPlayer(self, bus_name): + return bus_name.startswith('org.mpris.MediaPlayer2') and bus_name.split('.')[3] not in self.blacklist + + def refreshPlayerList(self): + player_bus_names = [ bus_name for bus_name in self._session_bus.list_names() if self.busNameIsAPlayer(bus_name) ] + for player_bus_name in player_bus_names: + self.addPlayer(player_bus_name) + if self.connected != True: + self.connected = True + self.printQueue() + + def addPlayer(self, bus_name, owner = None): + player = Player(self._session_bus, bus_name, owner = owner, connect = self._connect, _print = self.print) + self.players[player.owner] = player + + def removePlayer(self, owner): + if owner in self.players: + self.players[owner].disconnect() + del self.players[owner] + # If there are no more players, clear the output + if len(self.players) == 0: + _printFlush(ICON_NONE) + # Else, print the output of the next active player + else: + players = self.getSortedPlayerOwnerList() + if len(players) > 0: + self.players[players[0]].printStatus() + + def changePlayerOwner(self, bus_name, old_owner, new_owner): + player = Player(self._session_bus, bus_name, owner = new_owner, connect = self._connect, _print = self.print) + self.players[new_owner] = player + del self.players[old_owner] + + # Get a list of player owners sorted by current status and age + def getSortedPlayerOwnerList(self): + players = [ + { + 'number': int(owner.split('.')[-1]), + 'status': 2 if player.status == 'playing' else 1 if player.status == 'paused' else 0, + 'owner': owner + } + for owner, player in self.players.items() + ] + return [ info['owner'] for info in reversed(sorted(players, key=itemgetter('status', 'number'))) ] + + # Get latest player that's currently playing + def getCurrentPlayer(self): + playing_players = [ + player_owner for player_owner in self.getSortedPlayerOwnerList() + if + self.players[player_owner].status == 'playing' or + self.players[player_owner].status == 'paused' + ] + return self.players[playing_players[0]] if playing_players else None + + def print(self, status, player): + self.player_states[player.bus_name] = status + + if self.connected: + current_player = self.getCurrentPlayer() + if current_player != None: + _printFlush(self.player_states[current_player.bus_name]) + else: + _printFlush(ICON_STOPPED) + else: + self.print_queue.append([status, player]) + + def printQueue(self): + for args in self.print_queue: + self.print(args[0], args[1]) + self.print_queue.clear() + + +class Player: + def __init__(self, session_bus, bus_name, owner = None, connect = True, _print = None): + self._session_bus = session_bus + self.bus_name = bus_name + self._disconnecting = False + self.__print = _print + + self.metadata = { + 'artist' : '', + 'album' : '', + 'title' : '', + 'track' : 0 + } + + self._rate = 1. + self._positionAtLastUpdate = 0. + self._timeAtLastUpdate = time.time() + self._positionTimerRunning = False + + self._metadata = None + self.status = 'stopped' + self.icon = ICON_NONE + self.icon_reversed = ICON_PLAYING + if owner is not None: + self.owner = owner + else: + self.owner = self._session_bus.get_name_owner(bus_name) + self._obj = self._session_bus.get_object(self.bus_name, '/org/mpris/MediaPlayer2') + self._properties_interface = dbus.Interface(self._obj, dbus_interface='org.freedesktop.DBus.Properties') + self._introspect_interface = dbus.Interface(self._obj, dbus_interface='org.freedesktop.DBus.Introspectable') + self._media_interface = dbus.Interface(self._obj, dbus_interface='org.mpris.MediaPlayer2') + self._player_interface = dbus.Interface(self._obj, dbus_interface='org.mpris.MediaPlayer2.Player') + self._introspect = self._introspect_interface.get_dbus_method('Introspect', dbus_interface=None) + self._getProperty = self._properties_interface.get_dbus_method('Get', dbus_interface=None) + self._playerPlay = self._player_interface.get_dbus_method('Play', dbus_interface=None) + self._playerPause = self._player_interface.get_dbus_method('Pause', dbus_interface=None) + self._playerPlayPause = self._player_interface.get_dbus_method('PlayPause', dbus_interface=None) + self._playerStop = self._player_interface.get_dbus_method('Stop', dbus_interface=None) + self._playerPrevious = self._player_interface.get_dbus_method('Previous', dbus_interface=None) + self._playerNext = self._player_interface.get_dbus_method('Next', dbus_interface=None) + self._playerRaise = self._media_interface.get_dbus_method('Raise', dbus_interface=None) + self._signals = {} + + self.refreshPosition() + self.refreshStatus() + self.refreshMetadata() + + if connect: + self.printStatus() + self.connect() + + def play(self): + self._playerPlay() + def pause(self): + self._playerPause() + def playpause(self): + self._playerPlayPause() + def stop(self): + self._playerStop() + def previous(self): + self._playerPrevious() + def next(self): + self._playerNext() + def raisePlayer(self): + self._playerRaise() + + def connect(self): + if self._disconnecting is not True: + introspect_xml = self._introspect(self.bus_name, '/') + if 'TrackMetadataChanged' in introspect_xml: + self._signals['track_metadata_changed'] = self._session_bus.add_signal_receiver(self.onMetadataChanged, 'TrackMetadataChanged', self.bus_name) + self._signals['seeked'] = self._player_interface.connect_to_signal('Seeked', self.onSeeked) + self._signals['properties_changed'] = self._properties_interface.connect_to_signal('PropertiesChanged', self.onPropertiesChanged) + + def disconnect(self): + self._disconnecting = True + for signal_name, signal_handler in list(self._signals.items()): + signal_handler.remove() + del self._signals[signal_name] + + def refreshStatus(self): + # Some clients (VLC) will momentarily create a new player before removing it again + # so we can't be sure the interface still exists + try: + self.status = str(self._getProperty('org.mpris.MediaPlayer2.Player', 'PlaybackStatus')).lower() + self.updateIcon() + self.checkPositionTimer() + except dbus.exceptions.DBusException: + self.disconnect() + + def refreshMetadata(self): + # Some clients (VLC) will momentarily create a new player before removing it again + # so we can't be sure the interface still exists + try: + self._metadata = self._getProperty('org.mpris.MediaPlayer2.Player', 'Metadata') + self._parseMetadata() + except dbus.exceptions.DBusException: + self.disconnect() + + def updateIcon(self): + self.icon = ( + ICON_PLAYING if self.status == 'playing' else + ICON_PAUSED if self.status == 'paused' else + ICON_STOPPED if self.status == 'stopped' else + ICON_NONE + ) + self.icon_reversed = ( + ICON_PAUSED if self.status == 'playing' else + ICON_PLAYING + ) + + def _print(self, status): + self.__print(status, self) + + def _parseMetadata(self): + if self._metadata != None: + # Obtain properties from _metadata + _artist = _getProperty(self._metadata, 'xesam:artist', ['']) + _album = _getProperty(self._metadata, 'xesam:album', '') + _title = _getProperty(self._metadata, 'xesam:title', '') + _track = _getProperty(self._metadata, 'xesam:trackNumber', '') + _genre = _getProperty(self._metadata, 'xesam:genre', ['']) + _disc = _getProperty(self._metadata, 'xesam:discNumber', '') + _length = _getProperty(self._metadata, 'xesam:length', 0) or _getProperty(self._metadata, 'mpris:length', 0) + _length_int = _length if type(_length) is int else int(float(_length)) + _date = _getProperty(self._metadata, 'xesam:contentCreated', '') + _year = _date[0:4] if len(_date) else '' + _url = _getProperty(self._metadata, 'xesam:url', '') + _cover = _getProperty(self._metadata, 'xesam:artUrl', '') or _getProperty(self._metadata, 'mpris:artUrl', '') + _duration = _getDuration(_length_int) + # Update metadata + self.metadata['artist'] = re.sub(SAFE_TAG_REGEX, """\1\1""", _firstIfList(_artist)) + self.metadata['album'] = re.sub(SAFE_TAG_REGEX, """\1\1""", _firstIfList(_album)) + self.metadata['title'] = re.sub(SAFE_TAG_REGEX, """\1\1""", _firstIfList(_title)) + self.metadata['track'] = _track + self.metadata['genre'] = re.sub(SAFE_TAG_REGEX, """\1\1""", _firstIfList(_genre)) + self.metadata['disc'] = _disc + self.metadata['date'] = re.sub(SAFE_TAG_REGEX, """\1\1""", _date) + self.metadata['year'] = re.sub(SAFE_TAG_REGEX, """\1\1""", _year) + self.metadata['url'] = _url + self.metadata['filename'] = os.path.basename(_url) + self.metadata['length'] = _length_int + self.metadata['cover'] = re.sub(SAFE_TAG_REGEX, """\1\1""", _firstIfList(_cover)) + self.metadata['duration'] = _duration + + def onMetadataChanged(self, track_id, metadata): + self.refreshMetadata() + self.printStatus() + + def onPropertiesChanged(self, interface, properties, signature): + updated = False + if dbus.String('Metadata') in properties: + _metadata = properties[dbus.String('Metadata')] + if _metadata != self._metadata: + self._metadata = _metadata + self._parseMetadata() + updated = True + if dbus.String('PlaybackStatus') in properties: + status = str(properties[dbus.String('PlaybackStatus')]).lower() + if status != self.status: + self.status = status + self.checkPositionTimer() + self.updateIcon() + updated = True + if dbus.String('Rate') in properties and dbus.String('PlaybackStatus') not in properties: + self.refreshStatus() + if NEEDS_POSITION and dbus.String('Rate') in properties: + rate = properties[dbus.String('Rate')] + if rate != self._rate: + self._rate = rate + self.refreshPosition() + + if updated: + self.refreshPosition() + self.printStatus() + + def checkPositionTimer(self): + if NEEDS_POSITION and self.status == 'playing' and not self._positionTimerRunning: + self._positionTimerRunning = True + GLib.timeout_add_seconds(1, self._positionTimer) + + def onSeeked(self, position): + self.refreshPosition() + self.printStatus() + + def _positionTimer(self): + self.printStatus() + self._positionTimerRunning = self.status == 'playing' + return self._positionTimerRunning + + def refreshPosition(self): + try: + time_us = self._getProperty('org.mpris.MediaPlayer2.Player', 'Position') + except dbus.exceptions.DBusException: + time_us = 0 + + self._timeAtLastUpdate = time.time() + self._positionAtLastUpdate = time_us / 1000000 + + def _getPosition(self): + if self.status == 'playing': + return self._positionAtLastUpdate + self._rate * (time.time() - self._timeAtLastUpdate) + else: + return self._positionAtLastUpdate + + def _statusReplace(self, match, metadata): + tag = match.group('tag') + format = match.group('format') + formatlen = match.group('formatlen') + text = match.group('text') + tag_found = False + reversed_tag = False + + if tag.startswith('-'): + tag = tag[1:] + reversed_tag = True + + if format is None: + tag_is_format_match = re.match(FORMAT_TAG_REGEX, tag) + if tag_is_format_match: + format = tag_is_format_match.group('format') + formatlen = tag_is_format_match.group('formatlen') + tag_found = True + if format is not None: + text = text.format_map(CleanSafeDict(**metadata)) + if format == 'w': + formatlen = int(formatlen) + text = text[:formatlen] + elif format == 't': + formatlen = int(formatlen) + if len(text) > formatlen: + text = text[:max(formatlen - len(TRUNCATE_STRING), 0)] + TRUNCATE_STRING + if tag_found is False and tag in metadata and len(metadata[tag]): + tag_found = True + + if reversed_tag: + tag_found = not tag_found + + if tag_found: + return text + else: + return '' + + def printStatus(self): + if self.status in [ 'playing', 'paused' ]: + metadata = { **self.metadata, 'icon': self.icon, 'icon-reversed': self.icon_reversed } + if NEEDS_POSITION: + metadata['position'] = time.strftime("%M:%S", time.gmtime(self._getPosition())) + # replace metadata tags in text + text = re.sub(FORMAT_REGEX, lambda match: self._statusReplace(match, metadata), FORMAT_STRING) + # restore polybar tag formatting and replace any remaining metadata tags after that + try: + text = re.sub(r'􏿿p􏿿(.*?)􏿿p􏿿(.*?)􏿿p􏿿(.*?)􏿿p􏿿', r'%{\1}\2%{\3}', text.format_map(CleanSafeDict(**metadata))) + except: + print("Invalid format string") + self._print(text) + else: + self._print(ICON_STOPPED) + + +def _dbusValueToPython(value): + if isinstance(value, dbus.Dictionary): + return {_dbusValueToPython(key): _dbusValueToPython(value) for key, value in value.items()} + elif isinstance(value, dbus.Array): + return [ _dbusValueToPython(item) for item in value ] + elif isinstance(value, dbus.Boolean): + return int(value) == 1 + elif ( + isinstance(value, dbus.Byte) or + isinstance(value, dbus.Int16) or + isinstance(value, dbus.UInt16) or + isinstance(value, dbus.Int32) or + isinstance(value, dbus.UInt32) or + isinstance(value, dbus.Int64) or + isinstance(value, dbus.UInt64) + ): + return int(value) + elif isinstance(value, dbus.Double): + return float(value) + elif ( + isinstance(value, dbus.ObjectPath) or + isinstance(value, dbus.Signature) or + isinstance(value, dbus.String) + ): + return unquote(str(value)) + +def _getProperty(properties, property, default = None): + value = default + if not isinstance(property, dbus.String): + property = dbus.String(property) + if property in properties: + value = properties[property] + return _dbusValueToPython(value) + else: + return value + +def _getDuration(t: int): + seconds = t / 1000000 + return time.strftime("%M:%S", time.gmtime(seconds)) + +def _firstIfList(_value): + return _value[0] if type(_value) is list and len(_value) else _value + +class CleanSafeDict(dict): + def __missing__(self, key): + return '{{{}}}'.format(key) + + +""" +Seems to assure print() actually prints when no terminal is connected +""" + +_last_status = '' +def _printFlush(status, **kwargs): + global _last_status + if status != _last_status: + print(status, **kwargs) + sys.stdout.flush() + _last_status = status + + + +parser = argparse.ArgumentParser() +parser.add_argument('command', help="send the given command to the active player", + choices=[ 'play', 'pause', 'play-pause', 'stop', 'previous', 'next', 'status', 'list', 'current', 'metadata', 'raise' ], + default=None, + nargs='?') +parser.add_argument('-b', '--blacklist', help="ignore a player by it's bus name. Can be be given multiple times (e.g. -b vlc -b audacious)", + action='append', + metavar="BUS_NAME", + default=[]) +parser.add_argument('-f', '--format', default='{icon} {:artist:{artist} - :}{:title:{title}:}{:-title:{filename}:}') +parser.add_argument('--truncate-text', default='…') +parser.add_argument('--icon-playing', default='⏵') +parser.add_argument('--icon-paused', default='⏸') +#parser.add_argument('--icon-stopped', default='⏹') +parser.add_argument('--icon-stopped', default='') # show no icon if stopped +parser.add_argument('--icon-none', default='') +args = parser.parse_args() + +FORMAT_STRING = re.sub(r'%\{(.*?)\}(.*?)%\{(.*?)\}', r'􏿿p􏿿\1􏿿p􏿿\2􏿿p􏿿\3􏿿p􏿿', args.format) +NEEDS_POSITION = "{position}" in FORMAT_STRING + +TRUNCATE_STRING = args.truncate_text +ICON_PLAYING = args.icon_playing +ICON_PAUSED = args.icon_paused +ICON_STOPPED = args.icon_stopped +ICON_NONE = args.icon_none + +if args.command is None: + PlayerManager(blacklist = args.blacklist) +else: + player_manager = PlayerManager(blacklist = args.blacklist, connect = False) + current_player = player_manager.getCurrentPlayer() + if args.command == 'play' and current_player: + current_player.play() + elif args.command == 'pause' and current_player: + current_player.pause() + elif args.command == 'play-pause' and current_player: + current_player.playpause() + elif args.command == 'stop' and current_player: + current_player.stop() + elif args.command == 'previous' and current_player: + current_player.previous() + elif args.command == 'next' and current_player: + current_player.next() + elif args.command == 'status' and current_player: + current_player.printStatus() + elif args.command == 'list': + print("\n".join(sorted([ + "{} : {}".format(player.bus_name.split('.')[3], player.status) + for player in player_manager.players.values() ]))) + elif args.command == 'current' and current_player: + print("{} : {}".format(current_player.bus_name.split('.')[3], current_player.status)) + elif args.command == 'metadata' and current_player: + print(_dbusValueToPython(current_player._metadata)) + elif args.command == 'raise' and current_player: + current_player.raisePlayer() diff --git a/nixos-config/system-packages.nix b/nixos-config/system-packages.nix new file mode 100644 index 00000000..e563bf81 --- /dev/null +++ b/nixos-config/system-packages.nix @@ -0,0 +1,32 @@ +{ pkgs, ... }: + +{ + # List packages installed in system profile. To search, run: + # $ nix search wget + environment.systemPackages = with pkgs; [ + #vim # an editor + neovim # text editor + fwupd # firmware updates + fwupd-efi # firmware updates additional EFI stuff + wget # get stuff from the net + gvfs # virtual file system support + #hyprland # window manager + wayland # wayland server + xdg-utils # xdg directories, open, etc + xdg-user-dirs # directories + sweet # gtk theme + capitaine-cursors # cursor theme + xfce.tumbler # image files thumbnail generator (+ base requirement) + ffmpegthumbnailer # video files thumbnail generator + qt6.qtwayland # wayland for qt6 + libsForQt5.qt5.qtwayland # wayland for at5 + polkit_gnome # graphical authentication agent for polkit + freetype # font rendering and configuration + ]; + + fonts.fonts = with pkgs; [ + fira # fira sans font + fira-code # fira code font + font-awesome # icons font + ]; +} diff --git a/nixos-config/update.sh b/nixos-config/update.sh new file mode 100755 index 00000000..0c39e787 --- /dev/null +++ b/nixos-config/update.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env sh +set -euo pipefail + +git fetch -a +git reset --hard +git clean -fd +git pull + +# regenerate hardware config +nixos-generate-config + +# rebuild system +nixos-rebuild switch