From 7804080953b7e452707fd78421d36cd6ee2942ee Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 3 Aug 2026 14:30:55 +0200 Subject: [PATCH] Add configuration for GNOME --- .bash_completion | 34 + .config/Nextcloud/README.md | 8 + .config/Nextcloud/sync-exclude.lst | 50 + .config/forge/README.md | 7 + .config/forge/config/windows.json | 136 ++ .config/git/config | 14 + .config/gnome-settings/README.md | 60 + .config/gnome-settings/apps.ini | 463 ++++++ .config/gnome-settings/key-bindings.ini | 835 +++++++++++ .config/gnome-settings/local/README.md | 62 + .config/gnome-settings/misc.ini | 530 +++++++ .config/gnome-settings/notifications.ini | 1235 +++++++++++++++++ .config/gnome-settings/shell-base.ini | 56 + .config/gnome-settings/shell-extensions.ini | 609 ++++++++ .config/systemd/user/README.md | 4 + .config/systemd/user/gnome-restore.service | 12 + .../systemd/user/night-light-reset.service | 6 + .config/systemd/user/night-light-reset.timer | 6 + .local/bin/color-palette | 1121 +++++++++++++++ .local/bin/dark-scheme-toggle | 52 + .local/bin/dconf-load | 84 ++ .local/bin/dconf-verify | 43 + .local/bin/dotfiles-install | 25 + .local/bin/evolution-on-workspace-0 | 30 + .local/bin/gnome-restore | 152 ++ .../bin/gnome-shell-extension-forge-install | 49 + .local/bin/gnome-shell-extension-forge-patch | 73 + .../bin/gnome-shell-extensions-schemas-import | 34 + .local/bin/gnome-shell-restart | 75 + .local/bin/key-bindings | 78 ++ .local/bin/move-window-to-empty-workspace | 49 + .local/bin/nextcloud-restore | 162 +++ .local/bin/night-light-make-cooler | 88 ++ .local/bin/night-light-make-warmer | 102 ++ .local/bin/night-light-reset | 25 + .local/bin/night-light-toggle-enabled | 75 + .local/bin/night-light-toggle-schedule | 145 ++ .local/bin/randomize-background | 152 ++ .local/bin/screenshot-screen | 36 + .local/bin/screenshot-ui | 25 + .local/bin/screenshot-window | 36 + .local/bin/wifi-toggle | 27 + .local/share/applications/README.md | 4 + .../applications/screenshot-screen.desktop | 7 + .../share/applications/screenshot-ui.desktop | 7 + .../applications/screenshot-window.desktop | 7 + .zshrc | 9 + 47 files changed, 6899 insertions(+) create mode 100644 .config/Nextcloud/README.md create mode 100644 .config/Nextcloud/sync-exclude.lst create mode 100644 .config/forge/README.md create mode 100644 .config/forge/config/windows.json create mode 100644 .config/gnome-settings/README.md create mode 100644 .config/gnome-settings/apps.ini create mode 100644 .config/gnome-settings/key-bindings.ini create mode 100644 .config/gnome-settings/local/README.md create mode 100644 .config/gnome-settings/misc.ini create mode 100644 .config/gnome-settings/notifications.ini create mode 100644 .config/gnome-settings/shell-base.ini create mode 100644 .config/gnome-settings/shell-extensions.ini create mode 100644 .config/systemd/user/README.md create mode 100644 .config/systemd/user/gnome-restore.service create mode 100644 .config/systemd/user/night-light-reset.service create mode 100644 .config/systemd/user/night-light-reset.timer create mode 100755 .local/bin/color-palette create mode 100755 .local/bin/dark-scheme-toggle create mode 100755 .local/bin/dconf-load create mode 100755 .local/bin/dconf-verify create mode 100755 .local/bin/evolution-on-workspace-0 create mode 100755 .local/bin/gnome-restore create mode 100755 .local/bin/gnome-shell-extension-forge-install create mode 100755 .local/bin/gnome-shell-extension-forge-patch create mode 100755 .local/bin/gnome-shell-extensions-schemas-import create mode 100755 .local/bin/gnome-shell-restart create mode 100755 .local/bin/key-bindings create mode 100755 .local/bin/move-window-to-empty-workspace create mode 100755 .local/bin/nextcloud-restore create mode 100755 .local/bin/night-light-make-cooler create mode 100755 .local/bin/night-light-make-warmer create mode 100755 .local/bin/night-light-reset create mode 100755 .local/bin/night-light-toggle-enabled create mode 100755 .local/bin/night-light-toggle-schedule create mode 100755 .local/bin/randomize-background create mode 100755 .local/bin/screenshot-screen create mode 100755 .local/bin/screenshot-ui create mode 100755 .local/bin/screenshot-window create mode 100755 .local/bin/wifi-toggle create mode 100644 .local/share/applications/README.md create mode 100644 .local/share/applications/screenshot-screen.desktop create mode 100644 .local/share/applications/screenshot-ui.desktop create mode 100644 .local/share/applications/screenshot-window.desktop diff --git a/.bash_completion b/.bash_completion index 77d8cbd..b3b9391 100644 --- a/.bash_completion +++ b/.bash_completion @@ -11,6 +11,40 @@ complete -W '--force' dotfiles-install dotfiles-update complete -W '--quieter' generate-shell-completions +_color_palette() { + local cur="$2" prev="$3" + local flags='--scheme --show --write' + + # Values for `--scheme`, in both the "--scheme value" + # and the "--scheme=value" form + case "$prev" in + --scheme) + mapfile -t COMPREPLY < <(compgen -W 'dark light' -- "$cur") + return + ;; + =) + if [ "${COMP_WORDS[COMP_CWORD - 2]}" = "--scheme" ]; then + mapfile -t COMPREPLY < <(compgen -W 'dark light' -- "$cur") + return + fi + ;; + esac + + # Offer only flags not already on the line + local flag remaining='' + for flag in $flags; do + case " ${COMP_WORDS[*]} " in + *" $flag "*) ;; + *) remaining="$remaining $flag" ;; + esac + done + + mapfile -t COMPREPLY < <(compgen -W "$remaining" -- "$cur") +} + +complete -F _color_palette color-palette + + # Fuzzy completion with the `**` trigger # (e.g., `vi **`, `ssh **`) # diff --git a/.config/Nextcloud/README.md b/.config/Nextcloud/README.md new file mode 100644 index 0000000..509cc0b --- /dev/null +++ b/.config/Nextcloud/README.md @@ -0,0 +1,8 @@ +# Nextcloud-related Configurations + +This folder contains + the [sync exclude list](./sync-exclude.lst) + for the [Nextcloud](https://nextcloud.com/) client. + +The client's other files here (e.g., `nextcloud.cfg`) are untracked + and re-created by [`nextcloud-restore`](../../.local/bin/nextcloud-restore). diff --git a/.config/Nextcloud/sync-exclude.lst b/.config/Nextcloud/sync-exclude.lst new file mode 100644 index 0000000..9e184a4 --- /dev/null +++ b/.config/Nextcloud/sync-exclude.lst @@ -0,0 +1,50 @@ +# Global exclude patterns + +*~ +~$* +.~lock.* +~*.tmp +]*.~* +]Icon\r* +].DS_Store +].ds_store +*.textClipping +._* +]Thumbs.db +]photothumb.db +System Volume Information + +.*.sw? +.*.*sw? + +].TemporaryItems +].Trashes +].DocumentRevisions-V100 +].Trash-* +.fseventd +.apdisk +.Spotlight-V100 + +.directory + +*.part +*.filepart +*.crdownload + +*.kate-swp +*.gnucash.tmp-* + +.synkron.* +.sync.ffs_db +.symform +.symform-store +.fuse_hidden* +*.unison +.nfs* + +# syncthing +.stfolder +.stignore +.stversions + +*.sb-* diff --git a/.config/forge/README.md b/.config/forge/README.md new file mode 100644 index 0000000..e295e89 --- /dev/null +++ b/.config/forge/README.md @@ -0,0 +1,7 @@ +# Forge-related Configurations + +This folder contains settings + for the [Forge GNOME shell extension](https://extensions.gnome.org/extension/4481/forge/). + +`~/.config/forge/stylesheet/forge/stylesheet.css` is untracked + and written by [`color-palette`](../../.local/bin/color-palette). diff --git a/.config/forge/config/windows.json b/.config/forge/config/windows.json new file mode 100644 index 0000000..3eeaf80 --- /dev/null +++ b/.config/forge/config/windows.json @@ -0,0 +1,136 @@ +{ + "overrides": [ + { + "wmClass": "Ibus-ui-emojier", + "mode": "float" + }, + { + "wmClass": "Nextcloud", + "mode": "float" + }, + { + "wmClass": "org.gnome.Shell.Extensions", + "wmTitle": "Forge Settings", + "mode": "float" + }, + { + "wmClass": "jetbrains-toolbox", + "mode": "float" + }, + { + "wmClass": "jetbrains-goland", + "wmTitle": "splash", + "mode": "float" + }, + { + "wmClass": "jetbrains-webstorm", + "wmTitle": "splash", + "mode": "float" + }, + { + "wmClass": "jetbrains-phpstorm", + "wmTitle": "splash", + "mode": "float" + }, + { + "wmClass": "jetbrains-datagrip", + "wmTitle": "splash", + "mode": "float" + }, + { + "wmClass": "jetbrains-rubymine", + "wmTitle": "splash", + "mode": "float" + }, + { + "wmClass": "jetbrains-idea", + "wmTitle": "splash", + "mode": "float" + }, + { + "wmClass": "com.github.amezin.ddterm", + "mode": "float" + }, + { + "wmClass": "com.github.donadigo.eddy", + "mode": "float" + }, + { + "wmClass": "Conky", + "mode": "float" + }, + { + "wmClass": "Gnome-initial-setup", + "mode": "float" + }, + { + "wmClass": "org.gnome.Calculator", + "mode": "float" + }, + { + "wmClass": "gnome-terminal-server", + "wmTitle": "Preferences – General", + "mode": "float" + }, + { + "wmClass": "gnome-terminal-preferences", + "mode": "float" + }, + { + "wmClass": "Guake", + "mode": "float" + }, + { + "wmClass": "zoom", + "mode": "float" + }, + { + "wmClass": "firefox", + "wmTitle": "About Mozilla Firefox", + "mode": "float" + }, + { + "wmClass": "firefox", + "wmTitle": "!Mozilla Firefox", + "mode": "float" + }, + { + "wmClass": "org.mozilla.firefox.desktop", + "wmTitle": "About Mozilla Firefox", + "mode": "float" + }, + { + "wmClass": "org.mozilla.firefox.desktop", + "wmTitle": "!Mozilla Firefox", + "mode": "float" + }, + { + "wmClass": "thunderbird", + "wmTitle": "About Mozilla Thunderbird", + "mode": "float" + }, + { + "wmClass": "thunderbird", + "wmTitle": "!Mozilla Thunderbird", + "mode": "float" + }, + { + "wmClass": "org.mozilla.Thunderbird.desktop", + "wmTitle": "About Mozilla Thunderbird", + "mode": "float" + }, + { + "wmClass": "org.mozilla.Thunderbird.desktop", + "wmTitle": "!Mozilla Thunderbird", + "mode": "float" + }, + { + "wmClass": "evolution-alarm-notify", + "mode": "float" + }, + { + "wmClass": "variety", + "mode": "float" + } + ] +} \ No newline at end of file diff --git a/.config/git/config b/.config/git/config index e60e973..7e28b54 100644 --- a/.config/git/config +++ b/.config/git/config @@ -350,6 +350,20 @@ ui = auto +# The named colors below follow the semantic roles defined +# in ~/.config/gnome-settings/COLORS.md: +# +# red errors, failures, deletions +# green success, additions +# yellow warnings, highlights, modifications +# blue information +# magenta special items (e.g., tags, stashes) +# cyan secondary information (e.g., remotes) +# +# The terminals have "bold is bright" turned off, +# so "bold" is only a weight and "bright" is the emphasis shade + + [color "branch"] current = cyan dim bold reverse diff --git a/.config/gnome-settings/README.md b/.config/gnome-settings/README.md new file mode 100644 index 0000000..f273dcc --- /dev/null +++ b/.config/gnome-settings/README.md @@ -0,0 +1,60 @@ +# GNOME Settings + + +This folder contains my GNOME settings + split into several `*.ini` files by logical group. + +Machine-specific overrides may live in `local/*.ini` and are loaded last. +See this [README.md](./local/README.md) for details and examples. + + +All paths inside the files are absolute, + anchored at the root of `dconf`. +Therefore, they must always be loaded anchored at the root as well. + +The files are machine independent and use `/home/user` as a placeholder, + which must be rewritten to the real home directory before loading: + +```bash +sed "s|/home/user|$HOME|g" | dconf load / +``` + + +[`gnome-restore`](../../.local/bin/gnome-restore) automates this upon every login + (via the `gnome-restore.service` systemd user unit). + +Process: +1. Back up the current `dconf` state into ~/.local/state/gnome-settings-backups/ +2. On a fresh session, remove the temporary background state + written by [`randomize-background`](../../.local/bin/randomize-background) +3. Reset all managed sub-trees, + so that keys removed from the `*.ini` files do not linger in the live state +4. Patch a broken backup path in the installed `forge` extension (`theme.js`) +5. Ensure the enabled GNOME extensions' schemas are imported +6. Load every `*.ini` here, then every `local/*.ini` +7. Apply a small [patch](../../.local/bin/gnome-shell-extension-forge-patch) + to the `forge` extension to draw borders on unfocused windows + +[`dconf-load`](../../.local/bin/dconf-load) captures step 6 as an independent utility, + or for just one file given as an argument, + which is handy while iterating on a single `*.ini`: + +```bash +dconf-load key-bindings.ini +``` + +It neither backs up nor resets anything, + so keys removed from the `*.ini` files stay active until the next `gnome-restore`. +Its purpose is to help during development of the `*.ini` files. + + +[`dconf-verify`](../../.local/bin/dconf-verify) lists keys diverging + between the live state and the `*.ini` files, + and can be used to find live keys worth adopting. + + + +## Color Palette + +`COLORS.md` is an untracked, human-readable overview of the palette, + written by [`color-palette`](../../.local/bin/color-palette). diff --git a/.config/gnome-settings/apps.ini b/.config/gnome-settings/apps.ini new file mode 100644 index 0000000..ae5ec67 --- /dev/null +++ b/.config/gnome-settings/apps.ini @@ -0,0 +1,463 @@ +# Settings for various desktop apps + +# Note that configurations for apps no longer in use +# or not yet installed are dragged along here on purpose + + + +[ca/desrt/dconf-editor] + +show-warning=false + +bookmarks=['/org/gnome/shell/extensions/'] + +behaviour='safe' + +refresh-settings-schema-source=true +small-bookmarks-rows=true + +# Always start at dconf's root +restore-view=false +saved-pathbar-path='/' +saved-view='/' + + + +[desktop/ibus/panel] + +use-custom-font=true +custom-font='Adwaita Sans 11' + + + +[org/gnome/baobab] + + +[org/gnome/baobab/ui] + +active-chart='rings' + + + +[org/gnome/calculator] + +accuracy=12 +angle-units='degrees' +base=10 + +button-mode='basic' + +number-format='fixed' + +# For exchange rates +refresh-interval=86400 + +show-thousands=true +show-zeroes=true + +source-currency='EUR' +target-currency='USD' + +source-units='degree' +target-units='radian' + +window-maximized=false + + + +[org/gnome/calendar] + +active-view='week' + +weather-settings=(true, true, '', @mv nothing) + +window-maximized=false + + + +[org/gnome/Characters] + + + +[org/gnome/clocks] + +geolocation=true + +# Keep in sync with "locations" in [org/gnome/shell/world-clocks] +world-clocks=[{'location': <(uint32 2, <('Phoenix', 'KPHX', true, [(0.58353629681163499, -1.955660817467636)], [(0.58378430809834336, -1.9560609516519483)])>)>}, {'location': <(uint32 2, <('New York', 'KNYC', true, [(0.71180344078725644, -1.2909618758762367)], [(0.71059804659265924, -1.2916478949920254)])>)>}, {'location': <(uint32 2, <('UTC', 'BIRK', true, [(1.1216380205691279, -0.38222710618675809)], [(1.1195629471197465, -0.38251373858977449)])>)>}, {'location': <(uint32 2, <('London', 'EGLL', true, [(0.89845190959903677, -0.0080529408042633736)], [(0.89899290734596930, -0.0021946055346929697)])>)>}, {'location': <(uint32 2, <('Berlin', 'EDDB', true, [(0.91426163401859872, 0.23591034304566436)], [(0.91658875132345297, 0.23387411976724018)])>)>}] + + +[org/gnome/clocks/state/window] + +maximized=false + +panel-id='world' + + + +[org/gnome/Connections] + +first-run=false + + + +[org/gnome/Contacts] + +did-initial-setup=true + +sort-on-surname=false + +window-fullscreen=false +window-maximized=false + + + +[org/gnome/control-center] + +last-panel='system' + + + +[org/gnome/Disks] + + + +[org/gnome/evince] + +auto-reload=true + +allow-links-change-zoom=false + +# Default search path when opening a document +document-directory=@ms nothing + +# Last location to save extracted images to +pictures-directory=@ms nothing + +# Ignore a PDF's own copy/print prohibitions +override-restrictions=true + +# We know what F7 does +show-caret-navigation-message=false + +# Make scrolling larger PDFs smoother +page-cache-size=uint32 512 + + +[org/gnome/evince/default] + +continuous=true + +dual-page=false +dual-page-odd-left=false + +enable-spellchecking=true + +fullscreen=false +inverted-colors=false + +show-sidebar=false +sidebar-size=128 + +sizing-mode='automatic' +zoom=1.0 + + + +[org/gnome/evolution/mail] + +use-custom-font=true + +monospace-font='FiraCode Nerd Font 12' +variable-width-font='Adwaita Sans 12' + + + +[org/gnome/gedit] + + +[org/gnome/gedit/plugins/filebrowser] + +tree-view=true + + +[org/gnome/gedit/preferences/ui] + +show-tabs-mode='auto' + + + +[org/gnome/gnome-system-monitor] + +cpu-smooth-graph=true +cpu-stacked-area-chart=false + +disks-interval=1000 +graph-update-interval=250 +network-in-bits=true + +maximized=false + +show-all-fs=true +show-dependencies=false +show-whose-processes='user' + +update-interval=1000 + + + +[org/gnome/Loupe] + +# Show the image properties sidebar +show-properties=true + + + +[org/gnome/maps] + +map-type='MapsVectorSource' + +last-viewed-location=[49.616891519885499, 9.1811720200844604] +zoom-level=13 + +show-scale=true + +transportation-type='car' + + + +[org/gnome/nautilus] + + +[org/gnome/nautilus/compression] + +default-compression-format='zip' + + +[org/gnome/nautilus/icon-view] + +captions=['size', 'none', 'none'] + +default-zoom-level='large' + + +[org/gnome/nautilus/list-view] + +default-column-order=['name', 'size', 'type', 'owner', 'group', 'permissions', 'where', 'date_modified', 'date_modified_with_time', 'date_accessed', 'recency', 'starred', 'detailed_type'] +default-visible-columns=['name', 'size', 'date_modified', 'starred'] + +default-zoom-level='small' + +use-tree-view=true + + +[org/gnome/nautilus/preferences] + +migrated-gtk-settings=true + +always-use-location-entry=true + +default-folder-viewer='list-view' + +search-filter-time-type='last_modified' + +show-create-link=false +show-delete-permanently=true + + + +[org/gnome/software] + +allow-updates=false + +download-updates=false +download-updates-notify=false + +enable-repos-dialog=true + +show-only-free-apps=false + +# `flatpak` authors must be verified +show-only-verified-apps=true + +prompt-for-nonfree=false +show-nonfree-ui=true + +installed-page-show-size=true + +first-run=false + + + +[org/gnome/terminal] + + +[org/gnome/terminal/legacy] + +always-check-default-terminal=false + +confirm-close=false + + +# [org/gnome/terminal/legacy/keybindings] +# See ~/.config/gnome-settings/key-bindings.ini for the key bindings + + +[org/gnome/terminal/legacy/profiles:] + +# Dummy value +default='12345678-1234-5678-9000-123456789000' +list=['12345678-1234-5678-9000-123456789000'] + + +[org/gnome/terminal/legacy/profiles:/:12345678-1234-5678-9000-123456789000] + +visible-name='Default Profile' + +use-custom-command=true +custom-command='/usr/bin/bash' +login-shell=false + +audible-bell=false + +use-theme-colors=false + +# The 16 ANSI colors come from ~/.config/gnome-settings/COLORS.md, +# generated by ~/.local/bin/color-palette, with these semantic roles: +# +# 1. black chrome tone, "background-like", counterpart of white +# 2. red errors, failures, deletions (e.g., `diff`) +# 3. green success, executables, additions (e.g., `diff`) +# 4. yellow warnings, highlights, modifications (e.g., `diff`) +# 5. blue information, directories +# 6. magenta keywords, constants, special items +# 7. cyan strings, symlinks, secondary information +# 8. white default foreground text +# +# Each color also has a bright shade with the same role but more emphasis +# +# In light mode the bright shades are darker, not lighter, +# because they are solved for contrast against "base", not for luminance + +# Fallback values for when theme coloring is turned off +background-color='rgb(252,234,227)' +foreground-color='rgb(42,11,1)' + +# Keep in sync with the "Terminal ANSI Colors" table in ~/.config/gnome-settings/COLORS.md +# +# Order as described above, first normal version, then the bright shade of each +palette=['rgb(235,235,237)', 'rgb(177,79,35)', 'rgb(59,121,63)', 'rgb(126,107,24)', 'rgb(39,92,179)', 'rgb(143,77,138)', 'rgb(0,121,121)', 'rgb(42,11,1)', 'rgb(252,108,39)', 'rgb(174,47,0)', 'rgb(0,111,26)', 'rgb(115,92,0)', 'rgb(0,69,190)', 'rgb(138,47,133)', 'rgb(0,106,107)', 'rgb(42,11,1)'] + +bold-is-bright=false + +use-system-font=false +font='FiraCode Nerd Font 11' + + + +[org/gnome/TextEditor] + +style-scheme='builder' + +use-system-font=false +custom-font='FiraCode Nerd Font 11' + +discover-settings=true + +highlight-current-line=true + +auto-indent=true +indent-style='space' +tab-width=4 + +wrap-text=false + +last-save-directory='file:///home/user' + +show-grid=true +show-line-numbers=true +show-map=true +show-right-margin=false + +spellcheck=true + +restore-session=false + + + +[org/gnome/Totem] + +subtitle-font='Adwaita Sans Bold 20' + + + +[org/gnome/tweaks] + +show-extensions-notice=false + + + +[org/gnome/Weather] + +# Keep in sync with "locations" in [org/gnome/shell/weather] +locations=[<(uint32 2, <('Berlin', 'EDDB', true, [(0.91426163401859872, 0.23591034304566436)], [(0.91658875132345297, 0.23387411976724018)])>)>, <(uint32 2, <('Heidelberg', 'EDFM', true, [(0.86345801419289459, 0.14862448983795826)], [(0.86238428763706787, 0.1517127801361921)])>)>, <(uint32 2, <('London', 'EGLL', true, [(0.89845190959903677, -0.0080529408042633736)], [(0.89899290734596930, -0.0021946055346929697)])>)>, <(uint32 2, <('New York', 'KNYC', true, [(0.71180344078725644, -1.2909618758762367)], [(0.71059804659265924, -1.2916478949920254)])>)>, <(uint32 2, <('Phoenix', 'KPHX', true, [(0.58353629681163499, -1.955660817467636)], [(0.58378430809834336, -1.9560609516519483)])>)>] + + +[org/gnome/GWeather4] + +default-location=('Heidelberg', 'EDFM', @m(dd) nothing) + +distance-unit='meters' +pressure-unit='hpa' +speed-unit='kph' +temperature-unit='celsius' + + + +# [org/gtk/*/settings/file-chooser] + +# This is the "GTK file-chooser dialog" + + +[org/gtk/gtk4/settings/file-chooser] +# Keep in sync with [org/gtk/settings/file-chooser] + +date-format='regular' + +location-mode='path-bar' + +show-hidden=false +show-size-column=true +show-type-column=true + +sort-column='name' +sort-directories-first=true +sort-order='ascending' + +type-format='category' + + +[org/gtk/settings/file-chooser] +# Keep in sync with [org/gtk/gtk4/settings/file-chooser] + +date-format='regular' + +location-mode='path-bar' + +show-hidden=false +show-size-column=true +show-type-column=true + +sort-column='name' +sort-directories-first=true +sort-order='ascending' + +type-format='category' + + +[org/gnome/portal/filechooser] + + +[org/gnome/portal/filechooser/com.google.Chrome] + +last-folder-path='/home/user' diff --git a/.config/gnome-settings/key-bindings.ini b/.config/gnome-settings/key-bindings.ini new file mode 100644 index 0000000..ac690d7 --- /dev/null +++ b/.config/gnome-settings/key-bindings.ini @@ -0,0 +1,835 @@ +# Unify all key bindings to conveniently keep them consistent + +# Target: `i3wm` defaults are the rough spec, +# but with "hjkl" for direction instead of "jkl;" +# and arrows mapped differently than "hjkl" +# => We make GNOME/forge bend to match +# +#-------------------------------------------------------------------------------------------------- +# Modifier Layers +#-------------------------------------------------------------------------------------------------- +# +# - Everything bound globally lives on +# +# + ... Navigate windows and workspaces, +# which includes launching the app we want to get to +# +# + ... Move windows within or to another workspace +# +# + ... Swap and float windows (GNOME/forge only) +# + primary system layer, including +# media, devices, session, power, and some more +# +# + ... Grow windows +# + secondary system layer, including +# lights, brightness, and overflow from primary +# +# + ... Shrink windows +# + tertiary layer +# +# Each group may contain a few self-explanatory exceptions +# +# - These layers are never bound globally and stay free for the active app: +# +# + ... +# +# + ... Browser extension shortcuts +# +# + ... +# +# + ... Browser extension shortcuts +# +# - F1-F12 are deliberately not bound globally, with a few exceptions, +# +# F1 to show these key bindings +# +# F4 to close windows +# +# F11 to (un-)maximize a window +# +# F1-F12 are claimed by the X11 server for switching virtual terminals +# +# - A few exceptions to the layers above are kept on purpose, +# either because a binding is so common that people expect it, +# or because a key already says on its own what it does +# +# - Note that "compose:ralt" in ~/.config/gnome-settings/misc.ini +# turns the right key into +# => Every binding must be pressed with the LEFT +# +#------------------------------------------------------------------------------------------------------------------------------------------------ +# Layers +#------------------------------------------------------------------------------------------------------------------------------------------------ +# +# - "hjkl" act on windows in the tree +# - arrows act on the "machine" +# - "WS" => "workspace" +# - Rows read from window navigation over launchers to system control, +# with the destructive session & power rows last +# +# | Navigation | Window Management | Primary System | Secondary System | Tertiary System +# | | | | | +# Key(s) | | | | | +# ------------------+------------------------+------------------------+------------------------+------------------------+------------------------ +# | Jump to window ... | Move window ... | Swap window with ... | Grow window at ... | Shrink window at ... +# k | ... up | ... up | ... window above | ... top edge | ... bottom edge +# j | ... down | ... down | ... window below | ... bottom edge | ... top edge +# h | ... left | ... left | ... window left | ... left edge | ... right edge +# l | ... right | ... right | ... window right | ... right edge | ... left edge +# | | | | | +# | | | Adjust volume ... | Adjust brightness ... | +# Up | | | ... up | ... up | +# Down | | | ... down | ... down | +# | | | | | +# | | | Play ... | Seek ... | +# Left | | | ... previous track | ... backward | +# Right | | | ... next track | ... forward | +# ------------------+------------------------+------------------------+------------------------+------------------------+------------------------ +# m | Evolution | Maximize the window | Mute the volume | Mute the microphone | +# | | | | | +# f | Files (nautilus) | Toggle fullscreen | Toggle float ... | Toggle float ... | Toggle float ... +# | | | ... for the window | ... for the app | ... for entire WS +# | | | | | +# q | | Close the window | | | +# ------------------+------------------------+------------------------+------------------------+------------------------+------------------------ +# b | Split beside | | Toggle bluetooth | Randomize wallpaper | +# v | Split below | | | | +# e | Split toggle | | Show battery load | | +# s | Stacked layout | | | | +# w | Tabbed layout | | Toggle WiFi | Toggle airplane mode | +# ------------------+------------------------+------------------------+------------------------+------------------------+------------------------ +# grave ~ ` | Toggle last two WS | | Swap last two windows | | +# | | | | | +# n | Jump to first ... | Move window to ... | | | +# | ... empty WS | ... first empty WS | | | +# | | | | | +# | Jump to WS ... | Move window to WS ... | | | +# 1 to 0 | ... No. 1 to 0 | ... No. 1 to 0 | | | +# | | | | Adjust key lights ... | +# comma < , | ... on the left | ... on the left | | ... down | +# period > . | ... on the right | ... on the right | | ... up | +# ------------------+------------------------+------------------------+------------------------+------------------------+------------------------ +# slash / ? | GNOME's app drawer | GNOME's search | GNOME's message tray | Toggle key lights | Open quick settings +# | | | | | +# Tab | Show open windows | | | | +# | | | | | +# space | Open the launcher | Run a command | Toggle media playback | | +# | | | | | +# d | Open the launcher | Run a command | | Toggle dark scheme | +# | | | | | +# Return t | Alacritty (zsh) | GNOME Terminal (bash) | | | +# | | | | | +# semicolon : ; | Type an emoji | Copy an emoji | | | +# | | | | | +# i | Firefox | Chromium | Google Chrome | Toggle touchpad | +# ------------------+------------------------+------------------------+------------------------+------------------------+------------------------ +# | | | | Make night light ... | +# bracketleft [ | | | | ... cooler | +# bracketright ] | | | | ... warmer | +# | | | | | +# backslash \ | | | | Force night light ... | Turn night light ... +# | | | | ... on/off | ... on/off +# ------------------+------------------------+------------------------+------------------------+------------------------+------------------------ +# p | | | Open screenshot UI (1) | Capture the window (1) | Capture the screen (1) +# | | | | | +# r | | | Start screen recording | | Restart GNOME shell +# ------------------+------------------------+------------------------+------------------------+------------------------+------------------------ +# Escape | | | Lock screen | Suspend with lock (2) | Hibernate with lock (2) +# | | | | | +# BackSpace Delete | | | Log out | Reboot the machine | Shutdown the machine +# | | | | | +# Home | | | Reboot the machine | | +# | | | | | +# End | | | Shutdown the machine | | +# +# (1) If a machine has a Print key, +# pressing Print (UI), Print (Window), and Print (Screen) +# also works +# (2) Bound in local/*.ini only, on machines that support it +# +#-------------------------------------------------------------------------------------------------- +# Browser extension shortcuts +#-------------------------------------------------------------------------------------------------- +# +# - Two in-app layers are used because no single layer is conflict-free: +# + dodges Firefox's menu accelerators +# and Vimium, which treats letter as the plain shifted letter +# + keeps the c/d mnemonics for Dark Reader +# +# - Set manually, NOT managed by `dconf`: +# + Chrome => chrome://extensions/shortcuts (use scope: "In Chrome") +# + Firefox => about:addons -> gear icon -> "Manage Extension Shortcuts" +# +# - Notes: +# + "compose:ralt" applies here too => Must press with the left +# + b toggles the bookmarks toolbar in Firefox +# + v shadows the browsers' "paste without formatting" +# +# Key | Extension | Action +# ------------------------+----------------+----------------------------------------------------------------------- +# l | Bitwarden | Open Bitwarden popup +# comma | Bitwarden | Generate & copy password +# period | Bitwarden | Autofill last login (i.e., "password") +# | | +# d | Dark Reader | Toggle extension for all sites +# c | Dark Reader | Toggle extension for current site only +# + + + +[org/gnome/desktop/wm/keybindings] + +activate-window-menu=@as [] + +always-on-top=@as [] + +# F4 is an exception to the F-key rule, +# because everyone expects it +close=['q', 'F4'] + +# F11 is an exception to the F-key rule, +# because everyone expects it +toggle-fullscreen=['f', 'F11'] + +toggle-maximized=['m'] +maximize=@as [] +unmaximize=@as [] + +minimize=@as [] + +switch-applications=@as [] +switch-applications-backward=@as [] + +switch-input-source=@as [] +switch-input-source-backward=@as [] + +switch-group=@as [] +switch-group-backward=@as [] +switch-panels=@as [] +switch-panels-backward=@as [] +switch-windows=@as [] +switch-windows-backward=@as [] + +cycle-group=@as [] +cycle-group-backward=@as [] +cycle-panels=@as [] +cycle-panels-backward=@as [] +# Kept mainly because other people may expect this; +# otherwise, redundant because of Tab +cycle-windows=['Tab'] +cycle-windows-backward=['Tab'] + +begin-move=@as [] +begin-resize=@as [] + +# Deprecated and ignored, listed only for completeness +panel-main-menu=@as [] + +# GNOME's own run prompt on F2, +# replaced by `rofi` on space +panel-run-dialog=@as [] + +toggle-above=@as [] +toggle-on-all-workspaces=@as [] + +show-desktop=@as [] + +# The following could also be populated automatically +# in [org/gnome/shell/extensions/space-bar/shortcuts] +# but we use the more deterministic configuration here +switch-to-workspace-1=[ '1'] +switch-to-workspace-2=[ '2'] +switch-to-workspace-3=[ '3'] +switch-to-workspace-4=[ '4'] +switch-to-workspace-5=[ '5'] +switch-to-workspace-6=[ '6'] +switch-to-workspace-7=[ '7'] +switch-to-workspace-8=[ '8'] +switch-to-workspace-9=[ '9'] +switch-to-workspace-10=['0'] +switch-to-workspace-11=@as [] +switch-to-workspace-12=@as [] +switch-to-workspace-last=@as [] + +switch-to-workspace-up=@as [] +switch-to-workspace-down=@as [] +switch-to-workspace-left=[ 'comma'] +switch-to-workspace-right=['period'] +# "hotkey" in [desktop/ibus/panel/emoji] +# must remove period from its (default) binding; +# otherwise it contradicts "switch-to-workspace-right" + +# The following could also be populated automatically +# in [org/gnome/shell/extensions/space-bar/shortcuts] +# but we use the more deterministic configuration here +move-to-workspace-1=[ '1'] +move-to-workspace-2=[ '2'] +move-to-workspace-3=[ '3'] +move-to-workspace-4=[ '4'] +move-to-workspace-5=[ '5'] +move-to-workspace-6=[ '6'] +move-to-workspace-7=[ '7'] +move-to-workspace-8=[ '8'] +move-to-workspace-9=[ '9'] +move-to-workspace-10=['0'] +move-to-workspace-11=@as [] +move-to-workspace-12=@as [] +move-to-workspace-last=@as [] + +move-to-workspace-up=@as [] +move-to-workspace-down=@as [] +move-to-workspace-left=[ 'comma'] +move-to-workspace-right=['period'] + +move-to-monitor-up=@as [] +move-to-monitor-down=@as [] +move-to-monitor-left=@as [] +move-to-monitor-right=@as [] + + + +[org/gnome/shell/extensions/forge/keybindings] + +# The latest `forge` on https://extensions.gnome.org/extension/4481/forge/ +# may break "window-toggle-float", and is currently not maintained +# => Build from source with `gnome-shell-extension-forge-install` +window-toggle-float=['f'] +window-toggle-always-float=['f'] + +window-focus-up=[ 'k'] +window-focus-down=[ 'j'] +window-focus-left=[ 'h'] +window-focus-right=['l'] + +window-move-up=[ 'k'] +window-move-down=[ 'j'] +window-move-left=[ 'h'] +window-move-right=['l'] + +window-swap-up=[ 'k'] +window-swap-down=[ 'j'] +window-swap-left=[ 'h'] +window-swap-right=['l'] + +window-swap-last-active=['grave'] + +# + hjkl pushes an edge outward (i.e., "grow") +# while + hjkl pulls the opposite edge inward (i.e., "shrink") +window-resize-top-increase=[ 'k'] +window-resize-top-decrease=[ 'j'] +window-resize-bottom-increase=['j'] +window-resize-bottom-decrease=['k'] +window-resize-left-increase=[ 'h'] +window-resize-left-decrease=[ 'l'] +window-resize-right-increase=[ 'l'] +window-resize-right-decrease=[ 'h'] + +window-snap-center=@as [] +window-snap-one-third-left=@as [] +window-snap-one-third-right=@as [] +window-snap-two-third-left=@as [] +window-snap-two-third-right=@as [] + +window-gap-size-decrease=@as [] +window-gap-size-increase=@as [] + +# Split direction is the container's axis, not the divider drawn +# - b => "beside"/"horizontal" => new window opens "beside", to the right +# - v => "vertical" => new window opens below +con-split-horizontal=['b'] +con-split-vertical=[ 'v'] + +con-split-layout-toggle=['e'] + +con-stacked-layout-toggle=['s'] + +con-tabbed-layout-toggle=['w'] +con-tabbed-showtab-decoration-toggle=@as [] + +workspace-active-tile-toggle=['f'] + +prefs-open=@as [] + +# Allow tiling via drag-and-drop with the mouse +mod-mask-mouse-tile='None' + +focus-border-toggle=@as [] +prefs-tiling-toggle=@as [] + + + +[org/gnome/mutter/keybindings] + +cancel-input-capture=@as [] + +# We must set the XF86 key here explicitly +# because by default p is also bound with it +# and we want to disable this +switch-monitor=['XF86Display'] + +toggle-tiled-left=@as [] +toggle-tiled-right=@as [] + + + +[org/gnome/settings-daemon/plugins/media-keys] + +search=['slash'] + +calculator=@as [] +control-center=@as [] +email=@as [] +help=@as [] +home=@as [] +media=@as [] +www=@as [] + + +# Session & Power related + +# "Escape" means "step away" while the session survives, +# and "the deeper the layer, the deeper the sleep" +# +# `suspend` and `hibernate` are bound per machine in local/*.ini +# because not every machine supports them: +# - `mem` and `disk` must appear in /sys/power/state +# - `hibernate` additionally needs more swap than RAM, +# with swap inside the LUKS container +# +screensaver=['Escape'] +# +# Reserved: Escape +suspend=@as [] +# +# Reserved: Escape +hibernate=@as [] + +# "BackSpace" means "end the session", +# and "the deeper the layer, the further the machine goes" +# +# "Delete" mirrors it because on many keyboards it sits in the top right corner +# while "BackSpace" is the one key guaranteed to exist +# +# "Home" and "End" say what they do on their own +# (i.e., "back to the start" and "the end"); +# so, they need no layer to express severity +# and serve as intuitive shortcuts +logout=[ 'BackSpace', 'Delete'] +reboot=[ 'BackSpace', 'Delete', 'Home'] +shutdown=['BackSpace', 'Delete', 'End'] + +# The physical power button +# is disarmed by "power-button-action" in ~/.config/gnome-settings/misc.ini +# => A binding here would do "nothing" anyways +power=@as [] + + +# Devices + +# Shows an OSD message only if a machine has a battery +battery-status=['e'] + +touchpad-toggle=['i'] +touchpad-on=@as [] +touchpad-off=@as [] + +# "Airplane mode", i.e., all radios at once +# (for WiFi only see w below) +rfkill=['w'] +rfkill-bluetooth=['b'] + + +# Audio related + +# `play` toggles, so `pause` and `stop` are not needed +play=['space'] +pause=@as [] +stop=@as [] + +eject=@as [] + +next=[ 'Right'] +previous=['Left'] + +playback-forward=['Right'] +playback-rewind=[ 'Left'] + +playback-random=@as [] +playback-repeat=@as [] + +volume-up=['Up'] +volume-up-precise=@as [] +volume-up-quiet=@as [] +volume-down=['Down'] +volume-down-precise=@as [] +volume-down-quiet=@as [] +volume-mute=['m'] +volume-mute-quiet=@as [] + +mic-mute=['m'] + + +# Screen and light related + +# "Auto rotate" +rotate-video-lock=@as [] + +toggle-contrast=@as [] + +screen-brightness-up=[ 'Up'] +screen-brightness-down=['Down'] +screen-brightness-cycle=@as [] + +magnifier=@as [] +magnifier-zoom-in=@as [] +magnifier-zoom-out=@as [] + +decrease-text-size=@as [] +increase-text-size=@as [] + +screenreader=@as [] + +on-screen-keyboard=@as [] + +keyboard-brightness-up=[ 'period'] +keyboard-brightness-down=[ 'comma'] +keyboard-brightness-toggle=['slash'] + + +# Non-default or "custom" GNOME bindings + +# Due to the structure of the below groups, +# we must duplicate some of them to allow multiple bindings + +custom-keybindings=['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/show_key_bindings/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/move_window_to_empty_workspace/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/list_windows/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/show_launcher_1/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/show_launcher_2/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/show_run_1/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/show_run_2/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_primary_terminal_1/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_primary_terminal_2/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_secondary_terminal_1/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_secondary_terminal_2/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_file_manager/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_firefox/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_chromium/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_google_chrome/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_evolution/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/dark_scheme_toggle/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/night_light_force/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/night_light_toggle/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/night_light_make_cooler/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/night_light_make_warmer/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/randomize_background/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/wifi_toggle/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/restart_gnome_shell/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/emoji_picker_type/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/emoji_picker_copy/'] + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/show_key_bindings] +binding='F1' +command='/home/user/.local/bin/key-bindings' +name='Show the key bindings' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/move_window_to_empty_workspace] +binding='n' +command='/home/user/.local/bin/move-window-to-empty-workspace' +name='Move window to first empty workspace' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/list_windows] +binding='Tab' +command='rofi -show window' +name='Show open windows' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/show_launcher_1] +binding='space' +command='rofi -show drun' +name='Open the launcher [1/2]' + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/show_launcher_2] +# i3wm's default +binding='d' +command='rofi -show drun' +name='Open the launcher [2/2]' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/show_run_1] +binding='space' +command='rofi -show run' +name='Run a command [1/2]' + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/show_run_2] +binding='d' +command='rofi -show run' +name='Run a command [2/2]' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_primary_terminal_1] +# i3wm's default, used for our primary terminal with `zsh` +binding='Return' +command='alacritty' +name='Alacritty (zsh) [1/2]' + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_primary_terminal_2] +binding='t' +command='alacritty' +name='Alacritty (zsh) [2/2]' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_secondary_terminal_1] +# Our secondary terminal with `bash` +binding='Return' +command='gnome-terminal' +name='GNOME Terminal (bash) [1/2]' + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_secondary_terminal_2] +binding='t' +command='gnome-terminal' +name='GNOME Terminal (bash) [2/2]' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_file_manager] +binding='f' +command='nautilus' +name='Files' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_firefox] +binding='i' +command='firefox-esr' +name='Firefox' + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_chromium] +binding='i' +command='chromium' +name='Chromium' + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_google_chrome] +binding='i' +command='google-chrome' +name='Google Chrome' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch_evolution] +binding='m' +command='/home/user/.local/bin/evolution-on-workspace-0' +name='Evolution' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/dark_scheme_toggle] +binding='d' +command='/home/user/.local/bin/dark-scheme-toggle' +name='Toggle dark scheme' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/night_light_force] +binding='backslash' +command='/home/user/.local/bin/night-light-toggle-schedule' +name='Force night light on/off (i.e., override the automatic schedule)' + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/night_light_toggle] +binding='backslash' +command='/home/user/.local/bin/night-light-toggle-enabled' +name='Turn night light on/off (i.e., the feature as a whole)' + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/night_light_make_cooler] +binding='bracketleft' +command='/home/user/.local/bin/night-light-make-cooler' +name='Make night light cooler' + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/night_light_make_warmer] +binding='bracketright' +command='/home/user/.local/bin/night-light-make-warmer' +name='Make night light warmer' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/randomize_background] +binding='b' +command='/home/user/.local/bin/randomize-background' +name='Randomize wallpaper' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/wifi_toggle] +binding='w' +command='/home/user/.local/bin/wifi-toggle' +name='Toggle WiFi' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/restart_gnome_shell] +binding='r' +command='/home/user/.local/bin/gnome-shell-restart' +name='Restart GNOME shell (X11 only)' + + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/emoji_picker_type] +binding='semicolon' +command='/home/user/.local/bin/rofimoji --action type' +name='Type an emoji' + +[org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/emoji_picker_copy] +binding='semicolon' +command='/home/user/.local/bin/rofimoji --action copy' +name='Copy an emoji' + + + +[org/gnome/shell/keybindings] + +# Overview is still reachable indirectly but turned off on purpose +toggle-application-view=[ 'slash'] +toggle-message-tray=[ 'slash'] +toggle-overview=@as [] +toggle-quick-settings=['slash'] + +# Take fast screenshots without manual selection first, +# as opposed to the interactive UI below +screenshot=[ 'Print', 'p'] +screenshot-window=['Print', 'p'] + +# Manually select what to screenshot ... +show-screenshot-ui=['Print', 'p'] +# ... or what to record +show-screen-recording-ui=['r'] + +focus-active-notification=@as [] + +# The defaults would collide with "screen-brightness-up/down" on + Up/Down +shift-overview-up=@as [] +shift-overview-down=@as [] + +# Disable GNOME's default behavior of opening "favorite" apps +# with 1-10 and instead switch workspaces +switch-to-application-1=@as [] +switch-to-application-2=@as [] +switch-to-application-3=@as [] +switch-to-application-4=@as [] +switch-to-application-5=@as [] +switch-to-application-6=@as [] +switch-to-application-7=@as [] +switch-to-application-8=@as [] +switch-to-application-9=@as [] +# `switch-to-application-10=@as []` does not exist by default + +open-new-window-application-1=@as [] +open-new-window-application-2=@as [] +open-new-window-application-3=@as [] +open-new-window-application-4=@as [] +open-new-window-application-5=@as [] +open-new-window-application-6=@as [] +open-new-window-application-7=@as [] +open-new-window-application-8=@as [] +open-new-window-application-9=@as [] + + + +# [org/gnome/shell/extensions/nightthemeswitcher/time] +# This extension is not in use any more and its config kept as archive + +# nightthemeswitcher-ondemand-keybinding=['d'] + + + +# [org/gnome/shell/extensions/project-hamster] +# This extension is not in use any more and its config kept as archive + +# show-hamster-dropdown=['t'] + + + +[org/gnome/shell/extensions/space-bar/shortcuts] + +# We use the `switch-to-workspace-N`s in [org/gnome/desktop/wm/keybindings] +enable-activate-workspace-shortcuts=false + +# We use the `move-to-workspace-N`s in [org/gnome/desktop/wm/keybindings] +enable-move-to-workspace-shortcuts=false + +# Jump back to the previous workspace, +# also allowing toggling between two workspaces +# (Note that "grave" is the formal word for "backtick") +activate-previous-key=['grave'] + +# Jump to the first empty workspace +activate-empty-key=['n'] + +# Use the mouse to move to the previous workspace +# by clicking on the current one (again) +back-and-forth=false + +move-workspace-left=@as [] +move-workspace-right=@as [] + +# Preferences window +open-menu=@as [] + + + +[org/gnome/terminal/legacy/keybindings] + +copy='c' +paste='v' + +# Search in GNOME's terminal: +# - terminal feature that belongs on the in-app layer +# - the bare layer must stay free for the programs running inside, +# for example, f forwards pages in `vi`, `less`, and others, +# and g aborts or shows file info +# - these bindings are also the default +find='f' +find-clear='j' +find-next='g' +find-previous='h' + +fullscreen='F11' + +# "equal" because it is below "plus" on a US keyboard +zoom-in='equal' +zoom-out='minus' +zoom-normal='0' + + +# Unset on purpose because with a tiling window manager +# we don't want to have to manage tabs inside a window + +close-tab='disabled' +close-window='disabled' + +new-tab='disabled' +new-window='disabled' + +detach-tab='disabled' +next-tab='disabled' +prev-tab='disabled' + +move-tab-left='disabled' +move-tab-right='disabled' + +switch-to-tab-1='disabled' +switch-to-tab-2='disabled' +switch-to-tab-3='disabled' +switch-to-tab-4='disabled' +switch-to-tab-5='disabled' +switch-to-tab-6='disabled' +switch-to-tab-7='disabled' +switch-to-tab-8='disabled' +switch-to-tab-9='disabled' +switch-to-tab-10='disabled' +switch-to-tab-11='disabled' +switch-to-tab-12='disabled' +switch-to-tab-13='disabled' +switch-to-tab-14='disabled' +switch-to-tab-15='disabled' +switch-to-tab-16='disabled' +switch-to-tab-17='disabled' +switch-to-tab-18='disabled' +switch-to-tab-19='disabled' +switch-to-tab-20='disabled' +switch-to-tab-21='disabled' +switch-to-tab-22='disabled' +switch-to-tab-23='disabled' +switch-to-tab-24='disabled' +switch-to-tab-25='disabled' +switch-to-tab-26='disabled' +switch-to-tab-27='disabled' +switch-to-tab-28='disabled' +switch-to-tab-29='disabled' +switch-to-tab-30='disabled' +switch-to-tab-31='disabled' +switch-to-tab-32='disabled' +switch-to-tab-33='disabled' +switch-to-tab-34='disabled' +switch-to-tab-35='disabled' +switch-to-tab-last='disabled' + + + +[desktop/ibus/panel/emoji] + +# period is the default "hotkey", but it conflicts with +# "switch-to-workspace-right" in [org/gnome/desktop/wm/keybindings] +# +# Instead of GNOME's `ibus emoji` we use `rofimoji` +# in [org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/emoji_picker_*] +# +hotkey=@as [] diff --git a/.config/gnome-settings/local/README.md b/.config/gnome-settings/local/README.md new file mode 100644 index 0000000..71d4b03 --- /dev/null +++ b/.config/gnome-settings/local/README.md @@ -0,0 +1,62 @@ +# Machine-Specific Overrides + + +`*.ini` files here are loaded *after* the shared files one level up + and may override any of their keys. + +Use them for anything tied to one machine's hardware or capabilities, + for example, suspend/hibernate bindings, HiDPI scaling, or night light warmth. + +Same rules as for the shared files: + paths are anchored at the root of `dconf`, + and `/home/user` is rewritten before loading. + +Two files are *generated* and must not be edited by hand: + +- `palette.ini` is written by [`color-palette`](../../../.local/bin/color-palette) +- `temporary_background.ini` is written by + [`randomize-background`](../../../.local/bin/randomize-background) + and removed again by + [`gnome-restore`](../../../.local/bin/gnome-restore) + on the next login + + + +## Examples + + +Suspend and hibernate bindings are reserved + in [`key-bindings.ini`](../key-bindings.ini) + but only bound here + on machines that support them: + +```ini +# local/power.ini + +[org/gnome/settings-daemon/plugins/media-keys] + +suspend=['Escape'] +hibernate=['Escape'] +``` + + +A HiDPI panel: + +```ini +# local/display.ini + +[org/gnome/desktop/interface] + +text-scaling-factor=1.25 +``` + + +A machine in a dim room that should run warmer by default: + +```ini +# local/night-light.ini + +[org/gnome/settings-daemon/plugins/color] + +night-light-temperature=uint32 1800 +``` diff --git a/.config/gnome-settings/misc.ini b/.config/gnome-settings/misc.ini new file mode 100644 index 0000000..87eba23 --- /dev/null +++ b/.config/gnome-settings/misc.ini @@ -0,0 +1,530 @@ +# Various other settings + + + +[org/freedesktop/tracker/miner/files] + +# This is the `localsearch` indexing service + +index-recursive-directories=['/home/user/Cloud'] +index-single-directories=['&DOWNLOAD'] + + + +[org/gnome/desktop/a11y] + +# Caret browsing for read-only text and GTK3 only +always-show-text-caret=false + +# Show the "Universal Access" icon even when no feature is active +always-show-universal-access-status=false + + +[org/gnome/desktop/a11y/applications] + +# Touchscreen keyboard +screen-keyboard-enabled=false + +# Master switch for [org/gnome/desktop/a11y/magnifier] below +screen-magnifier-enabled=false + +# Disable `orca` +screen-reader-enabled=false + + +[org/gnome/desktop/a11y/interface] + +# Replace the GTK theme, +# overriding our "color-scheme" in [org/gnome/desktop/interface] +high-contrast=false + +# Adds a glyph inside toggle switches, for color blindness +show-status-shapes=false + + +[org/gnome/desktop/a11y/keyboard] + +enable=false + +# Latch modifiers instead of holding them +stickykeys-enable=false + +# Only accept keys held for "slowkeys-delay" ms +slowkeys-enable=false + +# Ignore a repeat of the same key within bouncekeys-delay ms +bouncekeys-enable=false + +# Move the pointer with the numeric keypad +# (Collides with "numlock-state" in [org/gnome/desktop/peripherals/keyboard]) +mousekeys-enable=false + +# Beep when Caps/Num/Scroll Lock are toggled +togglekeys-enable=false + + +# [org/gnome/desktop/a11y/magnifier] +# Inert because "screen-magnifier-enabled" above is false + + +[org/gnome/desktop/a11y/mouse] + +# Click by resting the pointer for "dwell-time" seconds +dwell-click-enabled=false + +# Hold the left button for "secondary-click-time" seconds to right-click +secondary-click-enabled=false + + + +[org/gnome/desktop/app-folders] + +# If this is non-empty, `gnome-shell` does not recreate the default folders +folder-children=['Unused'] + + + +[org/gnome/desktop/background] + +color-shading-type='solid' + +# Keep "picture-uri"s in sync with [org/gnome/desktop/screensaver] below +picture-options='zoom' +picture-uri-dark='file:///home/user/.local/share/backgrounds/dark.jpg' +picture-uri='file:///home/user/.local/share/backgrounds/light.jpg' + +primary-color='#fceae3' +secondary-color='#fceae3' + + + +[org/gnome/desktop/break-reminders] + +# We keep the settings below as defaults +# for when we re-enable the feature again + +selected-breaks=@as [] + + +[org/gnome/desktop/break-reminders/eyesight] + +# The 20-20-20 rule: every 20 minutes, look away for 20 seconds + +countdown=false + +delay-seconds=uint32 180 +duration-seconds=uint32 20 +interval-seconds=uint32 1200 + +fade-screen=true +lock-screen=false + +notify=true +notify-overdue=true +notify-upcoming=false + +play-sound=true + + +[org/gnome/desktop/break-reminders/movement] + +# Get up and move for 5 minutes every 30 minutes + +countdown=false + +delay-seconds=uint32 180 +duration-seconds=uint32 300 +interval-seconds=uint32 1800 + +fade-screen=true +lock-screen=false + +notify=true +notify-overdue=true +notify-upcoming=true + +play-sound=true + + + +[org/gnome/desktop/calendar] + +show-weekdate=true + + + +[org/gnome/desktop/datetime] + +automatic-timezone=true + + + +[org/gnome/desktop/file-sharing] + +require-password='always' + + + +[org/gnome/desktop/input-sources] + +per-window=false + +sources=[('xkb', 'us')] + +# `xkb-options` are purely additive and there is no "off" token; +# so, whatever is listed below is enabled, and everything else is disabled +# +# Enabled: +# +# - "caps:ctrl_modifier" +# => Caps Lock becomes an additional Control for quicker access when typing +# - "compose:ralt" +# => Right Alt can be used to Compose, e.g., Compose a " gives ä +# - "lv3:menu_switch" +# => Menu becomes "AltGr", which unlocks the third level of the layout, +# and which is different from the normal modifier +# +# Alternative: +# +# - "caps:ctrl_super" +# => Caps Lock becomes an additional for quicker window navigation +# +# Deliberately NOT enabled: +# +# - "grp:alt_shift_toggle" +# => Makes a bare tap of Alt+Shift cycle the active keyboard layouts +# - "terminate:ctrl_alt_bksp" +# => Maps BackSpace to the X keysym "Terminate_Server", +# which Kills the X server instantly with no prompt and no session save +# +xkb-options=['caps:ctrl_modifier', 'compose:ralt', 'lv3:menu_switch'] + + + +[org/gnome/desktop/interface] + +accent-color='orange' + +clock-format='24h' +clock-show-date=true +clock-show-seconds=true +clock-show-weekday=true + +# Options are: +# - "default" +# + Basically a light theme +# + Some of the shell chrome remains dark (e.g., the top bar) +# + Selectable in the `gnome-control-center` +# - "prefer-dark" +# + Official dark theme +# + Selectable in the `gnome-control-center` +# - "prefer-light" +# + Unofficial light theme +# + Not selectable in the `gnome-control-center` +# + Usable via the "Dark Style" toggle in the quick settings menu +# only with the "Light Style" extension +color-scheme='prefer-light' + +font-name='Adwaita Sans 11' +document-font-name='Adwaita Sans 11' +monospace-font-name='FiraCode Nerd Font 11' + +font-antialiasing='grayscale' +font-hinting='slight' + +enable-animations=true +enable-hot-corners=false + +# `ibus` is needed for Unicode/emoji input (e.g., Ctrl+Shift+u), but +# we leave it to auto-detection in case the system does not support it +# gtk-im-module='ibus' + +gtk-theme='Adwaita' +icon-theme='Adwaita' + +show-battery-percentage=true + +toolkit-accessibility=false + + + +[org/gnome/desktop/lockdown] + +# Keep in sync with "user-switch-enabled" in [org/gnome/desktop/screensaver] +disable-user-switching=true + + + +[org/gnome/desktop/media-handling] + +automount=true +automount-open=false + +autorun-never=true +autorun-x-content-ignore=@as [] +autorun-x-content-open-folder=@as [] +autorun-x-content-start-app=@as [] + + + +[org/gnome/desktop/peripherals/keyboard] + +numlock-state=true +remember-numlock-state=true + + + +[org/gnome/desktop/peripherals/mouse] + +natural-scroll=false + + + +[org/gnome/desktop/peripherals/touchpad] + +send-events='enabled' + +edge-scrolling-enabled=false +natural-scroll=true +two-finger-scrolling-enabled=true + +tap-to-click=true + + + +[org/gnome/desktop/privacy] + +disable-camera=false +disable-microphone=false +disable-sound-output=false + +hide-identity=true + +old-files-age=uint32 7 +recent-files-max-age=7 + +remember-app-usage=true +remember-recent-files=true + +remove-old-temp-files=true +remove-old-trash-files=true + +report-technical-problems=false +send-software-usage-stats=false + +show-full-name-in-top-bar=false + +usb-protection=true + +# TODO: Uncomment only together with a populated /etc/usbguard/rules.conf, +# or every new device is blocked +# usb-protection-level='always' + + + +[org/gnome/desktop/remote-desktop] + + +[org/gnome/desktop/remote-desktop/rdp] + +enable=false +view-only=true + + +[org/gnome/desktop/remote-desktop/vnc] + +enable=false +view-only=true + + + +[org/gnome/desktop/screen-time-limits] + +daily-limit-enabled=false +daily-limit-seconds=uint32 28800 + +grayscale=false + +history-enabled=false + + + +[org/gnome/desktop/screensaver] + +color-shading-type='solid' + +# Make the machine locked after suspend +lock-enabled=true + +# Only applies to the idle path, and +# "Suspend" locks immediately, regardless of this delay +# => Together with the "idle-*" settings we follow a +# "Blank first, lock much later" strategy +lock-delay=uint32 7200 + +logout-enabled=true + +# Keep "picture-uri" in sync with [org/gnome/desktop/background] above +picture-options='zoom' +picture-uri='file:///home/user/.local/share/backgrounds/dark.jpg' + +primary-color='#000000' +secondary-color='#000000' + +show-full-name-in-top-bar=false + +status-message-enabled=false + +# Keep in sync with "disable-user-switching" in [org/gnome/desktop/lockdown] +user-switch-enabled=false + + + +[org/gnome/desktop/search-providers] + +disable-external=false + +# The disabled entries should also be the end of the sort-order +disabled=['org.gnome.Characters.desktop', 'org.gnome.clocks.desktop', 'org.gnome.seahorse.Application.desktop', 'org.gnome.Terminal.desktop'] +sort-order=['org.gnome.Nautilus.desktop', 'org.gnome.Settings.desktop', 'org.gnome.Software.desktop', 'org.gnome.Calendar.desktop', 'org.gnome.Contacts.desktop', 'org.gnome.Characters.desktop', 'org.gnome.clocks.desktop', 'org.gnome.seahorse.Application.desktop', 'org.gnome.Terminal.desktop'] + + + +[org/gnome/desktop/session] + +# Blank the screen after 10 minutes of idle time +# => Together with the other "idle-*" and the "lock-delay" settings +# we follow a "Blank first, lock much later" strategy +idle-delay=uint32 600 + + + +[org/gnome/desktop/sound] + +allow-volume-above-100-percent=true + +event-sounds=true +input-feedback-sounds=true + + + +# [org/gnome/desktop/wm/keybindings] +# See ~/.config/gnome-settings/key-bindings.ini for the key bindings + + + +[org/gnome/desktop/wm/preferences] + +audible-bell=false +visual-bell=false + +action-double-click-titlebar='toggle-maximize' +action-middle-click-titlebar='none' +action-right-click-titlebar='none' + +mouse-button-modifier='' + +# Right-drag resizes when true, and middle-drag when false +# (May only be enabled if "mouse-button-modifier" is set above) +resize-with-right-button=false + +# Just show an "X" to close a window in the GUI +button-layout='close:' + +focus-mode='click' +raise-on-click=true + +# 'strict' would withhold focus from windows launched from a terminal +focus-new-windows='smart' + +titlebar-uses-system-font=false +titlebar-font='Adwaita Sans Bold 11' + +num-workspaces=10 +workspace-names=['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'] + + + +[org/gnome/mutter] + +dynamic-workspaces=false + +# We use `forge` for tiling +edge-tiling=false + +# Keep in sync with "overlay-key" under [org/gnome/shell/extensions/just-perfection] +overlay-key='' + +workspaces-only-on-primary=true + + + +# [org/gnome/mutter/keybindings] +# See ~/.config/gnome-settings/key-bindings.ini for the key bindings + + + +[org/gnome/settings-daemon/plugins/color] + +# We add some custom logic in ~/.local/bin/night-light-* scripts +# for more convenient management of the tool via keyboard bindings + +night-light-enabled=true + +# We use the live location to determine the schedule by default, +# and use ~/.local/bin/night-light-toggle-schedule to force "Night Light" on +night-light-schedule-automatic=true + +# If automatic does not work, we fall back to something reasonable +night-light-schedule-from=18.0 +night-light-schedule-to=6.0 + +# The `$_DEFAULT_TEMPERATURE` used in the ~/.local/bin/night-light-* scripts +night-light-temperature=uint32 2200 + + + +[org/gnome/settings-daemon/plugins/media-keys] + +# See ~/.config/gnome-settings/key-bindings.ini for the key bindings + +volume-step=10 + + + +[org/gnome/settings-daemon/plugins/power] + +# Together with the "idle-delay" and "lock-delay" settings +# we follow a "Blank first, lock much later" strategy +idle-dim=true +idle-brightness=10 + +power-button-action='nothing' + +power-saver-profile-on-low-battery=true + +# 0 => "never" +sleep-inactive-ac-timeout=0 +sleep-inactive-ac-type='nothing' + +# 0 => "never" +sleep-inactive-battery-timeout=0 +sleep-inactive-battery-type='nothing' + + + +[org/gnome/system/location] + +enabled=true + + + +[system/locale] + +region='en_DK.UTF-8' + + + +[system/proxy] + +mode='none' diff --git a/.config/gnome-settings/notifications.ini b/.config/gnome-settings/notifications.ini new file mode 100644 index 0000000..1f5c351 --- /dev/null +++ b/.config/gnome-settings/notifications.ini @@ -0,0 +1,1235 @@ +# Disable most desktop notifications + + +[org/gnome/desktop/notifications] + +# "application-children" is auto-populated upon a login + +show-banners=true + +# We turn `details-in-lock-screen` off for almost all registered apps +# => If `show-in-lock-screen` is ever turned on, +# individual apps must be turned on as well, +# except for a few ones already turned on below +show-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/alacritty] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/ca-desrt-dconf-editor] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/chromium] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/com-mattjakeman-extensionmanager] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/com-nextcloud-desktopclient-nextcloud] + +enable=true +enable-sound-alerts=false + +show-banners=true +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/com-obsproject-studio] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/com-spotify-client] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/com-system76-keyboardconfigurator] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/discord] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/display-im7-q16] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/firefox-esr] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/firewall-config] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/fr-romainvigier-metadatacleaner] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-analytics-panel] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-color-panel] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-control-center] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-datetime-panel] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-firmware-panel] + +enable=true +enable-sound-alerts=false + +show-banners=true +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-language-selector] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-network-panel] + +enable=true +enable-sound-alerts=false + +show-banners=true +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-power-panel] + +enable=true +enable-sound-alerts=true + +show-banners=true +force-expanded=false + +details-in-lock-screen=true + + +[org/gnome/desktop/notifications/application/gnome-printers-panel] + +enable=true +enable-sound-alerts=false + +show-banners=true +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-session-properties] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-support-panel] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-system-monitor] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-upgrade-panel] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gnome-wellbeing-panel] + +enable=true +enable-sound-alerts=true + +show-banners=true +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/google-chrome] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gparted] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/gvim] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/htop] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/idle] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/idle-python3-13] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/im-config] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/jupyter-notebook] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/jupyterlab] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/libreoffice-base] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/libreoffice-calc] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/libreoffice-draw] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/libreoffice-impress] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/libreoffice-math] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/libreoffice-startcenter] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/libreoffice-writer] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/lynis] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/menulibre] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/nm-connection-editor] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/nvidia-settings] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-bleachbit-bleachbit] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-bpython-interpreter-bpython] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-cryptomator-cryptomator] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-freedesktop-malcontentcontrol] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-baobab] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-calculator] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-calendar] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-characters] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-clocks] + +enable=true +enable-sound-alerts=true + +show-banners=true +force-expanded=false + +details-in-lock-screen=true + + +[org/gnome/desktop/notifications/application/org-gnome-connections] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-contacts] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-diskutility] + +enable=true +enable-sound-alerts=false + +show-banners=true +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-eog] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-evince] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-evolution] + +enable=true +enable-sound-alerts=false + +show-banners=true +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-evolution-alarm-notify] + +enable=true +enable-sound-alerts=false + +show-banners=true +force-expanded=false + +details-in-lock-screen=true + + +[org/gnome/desktop/notifications/application/org-gnome-extensions] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-extensions-desktop] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-fileroller] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-font-viewer] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-gedit] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-logs] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-loupe] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-maps] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-nautilus] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-octave] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-photos] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-powerstats] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-screenshot] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-seahorse-application] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-settings] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-shell-extensions] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-snapshot] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-software] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-systemmonitor] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-terminal] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-texteditor] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-totem] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-tour] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-tweaks] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-weather] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-gnome-zenity] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-mozilla-firefox] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-mozilla-thunderbird] + +enable=true +enable-sound-alerts=false + +show-banners=true +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-pulseaudio-pavucontrol] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/org-remmina-remmina] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/r] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/ranger] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/rofi] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/rofi-theme-selector] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/rstudio] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/rustdesk] + +enable=true +enable-sound-alerts=false + +show-banners=true +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/screenkey] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/screenshot-screen] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/screenshot-ui] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/screenshot-window] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/simple-scan] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/sqlitebrowser] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/system76-driver] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/telegramdesktop] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/texmaker] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/vlc] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/yelp] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false + + +[org/gnome/desktop/notifications/application/zoom] + +enable=false +enable-sound-alerts=false + +show-banners=false +force-expanded=false + +details-in-lock-screen=false diff --git a/.config/gnome-settings/shell-base.ini b/.config/gnome-settings/shell-base.ini new file mode 100644 index 0000000..3d131fd --- /dev/null +++ b/.config/gnome-settings/shell-base.ini @@ -0,0 +1,56 @@ +# Basic GNOME Shell configuration + + + +[org/gnome/shell] + +# Always reset the command history +command-history=@as [] + +development-tools=false + +# Empty because we disable the "dash" in [org/gnome/shell/extensions/just-perfection] +favorite-apps=@as [] + +last-selected-power-profile='balanced' + +# For mounting encrypted or remote filesystems +remember-mount-password=true + +# Never show GNOME's welcome dialog again +welcome-dialog-last-shown-version='999.999' + + + +[org/gnome/shell/app-switcher] + +current-workspace-only=true + + + +# [org/gnome/shell/keybindings] +# See ~/.config/gnome-settings/key-bindings.ini for the key bindings + + + +[org/gnome/shell/weather] + +automatic-location=true + +# Keep in sync with "locations" in [org/gnome/Weather] +locations=[<(uint32 2, <('Berlin', 'EDDB', true, [(0.91426163401859872, 0.23591034304566436)], [(0.91658875132345297, 0.23387411976724018)])>)>, <(uint32 2, <('Heidelberg', 'EDFM', true, [(0.86345801419289459, 0.14862448983795826)], [(0.86238428763706787, 0.1517127801361921)])>)>, <(uint32 2, <('London', 'EGLL', true, [(0.89845190959903677, -0.0080529408042633736)], [(0.89899290734596930, -0.0021946055346929697)])>)>, <(uint32 2, <('New York', 'KNYC', true, [(0.71180344078725644, -1.2909618758762367)], [(0.71059804659265924, -1.2916478949920254)])>)>, <(uint32 2, <('Phoenix', 'KPHX', true, [(0.58353629681163499, -1.955660817467636)], [(0.58378430809834336, -1.9560609516519483)])>)>] + + + +[org/gnome/shell/window-switcher] + +app-icon-mode='app-icon-only' + +current-workspace-only=true + + + +[org/gnome/shell/world-clocks] + +# Keep in sync with "world-clocks" in [org/gnome/clocks] +locations=[<(uint32 2, <('Phoenix', 'KPHX', true, [(0.58353629681163499, -1.955660817467636)], [(0.58378430809834336, -1.9560609516519483)])>)>, <(uint32 2, <('New York', 'KNYC', true, [(0.71180344078725644, -1.2909618758762367)], [(0.71059804659265924, -1.2916478949920254)])>)>, <(uint32 2, <('UTC', 'BIRK', true, [(1.1216380205691279, -0.38222710618675809)], [(1.1195629471197465, -0.38251373858977449)])>)>, <(uint32 2, <('London', 'EGLL', true, [(0.89845190959903677, -0.0080529408042633736)], [(0.89899290734596930, -0.0021946055346929697)])>)>, <(uint32 2, <('Berlin', 'EDDB', true, [(0.91426163401859872, 0.23591034304566436)], [(0.91658875132345297, 0.23387411976724018)])>)>] diff --git a/.config/gnome-settings/shell-extensions.ini b/.config/gnome-settings/shell-extensions.ini new file mode 100644 index 0000000..e556dda --- /dev/null +++ b/.config/gnome-settings/shell-extensions.ini @@ -0,0 +1,609 @@ +# Configure all the GNOME Shell extensions + +# - "alphabetical-app-grid" and "app-hider" create a simple and flat app drawer +# - "forge" takes the major role for the desktop layout + + + +[org/gnome/shell] + +enabled-extensions=['AlphabeticalAppGrid@stuarthayhurst', 'app-hider@lynith.dev', 'appindicator-hider@theophilediot.github.io', 'appindicatorsupport@rgcjonas.gmail.com', 'drive-menu@gnome-shell-extensions.gcampax.github.com', 'forge@jmmaranan.com', 'just-perfection-desktop@just-perfection', 'launch-new-instance@gnome-shell-extensions.gcampax.github.com', 'light-style@gnome-shell-extensions.gcampax.github.com', 'no-screenshot-box@screenshot', 'panel-date-format@keiii.github.com', 'rounded-window-corners@fxgn', 'space-bar@luchrioh'] + +disabled-extensions=@as [] + +disable-user-extensions=false + + + +[org/gnome/shell/extensions/alphabetical-app-grid] + +folder-order-position='alphabetical' +sort-folder-contents=true + +logging-enabled=false + + + +[org/gnome/shell/extensions/app-hider] + +# We hide CLI utilities and rarely needed apps from the app drawer +hidden-apps=['com.nextcloud.desktopclient.nextcloud.desktop', 'display-im7.q16.desktop', 'gvim.desktop', 'htop.desktop', 'idle-python3.13.desktop', 'idle.desktop', 'im-config.desktop', 'jupyter-notebook.desktop', 'jupyterlab.desktop', 'libreoffice-startcenter.desktop', 'lynis.desktop', 'nvidia-settings.desktop', 'org.bpython-interpreter.bpython.desktop', 'org.freedesktop.MalcontentControl.desktop', 'org.gnome.Extensions.desktop', 'org.gnome.Settings.desktop', 'org.gnome.Software.desktop', 'org.gnome.Totem.desktop', 'org.gnome.Tour.desktop', 'org.gnome.tweaks.desktop', 'ranger.desktop', 'rofi-theme-selector.desktop', 'rofi.desktop', 'screenshot-screen.desktop', 'screenshot-ui.desktop', 'screenshot-window.desktop', 'system-config-printer.desktop', 'texdoctk.desktop', 'yelp.desktop'] + + + +[org/gnome/shell/extensions/appindicator] + +tray-pos='right' +tray-order=999 + +compact-mode-enabled=true + +# E.g., needed for `nextcloud` +legacy-tray-enabled=true + +icon-opacity=240 + +# 0 => Use the icon's native size +icon-size=0 + +# 0.0 => No adjustment +icon-saturation=0.0 +icon-brightness=0.0 +icon-contrast=0.0 + +# Per-indicator icon overrides (indicator-id, icon-name, attention-icon-name) +custom-icons=@a(sss) [] + + + +[org/gnome/shell/extensions/appindicator-hider] + +# Hide RustDesk's tray icon +hidden-indicators=['id:tray-icon'] + + + +# [org/gnome/shell/extensions/auto-move-windows] +# This extension is not in use any more and its config kept as archive + +# Open these apps on fixed workspaces +# where the format is ".desktop:" +# application-list=['org.gnome.Evolution.desktop:10'] + + + +# [org/gnome/shell/extensions/drive-menu] +# This extension does not have any settings + + + +[org/gnome/shell/extensions/forge] + +# "Magic number" such that forge's stylesheet at +# ~/.config/forge/stylesheet/forge/stylesheet.css +# is not rewritten with default values +css-last-update=uint32 37 + +logging-enabled=false + +primary-layout-mode='tiling' + +workspace-skip-tile='' + +tiling-mode-enabled=true + +stacked-tiling-mode-enabled=true + +tabbed-tiling-mode-enabled=true +auto-exit-tabbed=true +showtab-decoration-enabled=true + +auto-split-enabled=false +split-border-toggle=false + +dnd-center-layout='tabbed' + +float-always-on-top-enabled=true + +focus-border-toggle=true + +focus-on-hover-enabled=false +move-pointer-focus-enabled=false + +preview-hint-enabled=true +quick-settings-enabled=false + +resize-amount=uint32 32 + +window-gap-hidden-on-single=false +window-gap-size=uint32 3 +window-gap-size-increment=uint32 2 + + + +[org/gnome/shell/extensions/just-perfection] + +support-notifier-type=0 + + +# Keep in sync with "overlay-key" in [org/gnome/mutter] +overlay-key=false + +# Does not work with "overlay-key" turned off +double-super-to-appgrid=false + + +background-menu=false + +# On-screen display for volume, brightness, ... +osd=true +# Show OSD notifications in the center of the screen +osd-position=8 + +# When switching between workspaces +workspace-popup=false + + +# Windows related stuff + +window-demands-attention-focus=true + +window-maximized-on-create=false + +# Right-click in the title bar +window-menu=false +window-menu-take-screenshot-button=true + + +# Panel related stuff + +panel=true +panel-in-overview=true + +panel-notification-icon=true + +# Panel should be at the top +top-panel-position=0 + +activities-button=false + +clock-menu=true +clock-menu-position=0 +clock-menu-position-offset=0 +calendar=true +events-button=true +invert-calendar-column-items=false +weather=true +world-clock=true + +keyboard-layout=false + +accessibility-menu=false + +power-icon=true + +quick-settings=true +quick-settings-airplane-mode=true +quick-settings-backlight=true +quick-settings-dark-mode=true +quick-settings-night-light=true + +screen-recording-indicator=true +screen-sharing-indicator=true + +# "Hot Corner" animation +ripple-box=false + + +# In app drawer and overview menu + +# Do not start in overview menu after login +startup-status=0 + +search=true +type-to-search=true + +dash=false +dash-app-running=false +dash-separator=false +show-apps-button=false + +# Reduce the noise in the overview menu +window-picker-icon=false +window-preview-caption=false +window-preview-close-button=false + +workspace=false +workspaces-in-app-grid=false +workspace-peek=false +workspace-wrap-around=true + +workspace-background-corner-size=0 +workspace-switcher-should-show=false +workspace-thumbnail-to-main-view=false + + +# Look & Feel + +accent-color-icon=false + +animation=1 + +theme=false + + + +# [org/gnome/shell/extensions/launch-new-instance] +# This extension does not have any settings + + + +# [org/gnome/shell/extensions/light-style] +# This extension does not have any settings + + + +# [org/gnome/shell/extensions/nightthemeswitcher] +# This extension is not in use any more and its config kept as archive + + +# [org/gnome/shell/extensions/nightthemeswitcher/time] + +# manual-schedule=true + +# sunrise=6.0 +# sunset=18.0 + + +# [org/gnome/shell/extensions/nightthemeswitcher/cursor-variants] + +# enabled=false + + +# [org/gnome/shell/extensions/nightthemeswitcher/gtk-variants] + +# enabled=false + + +# [org/gnome/shell/extensions/nightthemeswitcher/icon-variants] + +# enabled=false + + +# [org/gnome/shell/extensions/nightthemeswitcher/shell-variants] + +# enabled=false + + +# See ~/.config/gnome-settings/key-bindings.ini for the key bindings +# nightthemeswitcher-ondemand-keybinding=['d'] + + + +[org/gnome/shell/extensions/no-screenshot-box] + +mute-screenshot-sound=true + +remove-preselected-box=true + +# On a laptop with a touchpad "true" would be too tedious +screenshot-on-release=false + + + +# [org/gnome/shell/extensions/openbar] +# This extension is not in use any more and its config kept as archive + +# Top Bar Properties +# bartype='Floating' +# position='Top' +# set-notif-position=true +# height=32.0 +# set-overview=true +# set-fullscreen=true +# fitts-widgets=true +# margin=3.0 +# set-bottom-margin=true +# bottom-margin=0.0 + +# Top bar properties when a window is maximized +# => We want the same unified bar at all times +# wmaxbar=false + +# Disable "Auto Theming" because we manage colors manually +# accent-override=false +# auto-bgalpha=false +# autohg-bar=false +# autofg-bar=false +# autofg-menu=false +# autotheme-font=false +# autotheme-refresh=false +# smbgoverride=false + +# `openbar` keeps per-mode shadow profiles (i.e., "dark-*"/"light-*" below) +# and copies them over the live keys on every dark/light toggle +# => The "Live" keys below must always match the "dark-*" profile +# because we run dark mode by default + +# "Live" Bar +# bguri='file:///home/user/.local/share/backgrounds/dark.jpg' +# bgcolor=['0.180', '0.180', '0.196'] +# bgcolor-wmax=['0.180', '0.180', '0.196'] +# bgcolor2=['0.020', '0.290', '0.333'] +# fgcolor=['0.871', '0.929', '0.945'] +# bcolor=['0.588', '0.816', '0.867'] +# hcolor=['0.024', '0.478', '0.553'] +# iscolor=['0.180', '0.180', '0.196'] +# boxcolor=['0.180', '0.180', '0.196'] +# shcolor=['0', '0', '0'] +# balpha=1.0 +# bwidth=2.0 +# bradius=12.0 +# neon=false +# default-font='FiraCode Nerd Font 12' + +# "Live" Menus +# mbgcolor=['0.180', '0.180', '0.196'] +# smbgcolor=['0.020', '0.290', '0.333'] +# mfgcolor=['0.871', '0.929', '0.945'] +# mbcolor=['0.290', '0.643', '0.714'] +# mhcolor=['0.024', '0.478', '0.553'] +# mscolor=['0.129', '0.565', '0.643'] +# mshcolor=['0', '0', '0'] + +# "Live" Accents +# accent-color=['0.129', '0.565', '0.643'] +# hscd-color=['0.129', '0.565', '0.643'] +# vw-color=['0.129', '0.565', '0.643'] +# winbcolor=['0.290', '0.643', '0.714'] + +# Dark profile +# => Keep identical to the "Live" keys above +# dark-bguri='file:///home/user/.local/share/backgrounds/dark.jpg' +# dark-bgcolor=['0.180', '0.180', '0.196'] +# dark-bgcolor-wmax=['0.180', '0.180', '0.196'] +# dark-bgcolor2=['0.020', '0.290', '0.333'] +# dark-fgcolor=['0.871', '0.929', '0.945'] +# dark-bcolor=['0.588', '0.816', '0.867'] +# dark-hcolor=['0.024', '0.478', '0.553'] +# dark-iscolor=['0.180', '0.180', '0.196'] +# dark-boxcolor=['0.180', '0.180', '0.196'] +# dark-shcolor=['0', '0', '0'] +# dark-mbgcolor=['0.180', '0.180', '0.196'] +# dark-smbgcolor=['0.020', '0.290', '0.333'] +# dark-mfgcolor=['0.871', '0.929', '0.945'] +# dark-mbcolor=['0.290', '0.643', '0.714'] +# dark-mhcolor=['0.024', '0.478', '0.553'] +# dark-mscolor=['0.129', '0.565', '0.643'] +# dark-mshcolor=['0', '0', '0'] +# dark-accent-color=['0.129', '0.565', '0.643'] +# dark-hscd-color=['0.129', '0.565', '0.643'] +# dark-vw-color=['0.129', '0.565', '0.643'] +# dark-winbcolor=['0.290', '0.643', '0.714'] + +# Light profile +# light-bguri='file:///home/user/.local/share/backgrounds/light.jpg' +# light-bgcolor=['0.180', '0.180', '0.196'] +# light-bgcolor-wmax=['0.180', '0.180', '0.196'] +# light-bgcolor2=['0.020', '0.290', '0.333'] +# light-fgcolor=['0.871', '0.929', '0.945'] +# light-bcolor=['0.588', '0.816', '0.867'] +# light-hcolor=['0.024', '0.478', '0.553'] +# light-iscolor=['0.180', '0.180', '0.196'] +# light-boxcolor=['0.180', '0.180', '0.196'] +# light-shcolor=['0', '0', '0'] +# light-mbgcolor=['0.180', '0.180', '0.196'] +# light-smbgcolor=['0.020', '0.290', '0.333'] +# light-mfgcolor=['0.871', '0.929', '0.945'] +# light-mbcolor=['0.290', '0.643', '0.714'] +# light-mhcolor=['0.024', '0.478', '0.553'] +# light-mscolor=['0.129', '0.565', '0.643'] +# light-mshcolor=['0', '0', '0'] +# light-accent-color=['0.129', '0.565', '0.643'] +# light-hscd-color=['0.129', '0.565', '0.643'] +# light-vw-color=['0.129', '0.565', '0.643'] +# light-winbcolor=['0.290', '0.643', '0.714'] + + + +[org/gnome/shell/extensions/panel-date-format] + +format='%A %B %-d, %Y %H:%M:%S %Z%z' + + + +# [org/gnome/shell/extensions/project-hamster] +# This extension is not in use any more and its config kept as archive + +# How Hamster is shown in the top bar: +# - 0 => label +# - 1 => icon +# - 2 => label and icon +# +# Note that the icon does not adapt to a light panel +# panel-appearance=0 + +# Position among the panel's items, +# where `3` means "center, next to calendar", +# which is the only option not replacing another GUI element +# panel-placement=3 + + +# See ~/.config/gnome-settings/key-bindings.ini for the key bindings +# show-hamster-dropdown=['t'] + + + +[org/gnome/shell/extensions/rounded-window-corners-reborn] + +# Rounds all window corners uniformly (e.g., needed for `alacritty`) +# +# The rings drawn by forge sit 1px outside the frame and therefore use +# a "border-radius" of 13px (12px + 1px inset) in +# ~/.config/forge/stylesheet/forge/stylesheet.css so their curves +# stay concentric with the 12px window corners +# +# "padding" stays 0 because the unfocused borders drawn by +# ~/.local/bin/gnome-shell-extension-forge-patch sit 1px outside +# the window frame (its "inset" of 1) and never cover the contents +# +custom-rounded-corner-settings=@a{sv} {} +global-rounded-corner-settings={'padding': <{'left': uint32 0, 'right': 0, 'top': 0, 'bottom': 0}>, 'keepRoundedCorners': <{'maximized': true, 'fullscreen': true}>, 'borderRadius': , 'smoothing': <0.0>, 'borderColor': <(0.6, 0.149, 0.0, 1.0)>, 'enabled': } + +focused-shadow={'horizontalOffset': 0, 'verticalOffset': 4, 'blurOffset': 28, 'spreadRadius': 3, 'opacity': 60} +unfocused-shadow={'horizontalOffset': 0, 'verticalOffset': 2, 'blurOffset': 12, 'spreadRadius': -1, 'opacity': 65} + +# `forge` draws the borders +# => This extension must not +border-width=0 + +# Round GTK3/legacy apps, +# but not libadwaita's ones that are already rounded +skip-libadwaita-app=true +skip-libhandy-app=false + +blacklist=@as [] + +enable-preferences-entry=false + +tweak-kitty-terminal=false + +debug-mode=false + + + +[org/gnome/shell/extensions/space-bar] + + +[org/gnome/shell/extensions/space-bar/appearance] + +# The colors below are the "orange"/light defaults from +# ~/.config/gnome-settings/COLORS.md and are overridden at load time +# by local/palette.ini, written by ~/.local/bin/color-palette + +custom-styles-enabled=false + +workspace-margin=2 +workspaces-bar-padding=0 + +active-workspace-font-size=10 +active-workspace-font-weight='700' +active-workspace-text-color='rgb(42,11,1)' +active-workspace-background-color='rgb(237,91,0)' +active-workspace-border-color='rgb(153,38,0)' +active-workspace-border-radius=8 +active-workspace-border-width=0 +active-workspace-padding-h=8 +active-workspace-padding-v=2 + +inactive-workspace-font-size=10 +inactive-workspace-font-weight='600' +inactive-workspace-text-color='rgb(99,18,0)' +inactive-workspace-background-color='rgb(235,235,237)' +inactive-workspace-border-color='rgb(235,235,237)' +inactive-workspace-border-radius=8 +inactive-workspace-border-width=0 +inactive-workspace-padding-h=8 +inactive-workspace-padding-v=2 + +empty-workspace-font-size=10 +empty-workspace-font-weight='600' +empty-workspace-text-color='rgb(252,108,39)' +empty-workspace-background-color='rgb(235,235,237)' +empty-workspace-border-color='rgb(235,235,237)' +empty-workspace-border-radius=8 +empty-workspace-border-width=0 +empty-workspace-padding-h=8 +empty-workspace-padding-v=2 + + +[org/gnome/shell/extensions/space-bar/behavior] + +system-workspace-indicator=false + +position='left' +position-index=0 + +indicator-style='workspaces-bar' + +always-show-numbers=false +show-empty-workspaces=false +smart-workspace-names=false + +enable-custom-label=true +enable-custom-label-in-menu=true +custom-label-named='{{name}}' +custom-label-unnamed='{{number}}' + +scroll-wheel='disabled' + +toggle-overview=false + + +# [org/gnome/shell/extensions/space-bar/shortcuts] +# See ~/.config/gnome-settings/key-bindings.ini for the key bindings + + + +# [org/gnome/shell/extensions/system-monitor] +# This extension is not in use any more and its config kept as archive + +# center-display=false +# compact-display=false +# icon-display=false +# move-clock=false +# show-tooltip=false + +# battery-display=false +# battery-hidesystem=false +# battery-show-menu=false +# battery-show-text=false +# battery-time=false + +# cpu-display=true +# cpu-show-menu=false +# cpu-show-text=true +# cpu-style='digit' +# cpu-individual-cores=false + +# disk-display=false +# disk-show-menu=false +# disk-show-text=false +# disk-style='digit' +# disk-usage-style='none' + +# fan-display=false +# fan-show-menu=false +# fan-show-text=false +# fan-style='digit' + +# freq-display=false +# freq-show-menu=false +# freq-show-text=false +# freq-style='digit' + +# gpu-display=false +# gpu-show-menu=false +# gpu-show-text=false +# gpu-style='digit' + +# memory-display=true +# memory-show-menu=false +# memory-show-text=true +# memory-style='digit' + +# net-display=false +# net-show-menu=false +# net-show-text=false +# net-style='digit' + +# swap-display=false +# swap-show-menu=false +# swap-show-text=false +# swap-style='digit' + +# thermal-display=false +# thermal-show-menu=false +# thermal-show-text=false +# thermal-style='digit' +# thermal-fahrenheit-unit=false diff --git a/.config/systemd/user/README.md b/.config/systemd/user/README.md new file mode 100644 index 0000000..03fe92d --- /dev/null +++ b/.config/systemd/user/README.md @@ -0,0 +1,4 @@ +# `systemd` User Units + +This folder contains user services and timers, + enabled by [`dotfiles-install`](../../../.local/bin/dotfiles-install). diff --git a/.config/systemd/user/gnome-restore.service b/.config/systemd/user/gnome-restore.service new file mode 100644 index 0000000..1ece994 --- /dev/null +++ b/.config/systemd/user/gnome-restore.service @@ -0,0 +1,12 @@ +[Unit] +Description=Restore of the GNOME settings upon login +Before=graphical-session-pre.target +ConditionPathIsDirectory=%h/.config/gnome-settings + +[Service] +Environment=FRESH_SESSION=1 +Type=oneshot +ExecStart=%h/.local/bin/gnome-restore + +[Install] +WantedBy=graphical-session-pre.target diff --git a/.config/systemd/user/night-light-reset.service b/.config/systemd/user/night-light-reset.service new file mode 100644 index 0000000..0acdb17 --- /dev/null +++ b/.config/systemd/user/night-light-reset.service @@ -0,0 +1,6 @@ +[Unit] +Description=Return "Night Light" to the automatic schedule and default warmth + +[Service] +Type=oneshot +ExecStart=%h/.local/bin/night-light-reset diff --git a/.config/systemd/user/night-light-reset.timer b/.config/systemd/user/night-light-reset.timer new file mode 100644 index 0000000..54b6661 --- /dev/null +++ b/.config/systemd/user/night-light-reset.timer @@ -0,0 +1,6 @@ +[Timer] +OnCalendar=03:00 +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/.local/bin/color-palette b/.local/bin/color-palette new file mode 100755 index 0000000..adb4f67 --- /dev/null +++ b/.local/bin/color-palette @@ -0,0 +1,1121 @@ +#!/usr/bin/env python3 + +# This file's name matches the CLI convention in ~/.local/bin +# pylint: disable=invalid-name + +# Templates for six consumers live here +# => Length of file is managable +# pylint: disable=too-many-lines + +"""Derive a color palette from GNOME's accent color. + +References to GNOME mean references to libadwaita to be precise. +""" + +import argparse +import math +import os +import pathlib +import subprocess +import sys +import tempfile +import textwrap +from collections.abc import Container + +import coloraide # pylint: disable=import-error + +# Number of equally spaced stops between the pinned +# `LIGHTNESS_DARK_END` and `LIGHTNESS_LIGHT_END` +N_MAJOR_STOPS = 7 + +# Roles as (dark, light) indexes into the `ramp` +# => Initialized as mirrors (i.e., `(k, `-(k + 1)`) +ROLES = { + "base": (0, -1), + "surface-dim": (1, -2), + "emphasis-dim": (2, -3), + "accent-area": (3, -4), + "active-border": (4, -5), + "chrome-quiet": (5, -6), + "text": (6, -7), +} + + +# We pin the ends of the lightness grid, +# independent of the anchor's own lightness: +# - The light end is a tinted near-white, far enough from white so that +# the accent's tint stays visible, +# themed surfaces remain distinguishable from GNOME's white surfaces (~0.97), +# and light text on dark surfaces does not glare +# - The dark end is a deep base, clearly lighter than pure black, +# which stays reserved for the screensaver and shadows, +# and darker than GNOME's dark chrome (~0.32) so that +# bars and menus visibly float above the desktop and terminal base +LIGHTNESS_DARK_END = 0.200 +LIGHTNESS_LIGHT_END = 0.950 + + +# Chroma (i.e., "How colorful is a color?"; 0 => "gray") of the extreme stops +# => We use a `bell()` to vary the chroma across all stops so that +# the accent is the most colorful +CHROMA_DARK_END = 0.055 # Higher because hue is hard to see in dark colors +CHROMA_LIGHT_END = 0.022 # Lower because a subtle tint is already visible near white + + +# Lightness distance between the normal and the bright ANSI shade, +# as (dark, light) pairs +# => Smaller in light mode, where "bright" must go darker for contrast +# and the emphasis is carried by extra chroma instead (see below) +ANSI_BRIGHT_LIFT = (0.10, 0.05) + +# Chroma multiplier for the bright ANSI shades, as (dark, light) pairs +# => In light mode "brighter" reads as "more vivid" rather than "darker" +ANSI_BRIGHT_CHROMA_FACTOR = (1.0, 1.35) + + +# Cap for the ANSI accents' chroma so that the palette stays overall calm +ANSI_CHROMA_MAX = { + "red": 0.14, + "yellow": 0.10, + "green": 0.11, + "cyan": 0.10, + "blue": 0.15, + "magenta": 0.12, +} + +# WCAG 2.1 contrast the ANSI accents must reach against the `base` +# => Blue-heavy hues get a higher floor so they survive night light, +# which attenuates the blue channel screen-wide +ANSI_CONTRAST = { + "red": 4.5, + "yellow": 4.5, + "green": 4.5, + "cyan": 4.5, + "blue": 5.5, + "magenta": 5.0, +} + +# Hue angles (in "oklch": degrees on the color wheel) +# for the terminal's semantic ANSI colors +# => Fixed taste, deliberately not influenced by the anchor, +# except when the anchor's hue is within `ANSI_HUE_REUSE_MAX`, +# in which case the anchor's hue is used instead +ANSI_HUES = { + "red": 25, + "yellow": 95, + "green": 145, + "cyan": 195, + "blue": 260, + "magenta": 330, +} + +# Maximum circular hue distance (in degrees) for re-using the anchor's hue +ANSI_HUE_REUSE_MAX = 20 + +_ANSI_ORDER = [ + "black", + "red", + "green", + "yellow", + "blue", + "magenta", + "cyan", + "white", +] + + +# GNOME's nine built-in accents, used as anchor for the palette to be created +# (See https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/css-variables.html#accent-colors) +GNOME_ACCENTS = { + "blue": "#3584e4", + "green": "#3a944a", + "orange": "#ed5b00", + "pink": "#d56199", + "purple": "#9141ac", + "red": "#e62d42", + "slate": "#6f8396", + "teal": "#2190a4", + "yellow": "#c88800", +} + +DEFAULT_GNOME_ACCENT = "orange" + +# GNOME's built-in neutral surfaces as (dark, light) pairs, +# adopted verbatim so that our themed elements sit naturally next them +GNOME_NEUTRALS = { + "chrome": ("#2e2e32", "#ebebed"), # --headerbar or --sidebar-bg-color + "chrome-2": ("#28282c", "#f3f3f5"), # --secondary-sidebar-bg-color + "popover": ("#36363a", "#ffffff"), # --popover-bg-color + "view": ("#1d1d20", "#ffffff"), # --view-bg-color + "window": ("#222226", "#fafafb"), # --window-bg-color +} + + +_LIGHTNESS_RESOLUTION = 0.001 + + +# Corner geometry: +# - Windows are clipped to `WINDOW_RADIUS` +# by the rounded-window-corners-reborn extension +# - forge's stock focused rings sit `FOCUSED_INSET` outside the frame, +# while the patched unfocused hairline sits `BORDER_INSET` outside +# => Flush rings need radius + their own inset +# - `BORDER_INSET` must match ~/.local/bin/gnome-shell-extension-forge-patch +# - `FOCUSED_INSET` must match `inset` in forge's window.js (currently 3) +WINDOW_RADIUS = 12 +BORDER_INSET = 1 +FOCUSED_INSET = 3 + + +# Template for the stylesheet used by the GNOME shell extension `forge` +FORGE_CSS = textwrap.dedent("""\ + .color-palette-metadata {{ + /* Written by ~/.local/bin/color-palette */ + /* accent={accent_name} scheme={scheme_name} */ + }} + + .window-unfocused-border {{ + /* Unfocused windows are marked by a hairline only: */ + /* - Shadows from rounded-window-corners-reborn carry the focus distinction */ + /* - Width is kept > 0 so the patched border code in forge stays exercised */ + border-width: 1px; + border-color: {chrome_quiet}; + border-style: solid; + border-radius: {unfocused_ring_radius}; + }} + + .tiled {{ + color: {text}; + opacity: 1; + border-width: 3px; + }} + + .split {{ + color: {emphasis_dim}; + opacity: 1; + border-width: 3px; + }} + + .stacked {{ + color: {emphasis_dim}; + opacity: 1; + border-width: 3px; + }} + + .tabbed {{ + color: {emphasis_dim}; + opacity: 1; + border-width: 3px; + }} + + .floated {{ + color: {emphasis_dim}; + border-width: 3px; + opacity: 1; + }} + + .window-tiled-border {{ + /* The focused tiled ring is the one place besides space-bar's */ + /* active workspace that carries the full accent; all other */ + /* focused rings (split, stacked, tabbed, floated) use */ + /* emphasis-dim instead */ + border-width: 4px; + border-color: {accent}; + border-style: solid; + border-radius: {ring_radius}; + }} + + .window-split-border {{ + border-width: 4px; + border-color: {emphasis_dim}; + border-style: solid; + border-radius: {ring_radius}; + }} + + .window-split-horizontal {{ + border-left-width: 0; + border-top-width: 0; + border-bottom-width: 0; + }} + + .window-split-vertical {{ + border-left-width: 0; + border-top-width: 0; + border-right-width: 0; + }} + + .window-stacked-border {{ + border-width: 4px; + border-color: {emphasis_dim}; + border-style: solid; + border-radius: {ring_radius}; + }} + + .window-tabbed-border {{ + border-width: 4px; + border-color: {emphasis_dim}; + border-style: solid; + border-radius: {ring_radius}; + }} + + .window-tabbed-bg {{ + border-radius: 12px; + }} + + .window-tabbed-tab {{ + background-color: {surface_dim}; + border-color: {surface_dim_a60}; + border-width: 1px; + border-radius: 12px; + color: {chrome_quiet}; + margin: 1px; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); + }} + + .window-tabbed-tab-active {{ + background-color: {emphasis_dim}; + color: {text}; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); + }} + + .window-tabbed-tab-close {{ + padding: 3px; + margin: 4px; + border-radius: 12px; + width: 12px; + }} + + .window-tabbed-tab-icon {{ + margin: 3px; + }} + + .window-floated-border {{ + border-width: 4px; + border-color: {emphasis_dim}; + border-style: solid; + border-radius: {ring_radius}; + }} + + .window-tilepreview-tiled {{ + border-width: 1px; + border-color: {accent_a40}; + border-style: solid; + border-radius: {ring_radius}; + background-color: {accent_a30}; + }} + + .window-tilepreview-stacked {{ + border-width: 1px; + border-color: {emphasis_dim_a40}; + border-style: solid; + border-radius: {ring_radius}; + background-color: {emphasis_dim_a30}; + }} + + .window-tilepreview-swap {{ + border-width: 1px; + border-color: {active_border_a40}; + border-style: solid; + border-radius: {ring_radius}; + background-color: {active_border_a40}; + }} + + .window-tilepreview-tabbed {{ + border-width: 1px; + border-color: {emphasis_dim_a40}; + border-style: solid; + border-radius: {ring_radius}; + }} + """) + + +def as_ansi24(hex_value: str) -> str: + """Format a hex color as a 24-bit ANSI code ("38;2;R;G;B").""" + red, green, blue = (int(hex_value[i : i + 2], 16) for i in (1, 3, 5)) + return f"38;2;{red};{green};{blue}" + + +def as_hex(hex_value: str) -> str: + """Format a hex color as itself. + + This function is useful as a named dummy where a callback is needed. + """ + return hex_value + + +def as_rgb(hex_value: str) -> str: + """Format a hex color as an "rgb(r,g,b)" string. + + Example: rgb(33,144,164) + """ + red, green, blue = (int(hex_value[i : i + 2], 16) for i in (1, 3, 5)) + return f"rgb({red},{green},{blue})" + + +def as_rgba(hex_value: str, *, alpha: float) -> str: + """Format a hex color as an "rgba(r, g, b, a)" string.""" + red, green, blue = (int(hex_value[i : i + 2], 16) for i in (1, 3, 5)) + return f"rgba({red}, {green}, {blue}, {alpha})" + + +# Consumers of the palette, keyed by `dconf` path +# => Each entry maps a dconf key to a (role, formatter) pair +CONSUMERS = { + "org/gnome/desktop/background": { + "primary-color": ("base", as_hex), + "secondary-color": ("base", as_hex), + }, + "org/gnome/desktop/screensaver": { + "primary-color": ("black", as_hex), + "secondary-color": ("black", as_hex), + }, + "org/gnome/shell/extensions/space-bar/appearance": { + "active-workspace-background-color": ("accent", as_rgb), + "active-workspace-border-color": ("active-border", as_rgb), + "active-workspace-text-color": ("text", as_rgb), + "empty-workspace-background-color": ("chrome", as_rgb), + "empty-workspace-border-color": ("chrome", as_rgb), + "empty-workspace-text-color": ("emphasis-dim", as_rgb), + "inactive-workspace-background-color": ("chrome", as_rgb), + "inactive-workspace-border-color": ("chrome", as_rgb), + "inactive-workspace-text-color": ("chrome-quiet", as_rgb), + }, + "org/gnome/terminal/legacy/profiles:/:12345678-1234-5678-9000-123456789000": { + "background-color": ("base", as_rgb), + "foreground-color": ("text", as_rgb), + }, +} + + +def bell( + lightness: float, + *, + peak_lightness: float, + peak_chroma: float, +) -> float: + """Return the chroma at the `lightness`. + + We make chroma follow a parabola over lightness for our palette: + It peaks at `peak_chroma` (i.e., at the anchor's lightness) + and falls off to the tails at the grid's ends. + So, every stop is a shade of the `accent`, + and none is more colorful than the `accent` itself. + + Beyond the tails (e.g., for extra dark surface stops), + the chroma is held at the tail values. + """ + if lightness >= peak_lightness: + end = CHROMA_LIGHT_END + span = LIGHTNESS_LIGHT_END - peak_lightness + else: + end = CHROMA_DARK_END + span = peak_lightness - LIGHTNESS_DARK_END + + # If the `anchor` is at or beyond a pinned end, + # we have no falloff to compute + if span <= 0: + return end + + # `t` is the position between the peak (`t=0`) and an end (`t=1`), + # clamped at `1` so lightnesses beyond the end keep the tail chroma + t = min(abs(lightness - peak_lightness) / span, 1.0) + + return peak_chroma - (peak_chroma - end) * (t**2) + + +def hexify( + *, + lightness: float, + chroma: float, + hue: float, +) -> str: + """Convert an "oklch" color to a lower-case sRGB hex string. + + OKLCH can describe colors that sRGB cannot display, + mostly dark ones with high chroma. Such colors are made displayable + by reducing only their chroma (i.e., toward gray) until they + just fit into sRGB, keeping lightness and hue as requested. + So, a fitted stop is exactly as dark and exactly + in the anchor's hue as designed, just a little less colorful. + """ + return ( + coloraide.Color("oklch", (lightness, chroma, hue)) + .convert("srgb") + .fit(method="oklch-chroma") + .to_string(hex=True, upper=False) + ) + + +def hue_distance(angle_a: float, angle_b: float) -> float: + """Return the circular distance between two hue angles in degrees.""" + angle_distance = abs(angle_a - angle_b) % 360 + return min(angle_distance, 360 - angle_distance) + + +def lift( + *, + lightness: float, + chroma: float, + hue: float, + against: str, + target_contrast: float, +) -> float: + """Return the `lightness` at which `hue` reads on `against`. + + Starting from the given `lightness` (i.e., the anchor's lightness), + move away from the `against` color until the `target_contrast` + is reached. In simple terms, we make a color lighter on a dark + surface and darker on a light one. + + The minimal such lightness is found via bisection, exploiting that + WCAG contrast grows monotonically when moving away from the surface. + """ + surface = coloraide.Color(against) + surface_lightness = surface.convert("oklch")["lightness"] + + def contrast_at(candidate: float) -> float: + color = coloraide.Color("oklch", (candidate, chroma, hue)) + color.fit("srgb", method="oklch-chroma") + return color.contrast(surface, method="wcag21") + + # Already good at the lightness of the `anchor` + if contrast_at(lightness) >= target_contrast: + return lightness + + # `far` is the extreme lightness away from the surface + far = 1.0 if surface_lightness < lightness else 0.0 + + # Even the extreme cannot reach the target contrast + if contrast_at(far) < target_contrast: + return far + + # Bisect between "not enough contrast" (low) and "enough" (high) + low, high = lightness, far + + # Halving the interval below the `_LIGHTNESS_RESOLUTION` suffices + # because one 8-bit sRGB channel step spans roughly 0.002-0.004 in OKLCH + # lightness; so, finer results cannot change the final hex anymore + n_bisect_steps = math.ceil(math.log2(1 / _LIGHTNESS_RESOLUTION)) # => 10 + + for _ in range(n_bisect_steps): + mid = (low + high) / 2 + if contrast_at(mid) < target_contrast: + low = mid + else: + high = mid + + # `high` is on the "enough" side => Contrast is guaranteed + return high + + +def read_gnome( + key: str, + *, + valid_values: Container[str], + default: str = "", + section: str = "org.gnome.desktop.interface", +) -> str: + """Read the live GNOME setting `key`, restricted to `valid_values`. + + Reads a key from the [section] via `gsettings` and + returns `default` if reading fails or yields an unknown value. + """ + try: + out = subprocess.run( + ["gsettings", "get", section, key], + capture_output=True, + text=True, + check=True, + ).stdout + except (OSError, subprocess.CalledProcessError): + return default + + value = out.strip().strip("'\"").lower() + + if value not in valid_values: + print( + f"WARN: {key} is {value!r} => Falling back to {default!r}", + file=sys.stderr, + ) + return default + + return value + + +def stop( + lightness: float, + *, + anchor_lightness: float, + anchor_chroma: float, + anchor_hue: float, +) -> str: + """Compute the hex value of the grid stop at `lightness`.""" + return hexify( + lightness=lightness, + chroma=bell( + lightness, + peak_lightness=anchor_lightness, + peak_chroma=anchor_chroma, + ), + hue=anchor_hue, + ) + + +def derive_ansi_colors( + *, + anchor: coloraide.Color, + base: str, + is_dark: bool, +) -> dict[str, str]: + """Derive the terminal's semantic ANSI colors. + + The colors are solved against the `base` so that + they reach `ANSI_CONTRAST` in the current scheme. + """ + colors = {} + + # The (dark, light) constants, resolved for the current scheme + mode = 0 if is_dark else 1 + bright_lift = ANSI_BRIGHT_LIFT[mode] + + for name, ansi_hue in ANSI_HUES.items(): + + ansi_chroma = ANSI_CHROMA_MAX[name] + bright_chroma = ansi_chroma * ANSI_BRIGHT_CHROMA_FACTOR[mode] + + # Re-use the anchor's hue if it is close enough + # => One color, two roles + if hue_distance(ansi_hue, anchor["hue"]) <= ANSI_HUE_REUSE_MAX: + ansi_hue = anchor["hue"] + + normal_lightness = lift( + lightness=anchor["lightness"], + chroma=ansi_chroma, + hue=ansi_hue, + against=base, + target_contrast=ANSI_CONTRAST[name], + ) + + bright_lightness = normal_lightness + (bright_lift if is_dark else -bright_lift) + + colors[f"{name}-normal"] = hexify( + lightness=normal_lightness, + chroma=ansi_chroma, + hue=ansi_hue, + ) + + colors[f"{name}-bright"] = hexify( + lightness=bright_lightness, + chroma=bright_chroma, + hue=ansi_hue, + ) + + return colors + + +def derive_colors(*, scheme_override: str = "") -> dict[str, str]: + """Derive all palette colors from the live GNOME settings.""" + colors = { + "black": "#000000", # Screensaver, shadows + "white": "#ffffff", + } + + accent = read_gnome( + "accent-color", + valid_values=set(GNOME_ACCENTS), + default=DEFAULT_GNOME_ACCENT, + ) + + if scheme_override: + scheme = f"prefer-{scheme_override}" + else: + scheme = read_gnome( + "color-scheme", + valid_values={ + "default", # ~ "prefer-light" + "prefer-light", + "prefer-dark", + }, + default="prefer-dark", + ) + + is_dark = scheme == "prefer-dark" + + # The `anchor` seeds the palette: + # - its "hue" is used for all stops, and + # - its "chroma" becomes the `peak_chroma` for the `bell()` + anchor = coloraide.Color(GNOME_ACCENTS[accent]).convert("oklch") + + # Built up the `colors` palette + + # 0) The chosen GNOME accent itself + colors["accent"] = GNOME_ACCENTS[accent] + + # 1) GNOME's built-in colors + for neutral, (dark_hex, light_hex) in GNOME_NEUTRALS.items(): + colors[neutral] = dark_hex if is_dark else light_hex + + # 2) The `ramp`, equally spaced stops between the pinned ends, + # ordered from dark (=> 0) to light (=> `N_MAJOR_STOPS` - 1) + + ramp = [ + stop( + LIGHTNESS_DARK_END + + k * (LIGHTNESS_LIGHT_END - LIGHTNESS_DARK_END) / (N_MAJOR_STOPS - 1), + anchor_lightness=anchor["lightness"], + anchor_chroma=anchor["chroma"], + anchor_hue=anchor["hue"], + ) + for k in range(N_MAJOR_STOPS) + ] + + for role, (dark_index, light_index) in ROLES.items(): + colors[role] = ramp[dark_index if is_dark else light_index] + + # 3) The terminal's semantic ANSI colors, solved against the current `base` + + colors.update( + derive_ansi_colors( + anchor=anchor, + base=colors["base"], + is_dark=is_dark, + ) + ) + + # The non-hue ANSI slots, aliased to `ramp` roles + # => One color, two roles + colors["black-normal"] = colors["chrome"] + colors["black-bright"] = colors["emphasis-dim"] + colors["white-normal"] = colors["text"] + colors["white-bright"] = colors["text"] + + # 4) Expose some metadata for `main()`'s summary line + colors["_accent"] = accent + colors["_scheme"] = scheme + + return colors + + +def render_alacritty(colors: dict[str, str]) -> str: + """Render the colors imported by ~/.config/alacritty/alacritty.toml.""" + lines = [ + "# Written by ~/.local/bin/color-palette", + f"# accent={colors['_accent']} scheme={colors['_scheme']}", + "", + "[colors]", + "", + "draw_bold_text_with_bright_colors = false", + "", + "[colors.primary]", + f'background = "{colors["base"]}"', + f'foreground = "{colors["text"]}"', + "", + "[colors.selection]", + f'background = "{colors["surface-dim"]}"', + f'text = "{colors["text"]}"', + "", + "[colors.cursor]", + f'text = "{colors["base"]}"', + f'cursor = "{colors["text"]}"', + ] + + for shade in ("normal", "bright"): + lines += ["", f"[colors.{shade}]"] + + for name in _ANSI_ORDER: + lines.append(f'{name:<7} = "{colors[f"{name}-{shade}"]}"') + + lines += ["", "[bell]", f'color = "{colors["emphasis-dim"]}"'] + + return "\n".join(lines) + "\n" + + +def render_dconf(colors: dict[str, str]) -> str: + """Render the `dconf` overrides for ~/.config/gnome-settings/local/.""" + lines = [ + "# Written by ~/.local/bin/color-palette", + f"# accent={colors['_accent']} scheme={colors['_scheme']}", + "", + "", + "[org/gnome/desktop/interface]", + "", + f"accent-color='{colors['_accent']}'", + f"color-scheme='{colors['_scheme']}'", + ] + + for dconf_path, keys in CONSUMERS.items(): + lines += ["", "", f"[{dconf_path}]", ""] + + for key, (role, formatter) in keys.items(): + lines.append(f"{key}='{formatter(colors[role])}'") + + # The terminal's 16-slot ANSI palette + if "terminal" in dconf_path: + slots = [ + f"'{as_rgb(colors[f'{name}-normal'])}'" for name in _ANSI_ORDER + ] + [f"'{as_rgb(colors[f'{name}-bright'])}'" for name in _ANSI_ORDER] + lines.append(f"palette=[{', '.join(slots)}]") + + lines.append("") + + return "\n".join(lines) + + +def render_fastfetch(colors: dict[str, str]) -> str: + """Render ~/.config/fastfetch/config.jsonc.""" + return textwrap.dedent(f"""\ + // Written by ~/.local/bin/color-palette + // accent={colors['_accent']} scheme={colors['_scheme']} + // + // See: https://github.com/fastfetch-cli/fastfetch + + {{ + "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", + + "logo": {{ + "type": "small", + "color": {{ + "1": "{as_ansi24(colors['accent'])}", + "2": "{as_ansi24(colors['active-border'])}" + }}, + "padding": {{ + "top": 1, + "right": 4 + }} + }}, + + "display": {{ + "separator": " ", + "color": {{ + "keys": "{as_ansi24(colors['accent'])}", + "title": "{as_ansi24(colors['text'])}", + "separator": "{as_ansi24(colors['surface-dim'])}" + }}, + "key": {{ + "width": 12 + }} + }}, + + "modules": [ + "title", + "separator", + + "os", + "kernel", + "uptime", + "packages", + "shell", + + "break", + + "cpu", + "memory", + {{ + "type": "disk", + "folders": "/" + }}, + "localip", + + "break", + + "colors" + ] + }} + """) + + +def render_forge_css(colors: dict[str, str]) -> str: + """Render ~/.config/forge/stylesheet/forge/stylesheet.css.""" + return FORGE_CSS.format( + accent=as_rgb(colors["accent"]), + accent_a30=as_rgba(colors["accent"], alpha=0.3), + accent_a40=as_rgba(colors["accent"], alpha=0.4), + accent_name=colors["_accent"], + active_border_a40=as_rgba(colors["active-border"], alpha=0.4), + chrome_quiet=as_rgb(colors["chrome-quiet"]), + emphasis_dim=as_rgb(colors["emphasis-dim"]), + emphasis_dim_a30=as_rgba(colors["emphasis-dim"], alpha=0.3), + emphasis_dim_a40=as_rgba(colors["emphasis-dim"], alpha=0.4), + ring_radius=f"{WINDOW_RADIUS + FOCUSED_INSET}px", + unfocused_ring_radius=f"{WINDOW_RADIUS + BORDER_INSET}px", + scheme_name=colors["_scheme"], + surface_dim=as_rgb(colors["surface-dim"]), + surface_dim_a60=as_rgba(colors["surface-dim"], alpha=0.6), + text=as_rgb(colors["text"]), + ) + + +def render_colors_md(colors: dict[str, str]) -> str: + """Render ~/.config/gnome-settings/COLORS.md.""" + header = textwrap.dedent(f"""\ + + + # Color Palette + + All colors below are derived from GNOME's live settings + by [`color-palette`](../../.local/bin/color-palette). + + Current inputs: accent=**{colors['_accent']}** scheme=**{colors['_scheme']}** + + The `roles` form a ramp of {N_MAJOR_STOPS} stops in the accent's hue, + uniform in perceived lightness (i.e., OKLCH), + reading light-to-dark in dark mode and dark-to-light in light mode. + The GNOME neutrals are adopted verbatim from `libadwaita`. + The ANSI accents are solved to at least WCAG {min(ANSI_CONTRAST.values())}:1 + against `base` (blue: {ANSI_CONTRAST["blue"]}:1, magenta: {ANSI_CONTRAST["magenta"]}:1). + + ## Roles + + | Role | Hex | RGB | + |------|-----|-----| + """) + + lines = [] + + for role in [*ROLES, "accent", "chrome", "black"]: + lines.append(f"| {role} | `{colors[role]}` | {as_rgb(colors[role])} |") + + lines += [ + "", + "", + "## Terminal ANSI Colors", + "", + "| ANSI | Normal | Bright |", + "|------|--------|--------|", + ] + + for name in _ANSI_ORDER: + lines.append( + f"| {name} | `{colors[f'{name}-normal']}` | `{colors[f'{name}-bright']}` |" + ) + + lines.append("") + + return header + "\n".join(lines) + + +def render_rofi(colors: dict[str, str]) -> str: + """Render the local override imported at the end of theme.rasi.""" + return textwrap.dedent(f"""\ + /* Written by ~/.local/bin/color-palette */ + /* accent={colors['_accent']} scheme={colors['_scheme']} */ + + * {{ + accent-c: {colors['accent']}; + bg-dark: {colors['base']}; + bg: {colors['chrome']}; + border-c: {colors['active-border']}; + emphasis-c: {colors['emphasis-dim']}; + fg: {colors['text']}; + fg-a12: {as_rgba(colors['text'], alpha=0.12)}; + fg-dim: {colors['chrome-quiet']}; + }} + """) + + +def show_colors(colors: dict[str, str]) -> None: + """Print all derived colors of this run for debugging.""" + print( + f"Palette: accent={colors['_accent']} scheme={colors['_scheme']}", + file=sys.stderr, + ) + for name, hex_value in sorted(colors.items()): + if not name.startswith("_"): + print(f" {name:<16} {hex_value}", file=sys.stderr) + + +def show_swatches(colors: dict[str, str]) -> None: # pylint: disable=too-many-locals + """Show all colors as truecolor swatches, grouped and in ramp order.""" + + base = colors["base"] + surface = coloraide.Color(base) + + def rgb_of(hex_value: str) -> tuple[int, int, int]: + return tuple(int(hex_value[i : i + 2], 16) for i in (1, 3, 5)) + + def block(hex_value: str) -> str: + red, green, blue = rgb_of(hex_value) + return f"\033[48;2;{red};{green};{blue}m \033[0m" + + def sample(fg_hex: str, bg_hex: str) -> str: + red, green, blue = rgb_of(fg_hex) + b_red, b_green, b_blue = rgb_of(bg_hex) + return ( + f"\033[38;2;{red};{green};{blue}m" + f"\033[48;2;{b_red};{b_green};{b_blue}m Sample \033[0m" + ) + + def ratio(hex_value: str, *, against: coloraide.Color = surface) -> float: + return coloraide.Color(hex_value).contrast(against, method="wcag21") + + def flag(value: float, *, target_contrast: float) -> str: + return " !" if value < (target_contrast - 0.05) else " " + + print(f"Palette: accent={colors['_accent']} scheme={colors['_scheme']}") + + # 1) The `ramp` roles, printed dark-to-light as in the ramp itself + # => Surfaces are judged by "does `text` read on them", + # foreground-ish roles by "do they read on `base`" + print("\n Ramp (on base, ratio of `text` on it)") + for role in ROLES: + hex_value = colors[role] + text_on_it = ratio(colors["text"], against=coloraide.Color(hex_value)) + print( + f" {role:<16} {hex_value} {block(hex_value)}" + f" {sample(hex_value, base)}" + f" {ratio(hex_value):4.1f}:1 text:{text_on_it:4.1f}:1" + ) + + # 2) GNOME's neutrals + print("\n GNOME neutrals") + for name in [*GNOME_NEUTRALS, "black", "white", "accent"]: + hex_value = colors[name] + print(f" {name:<16} {hex_value} {block(hex_value)}") + + # 3) The ANSI accents, normal and bright side by side + # => "black" and "white" alias ramp roles and are exempt + # from the contrast target that `lift()` solves for + print("\n ANSI (normal | bright, on base)") + for name in _ANSI_ORDER: + normal, bright = colors[f"{name}-normal"], colors[f"{name}-bright"] + r_normal, r_bright = ratio(normal), ratio(bright) + + if name in ("black", "white"): + f_normal = f_bright = " " + else: + f_normal, f_bright = ( + flag(r_normal, target_contrast=ANSI_CONTRAST[name]), + flag(r_bright, target_contrast=ANSI_CONTRAST[name]), + ) + + print( + f" {name:<8}" + f" {block(normal)} {sample(normal, base)} {r_normal:4.1f}:1{f_normal}" + f" | {block(bright)} {sample(bright, base)} {r_bright:4.1f}:1{f_bright}" + ) + + +def write_consumers(colors: dict[str, str]) -> int: + """Write the `colors` into all consumer files.""" + config_home = pathlib.Path( + os.environ.get("XDG_CONFIG_HOME", pathlib.Path.home() / ".config") + ) + + results = ( + write_file( + content=render_alacritty(colors), + target=config_home / "alacritty" / "colors.toml", + ), + write_file( + content=render_colors_md(colors), + target=config_home / "gnome-settings" / "COLORS.md", + ), + write_file( + content=render_dconf(colors), + target=config_home / "gnome-settings" / "local" / "palette.ini", + ), + write_file( + content=render_fastfetch(colors), + target=config_home / "fastfetch" / "config.jsonc", + ), + write_file( + content=render_forge_css(colors), + target=config_home / "forge" / "stylesheet" / "forge" / "stylesheet.css", + ), + write_file( + content=render_rofi(colors), + target=config_home / "rofi" / "colors.rasi", + ), + ) + + return 0 if all(results) else 1 + + +def write_file(*, content: str, target: pathlib.Path) -> bool: + """Write the `content` to the `target`, if it differs. + + The write is done via a temporary file in the same directory + so that a failing run does not leave a truncated target behind. + + Returns: + status: If the write succeeded + """ + try: + if target.exists() and target.read_text(encoding="utf-8") == content: + print(f" unchanged {target}", file=sys.stderr) + return True + + target.parent.mkdir(parents=True, exist_ok=True) + + fd, tmp_path = tempfile.mkstemp( + dir=target.parent, + prefix=".palette-", + suffix=".tmp", + ) + + try: + with os.fdopen(fd, "w", encoding="utf-8") as tmp: + tmp.write(content) + + os.chmod(tmp_path, 0o644) + os.replace(tmp_path, target) + + except BaseException: + try: + os.unlink(tmp_path) + except OSError: + pass + + raise + + except (OSError, UnicodeDecodeError) as error: + print(f" FAILED {target} ({error})", file=sys.stderr) + return False + + print(f" written {target}", file=sys.stderr) + return True + + +def main() -> int: + """Process the arguments and derive a color palette.""" + + # Keep the `argparse` flags and descriptions below in sync + # with the tab completions in ~/.bash_completion and ~/.zshrc + + parser = argparse.ArgumentParser( + description="Derive a color palette from GNOME's accent color" + ) + parser.add_argument( + # Used by ~/.local/bin/dark-scheme-toggle to speed up the change + "--scheme", + choices=["dark", "light"], + default=None, + help="Override the live GNOME color-scheme", + ) + parser.add_argument( + "--show", + action="store_true", + help="Show the derived colors as swatches in the terminal", + ) + parser.add_argument( + "--write", + action="store_true", + help="Write the derived color palette into all consumer files", + ) + args = parser.parse_args() + + colors = derive_colors(scheme_override=args.scheme) + + if args.show: + show_swatches(colors) + else: + show_colors(colors) + + if not args.write: + return 0 + + return write_consumers(colors) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.local/bin/dark-scheme-toggle b/.local/bin/dark-scheme-toggle new file mode 100755 index 0000000..e26379a --- /dev/null +++ b/.local/bin/dark-scheme-toggle @@ -0,0 +1,52 @@ +#!/bin/sh + +# Triggered by d +# as defined in [org/gnome/settings-daemon/plugins/media-keys] +# in ~/.config/gnome-settings/key-bindings.ini + +set -eu + + +XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" + +Forge=org.gnome.shell.extensions.forge +Interface=org.gnome.desktop.interface + + +if [ "$(gsettings get $Interface color-scheme)" = "'prefer-dark'" ]; then + new_theme="light" +else + new_theme="dark" +fi + + +# Render and apply the palette for the target scheme before flipping, +# so that the visible switch happens in one step +"$HOME/.local/bin/color-palette" --write --scheme "$new_theme" + + +dconf load / < "$XDG_CONFIG_HOME/gnome-settings/local/palette.ini" + + +# Set the "color-scheme" explicitly here to be on the safe side +# (although it is also contained in the palette.ini file) +# +# See comments for "color-scheme" under [org/gnome/desktop/interface] +# in ~/.config/gnome-settings/misc.ini +gsettings set $Interface color-scheme "prefer-$new_theme" + + +# `forge` only re-reads its stylesheet on a restart; +# however, it takes about one second to do so +# gnome-extensions disable forge@jmmaranan.com +# gnome-extensions enable forge@jmmaranan.com + +# As a hack, we make `forge` re-read its stylesheet by poking a watched setting +if gnome-extensions list --enabled 2>/dev/null | grep -qxF "forge@jmmaranan.com"; then + _gap=$(gsettings get $Forge window-gap-size | awk '{print $2}') + gsettings set $Forge window-gap-size "uint32 $((_gap + 1))" + gsettings set $Forge window-gap-size "uint32 $_gap" +fi + + +echo "Switched the color theme to $new_theme" diff --git a/.local/bin/dconf-load b/.local/bin/dconf-load new file mode 100755 index 0000000..d3446bb --- /dev/null +++ b/.local/bin/dconf-load @@ -0,0 +1,84 @@ +#!/bin/sh + +# Load the ~/.config/gnome-settings[/local]/*.ini files into `dconf`, +# or just the single given as an argument +# +# Unlike ~/.local/bin/gnome-restore this neither backs up nor resets +# anything, so keys removed from the *.ini files stay active +# => Use this for a quick iteration on the *.ini files, +# and `gnome-restore` for a deterministic state + +set -eu + + +XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" + +GNOME_CFG_DIR="$XDG_CONFIG_HOME/gnome-settings" + + + +_usage() { + echo "Usage: $(basename "$0") []" >&2 + exit 1 +} + + +_load() { + # The *.ini files are machine independent and use a placeholder /home/user + echo " $(basename "$1")" + sed "s|/home/user|$HOME|g" "$1" | dconf load / +} + + + +if ! command -v dconf >/dev/null 2>&1; then + echo "dconf is not installed" >&2 + exit 1 +fi + + +[ "$#" -le 1 ] || _usage + + + +# A single file, e.g., while iterating on it + +if [ "$#" -eq 1 ]; then + + if [ ! -f "$1" ]; then + echo "No such file: $1" >&2 + exit 1 + fi + + echo "Loading a single GNOME settings file:" + + _load "$1" + + exit 0 + +fi + + + +# Load all *.ini files in ~/.config/gnome-settings[/local]/ + + +if [ ! -d "$GNOME_CFG_DIR" ]; then + echo "No such directory: $GNOME_CFG_DIR" >&2 + exit 1 +fi + + +echo "Loading GNOME settings from: $GNOME_CFG_DIR" + + +for file in "$GNOME_CFG_DIR"/*.ini; do + [ -e "$file" ] || continue # because the glob does not expand if empty + _load "$file" +done + + +for file in "$GNOME_CFG_DIR"/local/*.ini; do + [ -e "$file" ] || continue # because the glob does not expand if empty + _load "$file" +done diff --git a/.local/bin/dconf-verify b/.local/bin/dconf-verify new file mode 100755 index 0000000..ec26818 --- /dev/null +++ b/.local/bin/dconf-verify @@ -0,0 +1,43 @@ +#!/bin/sh + +# List GNOME `dconf` keys that are live but not described by ~/.config/gnome-settings/*.ini + +set -eu + + +# Keep in sync with the `RESET_PATHS` in ~/.local/bin/gnome-restore +# (Note: The `$FILTER` below is a bit more generic than the `RESET_PATHS`) +FILTER='^(org/gnome/|org/gtk/|org/freedesktop/(ibus|tracker)/|ca/desrt/|system/)' + +GNOME_CFG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/gnome-settings" + + +flat() { + awk '/^\[/ { s = substr($0, 2, length($0) - 2); next } + /^[A-Za-z0-9][A-Za-z0-9_-]*=/ { i = index($0, "="); print s "/" substr($0, 1, i - 1) }' +} + + +live=$(mktemp) +managed=$(mktemp) + +trap 'rm -f "$live" "$managed"' EXIT INT TERM + + +dconf dump / | flat | grep -E "$FILTER" | sort -u > "$live" + +find "$GNOME_CFG_DIR" -maxdepth 2 -type f -name '*.ini' -exec cat {} + \ + | sed "s|/home/user|$HOME|g" \ + | flat | grep -E "$FILTER" | sort -u > "$managed" + + +echo "" +echo "== Live keys NOT in the ~/.config/gnome-settings/*.ini files ==" +comm -23 "$live" "$managed" +echo "" + + +echo "" +echo "== Keys in the ~/.config/gnome-settings/*.ini files but NOT live ==" +comm -13 "$live" "$managed" +echo "" diff --git a/.local/bin/dotfiles-install b/.local/bin/dotfiles-install index c88becb..247f19b 100755 --- a/.local/bin/dotfiles-install +++ b/.local/bin/dotfiles-install @@ -151,6 +151,31 @@ sh "$XDG_BIN_HOME/generate-shell-completions" --quieter \ || echo "WARN: generating shell completions failed" >&2 +if command -v python3 >/dev/null 2>&1; then + + python3 -m \ + pip install \ + --user \ + --upgrade \ + --break-system-packages \ + coloraide \ + rofimoji \ + || echo "WARN: pip install failed" >&2 + + python3 "$XDG_BIN_HOME/color-palette" --write \ + || echo "WARN: color-palette failed" >&2 + +fi + + +if systemctl --user daemon-reload 2>/dev/null; then + systemctl --user enable gnome-restore.service + systemctl --user enable night-light-reset.timer +else + echo "WARN: no systemd user session, units not enabled" >&2 +fi + + # Keep only the most recent backups _keep=10 if [ -d "$BACKUPS_DIR" ]; then diff --git a/.local/bin/evolution-on-workspace-0 b/.local/bin/evolution-on-workspace-0 new file mode 100755 index 0000000..e730040 --- /dev/null +++ b/.local/bin/evolution-on-workspace-0 @@ -0,0 +1,30 @@ +#!/bin/sh + +# Raise Evolution and switch to its workspace, +# or switch to the target workspace first and launch it there +# +# Switching before the launch avoids the flicker that +# `auto-move-windows` causes by relocating an already mapped window +# +# Note that `auto-move-windows` must not also manage Evolution, +# or both mechanisms fight over the placement + +set -eu + + +_CLASS=evolution +_COMMAND=/usr/bin/evolution + +# This is the last of the 10 named but 0-based workspaces +# in "num-workspaces" under [org/gnome/desktop/wm/preferences] +_WORKSPACE=9 + + +# "wmctrl -x -a" already switches to the window's workspace +wmctrl -x -a "$_CLASS" && exit 0 + + +wmctrl -s "$_WORKSPACE" + + +exec "$_COMMAND" diff --git a/.local/bin/gnome-restore b/.local/bin/gnome-restore new file mode 100755 index 0000000..af47b51 --- /dev/null +++ b/.local/bin/gnome-restore @@ -0,0 +1,152 @@ +#!/bin/sh + +# Restore the GNOME settings from ~/.config/gnome-settings/*.ini +# +# `dconf load` merges into the existing state, implying +# that keys removed from the *.ini files would still be active +# => Reset the subtrees we fully manage first, +# which makes the resulting state deterministic + +set -eu + + +XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" +XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" +XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}" + +BACKUP_DIR="$XDG_STATE_HOME/gnome-settings-backups" +GNOME_CFG_DIR="$XDG_CONFIG_HOME/gnome-settings" + + +# Subtrees either +# - fully described by the *.ini files +# - or reset to their defaults +# +# Notes: +# - Keep in sync with the `grep`s in ~/.local/bin/dconf-verify +# - "/org/freedesktop/tracker/" is exempted as we think resetting it +# makes `localsearch3` re-index everything from scratch +RESET_PATHS=" +/ca/desrt/dconf-editor/ +/desktop/ibus/ +/org/gnome/baobab/ +/org/gnome/calculator/ +/org/gnome/calendar/ +/org/gnome/Characters/ +/org/gnome/clocks/ +/org/gnome/Connections/ +/org/gnome/Contacts/ +/org/gnome/control-center/ +/org/gnome/desktop/ +/org/gnome/Disks/ +/org/gnome/evince/ +/org/gnome/Extensions/ +/org/gnome/file-roller/ +/org/gnome/gedit/ +/org/gnome/gnome-system-monitor/ +/org/gnome/Loupe/ +/org/gnome/maps/ +/org/gnome/mutter/ +/org/gnome/nautilus/ +/org/gnome/nm-applet/ +/org/gnome/portal/ +/org/gnome/settings-daemon/ +/org/gnome/shell/ +/org/gnome/simple-scan/ +/org/gnome/software/ +/org/gnome/system/location/ +/org/gnome/terminal/ +/org/gnome/TextEditor/ +/org/gnome/Totem/ +/org/gnome/tweaks/ +/org/gnome/Weather/ +/org/gnome/GWeather4/ +/org/gtk/ +/system/ +" + + + +if ! command -v dconf >/dev/null 2>&1; then + echo "dconf is not installed => Nothing to do" >&2 + exit 0 +fi + + +if [ ! -d "$GNOME_CFG_DIR" ]; then + echo "No settings found at: $GNOME_CFG_DIR" >&2 + exit 1 +fi + + + +echo "Backing up current GNOME settings to: $BACKUP_DIR" + +mkdir -p "$BACKUP_DIR" +dconf dump / > "$BACKUP_DIR/$(date +%Y%m%d-%H%M%S).ini" + +# Keep the ten most recent backups +# shellcheck disable=SC2012 # File names are timestamps => `ls` is safe +ls -1t "$BACKUP_DIR"/*.ini | tail -n +11 | xargs -r rm -- + + +# Remove temporary background image files +# created by ~/.local/bin/randomize-background +if [ "${FRESH_SESSION:-}" = "1" ]; then + echo "Removing session-scoped background image files" + rm -f "$XDG_CONFIG_HOME/gnome-settings/local/temporary_background.ini" + find "$XDG_DATA_HOME/backgrounds" -name '*-tmp-*.jpg' -delete 2>/dev/null || true +fi + + +echo "Resetting managed subtrees in dconf" + +for path in $RESET_PATHS; do + echo " $path" + dconf reset -f "$path" +done + + + +# forge's `patchCss()` writes its stylesheet backup to a path built from a +# property that no longer exists, yielding "~/undefined.bak" +# => Patch the path in the installed extension +THEME_JS="$XDG_DATA_HOME/gnome-shell/extensions/forge@jmmaranan.com/lib/shared/theme.js" +if [ -w "$THEME_JS" ]; then + sed -i 's|this\.configMgr\.stylesheetFileName + "\.bak"|configCss.get_path() + ".bak"|' "$THEME_JS" +fi + + + +echo "Import GNOME extensions' schemas" + +SCHEMAS="$HOME/.local/bin/gnome-shell-extensions-schemas-import" +if [ -x "$SCHEMAS" ]; then + "$SCHEMAS" || echo "WARN: importing GNOME extension schemas failed" >&2 +fi + + + +LOAD="$HOME/.local/bin/dconf-load" + +if [ -x "$LOAD" ]; then + "$LOAD" +else + echo "ERROR: $LOAD is missing" >&2 + exit 1 +fi + + + +echo "Patching forge" + +# Draw a whitish border on unfocused windows as well + +PATCH="$HOME/.local/bin/gnome-shell-extension-forge-patch" +if [ -x "$PATCH" ]; then + "$PATCH" || echo "WARN: forge border patch failed" >&2 +fi + + + +echo "Restored GNOME to the defaults defined in ~/.config/gnome-settings" diff --git a/.local/bin/gnome-shell-extension-forge-install b/.local/bin/gnome-shell-extension-forge-install new file mode 100755 index 0000000..f4cf375 --- /dev/null +++ b/.local/bin/gnome-shell-extension-forge-install @@ -0,0 +1,49 @@ +#!/bin/sh + +# Install `forge` from source into the same place that +# https://extensions.gnome.org/extension/4481/forge/ would use +# +# The released version may break "window-toggle-float" +# (see https://github.com/forge-ext/forge/issues/492) +# and is not maintained at the moment +# +# A re-login is required afterwards, +# because `gnome-shell` only scans the extensions directory on startup + +set -eu + + +_UUID=forge@jmmaranan.com +_REPO=https://github.com/forge-ext/forge.git +_TARGET="${XDG_DATA_HOME:-$HOME/.local/share}/gnome-shell/extensions/$_UUID" + + +for _tool in git make glib-compile-schemas msgfmt xgettext; do + command -v "$_tool" >/dev/null || { + echo "ERROR: $_tool is missing (try: apt install git make libglib2.0-bin gettext)" >&2 + exit 1 + } +done + + +_workdir=$(mktemp -d) +trap 'rm -rf "$_workdir"' EXIT + + +git clone --depth 1 "$_REPO" "$_workdir" + + +_commit=$(git -C "$_workdir" rev-parse --short HEAD) + + +make -C "$_workdir" build + + +rm -rf "$_TARGET" + + +make -C "$_workdir" install + + +echo "Installed $_UUID from $_commit into $_TARGET" +echo "Log out and back in, then: gnome-extensions enable $_UUID" diff --git a/.local/bin/gnome-shell-extension-forge-patch b/.local/bin/gnome-shell-extension-forge-patch new file mode 100755 index 0000000..e5987e3 --- /dev/null +++ b/.local/bin/gnome-shell-extension-forge-patch @@ -0,0 +1,73 @@ +#!/bin/sh + +# Patch `forge` to draw a border on all tiled and unfocused windows +# +# Idempotent: Re-running on a patched file changes nothing + +set -eu + + +XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" + +WINDOW_JS="$XDG_DATA_HOME/gnome-shell/extensions/forge@jmmaranan.com/lib/extension/window.js" + + +[ -w "$WINDOW_JS" ] || { echo "Not writable or missing: $WINDOW_JS" >&2; exit 1; } + + +grep -q "showUnfocusedBorders" "$WINDOW_JS" && { echo "Already patched"; exit 0; } + + +# Call our method after the focused border is drawn +sed -i '/^ updateBorderLayout() {$/,/^ }$/ s|this\.showWindowBorders();|this.showWindowBorders();\n this.showUnfocusedBorders();|' "$WINDOW_JS" + + +# Append the method right after `updateBorderLayout()` +awk ' + /^ updateBorderLayout\(\) \{$/ { inFn=1 } + inFn && /^ \}$/ { + print + print "" + print " showUnfocusedBorders() {" + print " if (!this.ext.settings.get_boolean(\"focus-border-toggle\")) return;" + print " this.tree.nodeWindows.forEach((nodeWindow) => {" + print " let metaWindow = nodeWindow.nodeValue;" + print " if (!metaWindow || metaWindow === this.focusMetaWindow) return;" + print " if (metaWindow.minimized || metaWindow.is_fullscreen()) return;" + print " if (metaWindow.get_workspace() !== global.workspace_manager.get_active_workspace()) return;" + print " let maximized = false;" + print " try { maximized = metaWindow.is_maximized(); }" + print " catch (e) { maximized = metaWindow.get_maximized() === 3; }" + print " if (maximized) return;" + print " if (nodeWindow.parentNode.isTabbed() || nodeWindow.parentNode.isStacked()) return;" + print " let windowActor = metaWindow.get_compositor_private();" + print " if (!windowActor || !windowActor.border) return;" + print " let border = windowActor.border;" + print " border.set_style_class_name(\"window-unfocused-border\");" + print " let rect = metaWindow.get_frame_rect();" + print " let inset = 1;" + print " border.set_size(rect.width + inset * 2, rect.height + inset * 2);" + print " border.set_position(rect.x - inset, rect.y - inset);" + print " border.show();" + print " if (global.window_group && global.window_group.contains(border)) {" + print " global.window_group.remove_child(border);" + print " global.window_group.insert_child_above(border, windowActor);" + print " }" + print " });" + print " }" + inFn=0 + next + } + { print } +' "$WINDOW_JS" > "$WINDOW_JS.tmp" && mv "$WINDOW_JS.tmp" "$WINDOW_JS" + + +grep -q 'this\.showUnfocusedBorders();' "$WINDOW_JS" \ + && grep -q 'showUnfocusedBorders() {' "$WINDOW_JS" \ + || { + echo "ERROR: patch anchors did not match (upstream changed?): $WINDOW_JS" >&2 + exit 1 +} + + +echo "Patched: $WINDOW_JS" diff --git a/.local/bin/gnome-shell-extensions-schemas-import b/.local/bin/gnome-shell-extensions-schemas-import new file mode 100755 index 0000000..ddbeae2 --- /dev/null +++ b/.local/bin/gnome-shell-extensions-schemas-import @@ -0,0 +1,34 @@ +#!/bin/sh + +# Make the `GSettings` schemas of all user-installed GNOME Shell extensions +# visible to a plain `gsettings`, by symlinking the *.gschema.xml files +# into the user schema directory and compiling them +# +# Idempotent: Re-running refreshes the links, prunes dangling ones +# (e.g., from removed extensions), and recompiles + +set -eu + + +XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" + +EXTENSIONS_DIR="$XDG_DATA_HOME/gnome-shell/extensions" +SCHEMAS_DIR="$XDG_DATA_HOME/glib-2.0/schemas" + + +mkdir -p "$SCHEMAS_DIR" + + +# Prune symlinks whose target no longer exists +find "$SCHEMAS_DIR" -maxdepth 1 -type l ! -exec test -e {} \; -exec rm -v -- {} \; + + +# Link every schema shipped by an extension +for xml in "$EXTENSIONS_DIR"/*/schemas/*.gschema.xml; do + [ -e "$xml" ] || continue # because the glob does not expand if empty + echo " $(basename "$xml")" + ln -sf -- "$xml" "$SCHEMAS_DIR/" +done + + +glib-compile-schemas "$SCHEMAS_DIR" diff --git a/.local/bin/gnome-shell-restart b/.local/bin/gnome-shell-restart new file mode 100755 index 0000000..33ce1cb --- /dev/null +++ b/.local/bin/gnome-shell-restart @@ -0,0 +1,75 @@ +#!/bin/sh + +# Restart `gnome-shell` in place, +# preserving the session and all windows +# +# Usually survives, but treat it like a logout: +# save your work first, because a crash on the way down can end the session +# +# Debian normally forbids starting/stopping this unit by hand; +# we allow it via a user-level drop-in (installed on first run) + +set -eu + + +if [ "${XDG_SESSION_TYPE:-}" != "x11" ]; then + echo "gnome-shell-restart: only works on X11, not on ${XDG_SESSION_TYPE:-unknown}" >&2 + exit 1 +fi + + +_UNIT="org.gnome.Shell@x11.service" + +_DROPIN_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/$_UNIT.d" +_DROPIN_FILE="$_DROPIN_DIR/allow-restart.conf" + + +if [ ! -f "$_DROPIN_FILE" ] || ! grep -q "SuccessExitStatus" "$_DROPIN_FILE"; then + + mkdir -p "$_DROPIN_DIR" + + cat > "$_DROPIN_FILE" << 'EOF' +# Written by ~/.local/bin/gnome-shell-restart + +# Debian forbids starting and stopping this unit by hand +# to protect Wayland sessions, where the shell cannot restart in place +# => We are on X11 +[Unit] +RefuseManualStart=no +RefuseManualStop=no + +# The old shell sometimes crashes while shutting down. +# +# Without this line, `systemd` treats that crash as a real failure +# and ends the whole session (i.e., logs us out) +# +# => We count a segfault as a normal exit +# +# If the shell truly keeps crashing, the start limit still ends the session +[Service] +SuccessExitStatus=1 SEGV +EOF + + systemctl --user daemon-reload +fi + + +# The unit allows at most 3 starts per 15 seconds +# and one restart too many counts as a failure and logs us out +# => Refuse to restart again too soon after the last one +_STARTED="$(systemctl --user show -p ActiveEnterTimestamp --value "$_UNIT")" +if [ -n "$_STARTED" ]; then + _AGE=$(( $(date +%s) - $(date -d "$_STARTED" +%s) )) + if [ "$_AGE" -lt 20 ]; then + echo "gnome-shell-restart: last shell start was only ${_AGE}s ago" >&2 + echo "gnome-shell-restart: refusing to trip the start limit (wait $(( 20 - _AGE ))s)" >&2 + exit 1 + fi +fi + + +# Forget any earlier failure, so it does not block this restart +systemctl --user reset-failed "$_UNIT" 2> /dev/null || true + + +exec systemctl --user restart "$_UNIT" diff --git a/.local/bin/key-bindings b/.local/bin/key-bindings new file mode 100755 index 0000000..d0a5d65 --- /dev/null +++ b/.local/bin/key-bindings @@ -0,0 +1,78 @@ +#!/bin/sh + +# Show the key bindings from ~/.config/gnome-settings/key-bindings.ini, +# one binding per line, flattened from the comment tables in that file +# +# The key column comes first and the search input is prefilled with "^", +# so that a typed pattern only matches the key column + +set -eu + + +INI="${XDG_CONFIG_HOME:-$HOME/.config}/gnome-settings/key-bindings.ini" + + +awk -F '|' ' + /^# .*\|/ { + sub(/^# ?/, "") + for (i = 1; i <= NF; i++) gsub(/^[ \t]+|[ \t]+$/, "", $i) + key = $1 + + if (key == "Key(s)") { # Header of the table + mode = "layers" + for (i = 2; i <= NF; i++) mod[i] = $i + next + } + if (key == "Key") { # Header of the browser table + mode = "browser" + next + } + if (mode == "" || key ~ /^[-+]+$/) next + + # Every line starts with a layer rank used by `sort` below: + # 1 , 2 , 3 , + # 4 , 5 , 6 browser bindings + + if (mode == "browser") { + if (key == "") next + k = split(key, parts, ">") + line[++n] = sprintf("6 %-17s %-20s %s: %s", parts[k], + substr(key, 1, length(key) - length(parts[k])), $2, $3) + next + } + + if (key == "") { # Row without a key => prefix or continuation + for (i = 2; i <= NF; i++) { + cell = $i + if (cell == "") continue + if (cell ~ /^\.\.\./) { # Continues the row above + sub(/^\.\.\. */, "", cell) + sub(/ \.\.\.$/, "", line[last[i]]) + line[last[i]] = line[last[i]] " " cell + } else { # Prefix for the rows below + sub(/ \.\.\.$/, "", cell) + prefix[i] = cell + } + } + next + } + + for (i = 2; i <= NF; i++) { + cell = $i + if (cell == "") continue + if (cell ~ /^\.\.\./) { + sub(/^\.\.\. */, "", cell) + cell = prefix[i] " " cell + } + line[++n] = sprintf("%d %-17s %-20s %s", i - 1, key, mod[i], cell) + last[i] = n + } + } + + END { for (i = 1; i <= n; i++) print line[i] } +' "$INI" \ + | LC_ALL=C sort -s -f -k2,2 -k1,1n \ + | cut -d ' ' -f 2- \ + | rofi -dmenu -i -p keys -no-custom -matching regex -filter '^' -no-sort \ + -theme-str '* { font: "FiraCode Nerd Font 10"; } window { width: 60em; } listview { lines: 20; }' \ + >/dev/null diff --git a/.local/bin/move-window-to-empty-workspace b/.local/bin/move-window-to-empty-workspace new file mode 100755 index 0000000..0e19020 --- /dev/null +++ b/.local/bin/move-window-to-empty-workspace @@ -0,0 +1,49 @@ +#!/bin/sh + +# Move the active window to the first empty workspace and follow it +# +# Note: This assumes that only the main monitor has workspaces enabled + +set -eu + + +active=$(xprop -root _NET_ACTIVE_WINDOW | awk '{print $NF}') +desktop=$(xprop -id "$active" _NET_WM_DESKTOP 2>/dev/null | awk '{print $NF}') + +# 4294967295 (i.e., 0xFFFFFFFF) means "on all workspaces", +# which is what `mutter` reports for windows on secondary monitors +# when "workspaces-only-on-primary" is true +if [ "$desktop" = "4294967295" ] || [ -z "$desktop" ]; then + command -v notify-send >/dev/null \ + && notify-send 'Window Management' 'Active window is not on a workspace on the main screen' + exit 1 +fi + + +occupied_workspaces=$(wmctrl -l) || exit 1 # fail safe +occupied_workspaces=$(echo "$occupied_workspaces" | awk '{print $2}' | sort -u) + +total=$(wmctrl -d | wc -l) + + +# 0-based index for the first workspace +i=0 + + +while [ "$i" -lt "$total" ]; do + + if ! echo "$occupied_workspaces" | grep -qx "$i"; then + wmctrl -r :ACTIVE: -t "$i" + wmctrl -s "$i" + exit 0 + fi + + i=$((i + 1)) + +done + + +command -v notify-send >/dev/null \ + && notify-send 'Window Management' 'No empty workspace available' + +exit 1 diff --git a/.local/bin/nextcloud-restore b/.local/bin/nextcloud-restore new file mode 100755 index 0000000..3d3f019 --- /dev/null +++ b/.local/bin/nextcloud-restore @@ -0,0 +1,162 @@ +#!/bin/sh + +# This script ensures my `nextcloud` clients operate in a uniform way + +set -eu + + + +XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" + +NEXTCLOUD_CFG="${XDG_CONFIG_HOME:-$HOME/.config}/Nextcloud/nextcloud.cfg" +SERVER_URL_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/Nextcloud/server.txt" + + + +if [ -r "$SERVER_URL_FILE" ]; then + # `tr` strips spaces, tabs, and CR/LF + SERVER_URL=$(head -n 1 "$SERVER_URL_FILE" | tr -d '[:space:]') +else + SERVER_URL="" +fi + + +[ "${1:-}" = "--force" ] && force=1 || force=0 + +if [ -z "$SERVER_URL" ]; then + + if [ "$force" -eq 1 ]; then + + echo "" >&2 + echo "WARN: No usable server URL at $SERVER_URL_FILE, leaving the server URL unmanaged" >&2 + echo "" >&2 + + else + + echo "" >&2 + echo "No usable server URL found at $SERVER_URL_FILE" >&2 + echo "" >&2 + echo "Create it with a single line, e.g., 'https://cloud.example.com'," >&2 + echo "or re-run with --force to skip the URL settings" >&2 + echo "" >&2 + + exit 1 + + fi + +fi + + + +if ! command -v crudini >/dev/null 2>&1; then + echo "crudini is not installed => cannot configure nextcloud" >&2 + exit 1 +fi + + +if ! command -v nextcloud >/dev/null 2>&1; then + echo "Nextcloud is not installed" >&2 + exit 1 +fi + + + +del_key() { # section key + crudini --del "$NEXTCLOUD_CFG" "$1" "$2" +} + + +set_key() { # section key value + crudini --set "$NEXTCLOUD_CFG" "$1" "$2" "$3" +} + + + +# Stop `nextcloud` + +nextcloud --quit 2>/dev/null || true + +for _ in $(seq 30); do + pgrep -x nextcloud >/dev/null || break + sleep 0.5 +done + +if pgrep -x nextcloud >/dev/null; then + echo "Nextcloud did not exit; aborting" >&2 + exit 1 +fi + + + +mkdir -p "$(dirname "$NEXTCLOUD_CFG")" +touch "$NEXTCLOUD_CFG" + + + +# Configure base settings for `nextcloud` + +if [ -n "$SERVER_URL" ]; then + set_key General forceOverrideServerUrl true + set_key General overrideServerUrl "$SERVER_URL" +fi + +set_key General overrideLocalDir "$HOME/Cloud" + +set_key General launchOnSystemStartup true +set_key General monoIcons true +set_key General crashReporter false + +# Windows-only, kept for completeness +set_key General showInExplorerNavigationPane true + +set_key General optionalServerNotifications false +set_key General showCallNotifications false +set_key General showChatNotifications false + +set_key General isVfsEnabled false +set_key General confirmExternalStorage true +set_key General useNewBigFolderSizeLimit false +set_key General newBigFolderSizeLimit 999 +set_key General notifyExistingFoldersOverLimit false +set_key General stopSyncingExistingFoldersOverLimit false + +set_key General moveToTrash true +set_key General promptDeleteAllFiles true + +set_key Proxy type 0 + + +# Make `nextcloud` sync at full speed, with sane fallback limits + +# Modes: +# - 0 => unlimited +# - -1 => automatic limit +# - +1 => manually set limit + +# Limits: +# - values are in KBytes/s +# - we assume line speeds of 100/30 Mbit/s, to be capped at 80% + +set_key BWLimit useDownloadLimit 0 +# 100 Mbit/s / 8 => 12500 KBytes/s * 0.8 => 10000 +set_key BWLimit downloadLimit 10000 + +set_key BWLimit useUploadLimit 0 +# 30 Mbit/s / 8 => 3750 KBytes/s * 0.8 => 3000 +set_key BWLimit uploadLimit 3000 + + +set_key Nextcloud remotePollInterval 60000 + + + +# Because of a bug, `nextcloud` wants to create logs at a deprecated location +# => An empty file at that location makes `nextcloud` use the new location +# inside ~/.config/Nextcloud +rm -rf "$XDG_DATA_HOME/Nextcloud" +touch "$XDG_DATA_HOME/Nextcloud" + + + +# Resume `nextcloud` +setsid nextcloud --background >/dev/null 2>&1 & diff --git a/.local/bin/night-light-make-cooler b/.local/bin/night-light-make-cooler new file mode 100755 index 0000000..0192d98 --- /dev/null +++ b/.local/bin/night-light-make-cooler @@ -0,0 +1,88 @@ +#!/bin/sh + +# Triggered by bracketleft +# as defined in [org/gnome/settings-daemon/plugins/media-keys] +# in ~/.config/gnome-settings/key-bindings.ini +# +# This script makes the screen a bit cooler than it currently is, +# unless it is currently in a ramping phase and not fully tinted +# +# The default warmth is restored at login by ~/.local/bin/gnome-restore + +set -eu + + +# GNOME's GUI lets one choose between 1700 and 4700 only, +# and 6500 is the point where no color correction is applied at all +# while 1000 is the absolute minimum supported by the OS +_MIN_TEMPERATURE=1200 +_MAX_TEMPERATURE=4700 + + +notify() { + ID_FILE="${XDG_RUNTIME_DIR}/night-light-notification-id" + _id=$(cat "$ID_FILE" 2>/dev/null || true) + + if [ -n "$_id" ]; then + notify-send -p -t 5000 --replace-id="$_id" "Night Light" "$1" > "$ID_FILE" || rm -f "$ID_FILE" + else + notify-send -p -t 5000 "Night Light" "$1" > "$ID_FILE" || rm -f "$ID_FILE" + fi +} + + +Schema=org.gnome.settings-daemon.plugins.color + + +active=$(gdbus call --session \ + --dest org.gnome.SettingsDaemon.Color \ + --object-path /org/gnome/SettingsDaemon/Color \ + --method org.freedesktop.DBus.Properties.Get \ + org.gnome.SettingsDaemon.Color NightLightActive) + +applied=$(gdbus call --session \ + --dest org.gnome.SettingsDaemon.Color \ + --object-path /org/gnome/SettingsDaemon/Color \ + --method org.freedesktop.DBus.Properties.Get \ + org.gnome.SettingsDaemon.Color Temperature \ + | sed -E 's/.*uint32 ([0-9]+).*/\1/') + +automatic=$(gsettings get $Schema night-light-schedule-automatic) +current=$(gsettings get $Schema night-light-temperature | awk '{print $2}') + + +# Cooling never turns "Night Light" on +if [ "$active" = "(,)" ]; then + notify "is not tinting the screen right now (target: ${current}K)" + exit 0 +fi + + +# While the screen is not (fully) tinted, +# stepping the target has no visible effect +# => Only show what is going on +if [ "$automatic" = "true" ] && [ $((applied - current)) -gt 300 ]; then + notify "is still following the schedule's ramp (target: ${current}K)" + exit 0 +fi + + +# Walking past the cool end switches the feature off +if [ "$current" -ge $_MAX_TEMPERATURE ]; then + gsettings set $Schema night-light-enabled false + notify "is now off (stepped past ${_MAX_TEMPERATURE}K)" + exit 0 +fi + + +# 500 = (4700 - 1700 + 500) / 7 +# => Seven steps spanning more than the range in the GUI, +# mainly because we want to allow a bit more warmth (i.e., +500) +new=$((current + 500)) + +if [ "$new" -gt $_MAX_TEMPERATURE ]; then + new=$_MAX_TEMPERATURE +fi + + +gsettings set $Schema night-light-temperature "uint32 $new" diff --git a/.local/bin/night-light-make-warmer b/.local/bin/night-light-make-warmer new file mode 100755 index 0000000..f61fc41 --- /dev/null +++ b/.local/bin/night-light-make-warmer @@ -0,0 +1,102 @@ +#!/bin/sh + +# Triggered by bracketright +# as defined in [org/gnome/settings-daemon/plugins/media-keys] +# in ~/.config/gnome-settings/key-bindings.ini +# +# This script ensures "Night Light" is forced on +# or makes the screen a bit warmer than it currently is +# +# The default warmth is restored at login by ~/.local/bin/gnome-restore + +set -eu + + +# GNOME's GUI lets one choose between 1700 and 4700 only, +# and 6500 is the point where no color correction is applied at all +# while 1000 is the absolute minimum supported by the OS +_MIN_TEMPERATURE=1200 +_MAX_TEMPERATURE=4700 + +_DEFAULT_TEMPERATURE=2200 + + +notify() { + ID_FILE="${XDG_RUNTIME_DIR}/night-light-notification-id" + _id=$(cat "$ID_FILE" 2>/dev/null || true) + + if [ -n "$_id" ]; then + notify-send -p -t 5000 --replace-id="$_id" "Night Light" "$1" > "$ID_FILE" || rm -f "$ID_FILE" + else + notify-send -p -t 5000 "Night Light" "$1" > "$ID_FILE" || rm -f "$ID_FILE" + fi +} + + +Schema=org.gnome.settings-daemon.plugins.color + + +active=$(gdbus call --session \ + --dest org.gnome.SettingsDaemon.Color \ + --object-path /org/gnome/SettingsDaemon/Color \ + --method org.freedesktop.DBus.Properties.Get \ + org.gnome.SettingsDaemon.Color NightLightActive) + +applied=$(gdbus call --session \ + --dest org.gnome.SettingsDaemon.Color \ + --object-path /org/gnome/SettingsDaemon/Color \ + --method org.freedesktop.DBus.Properties.Get \ + org.gnome.SettingsDaemon.Color Temperature \ + | sed -E 's/.*uint32 ([0-9]+).*/\1/') + +automatic=$(gsettings get $Schema night-light-schedule-automatic) +current=$(gsettings get $Schema night-light-temperature | awk '{print $2}') + + +if [ "$active" = "(,)" ] \ + && { [ "$automatic" = "false" ] || [ $((applied - current)) -le 300 ]; }; then + at_target=true +else + at_target=false +fi + + +# Step only if the screen already shows the target tint +# while off or ramping in, forcing on below provides the warming already +if [ "$at_target" = "true" ]; then + + # 500 = (4700 - 1700 + 500) / 7 + # => Seven steps spanning more than the range in the GUI, + # mainly because we want to allow a bit more warmth (i.e., +500) + new=$((current - 500)) + + if [ "$new" -lt $_MIN_TEMPERATURE ]; then + new=$_MIN_TEMPERATURE + fi + + gsettings set $Schema night-light-temperature "uint32 $new" + +# Untinted and parked at a cool target +# => Forcing on would barely show otherwise +elif [ "$active" = "(,)" ] && [ "$current" -gt $_DEFAULT_TEMPERATURE ]; then + + gsettings set $Schema night-light-temperature "uint32 $_DEFAULT_TEMPERATURE" + +fi + + +# Always force "Night Light" on, saving a backslash call, +# with the same settings as in ~/.local/bin/night-light-toggle-schedule +if [ "$active" = "(,)" ] || [ "$automatic" = "true" ]; then + gsettings set $Schema night-light-enabled true + gsettings set $Schema night-light-schedule-automatic false + gsettings set $Schema night-light-schedule-from 0.0 + gsettings set $Schema night-light-schedule-to 24.0 +fi + + +# Notify only if the press produced no visible change +# (i.e., already at the minimum and not just snapped to full warmth by the force) +if [ "$at_target" = "true" ] && [ "$current" -le $_MIN_TEMPERATURE ]; then + notify "is already as warm as it gets (${_MIN_TEMPERATURE}K)" +fi diff --git a/.local/bin/night-light-reset b/.local/bin/night-light-reset new file mode 100755 index 0000000..5224c6d --- /dev/null +++ b/.local/bin/night-light-reset @@ -0,0 +1,25 @@ +#!/bin/sh + +# Ensure "Night Light" is enabled, +# set to run according to the automatic schedule, +# and reverted to the `_DEFAULT_TEMPERATURE` +# +# Intended to be run at 03:00 by the `night-light-reset.timer` systemd +# user unit, so that a forced state does not persist +# +# See ~/.local/bin/night-light-toggle-schedule for detailed documentation + +set -eu + + +_DEFAULT_TEMPERATURE=2200 + + +Schema=org.gnome.settings-daemon.plugins.color + +gsettings set $Schema night-light-enabled true +gsettings set $Schema night-light-schedule-automatic true +gsettings set $Schema night-light-schedule-from 18.0 +gsettings set $Schema night-light-schedule-to 6.0 + +gsettings set $Schema night-light-temperature "uint32 $_DEFAULT_TEMPERATURE" diff --git a/.local/bin/night-light-toggle-enabled b/.local/bin/night-light-toggle-enabled new file mode 100755 index 0000000..a7791b3 --- /dev/null +++ b/.local/bin/night-light-toggle-enabled @@ -0,0 +1,75 @@ +#!/bin/sh + +# Triggered by backslash +# as defined in [org/gnome/settings-daemon/plugins/media-keys] +# in ~/.config/gnome-settings/key-bindings.ini +# +# See ~/.local/bin/night-light-toggle-schedule +# for the other half of the toggling logic +# and extensive documentation + +set -eu + + +notify() { + ID_FILE="${XDG_RUNTIME_DIR}/night-light-notification-id" + _id=$(cat "$ID_FILE" 2>/dev/null || true) + + if [ -n "$_id" ]; then + notify-send -p -t 5000 --replace-id="$_id" "Night Light" "$1" > "$ID_FILE" || rm -f "$ID_FILE" + else + notify-send -p -t 5000 "Night Light" "$1" > "$ID_FILE" || rm -f "$ID_FILE" + fi +} + + +Schema=org.gnome.settings-daemon.plugins.color + + +if [ "$(gsettings get $Schema night-light-enabled)" = "true" ]; then + + active_before=$(gdbus call --session \ + --dest org.gnome.SettingsDaemon.Color \ + --object-path /org/gnome/SettingsDaemon/Color \ + --method org.freedesktop.DBus.Properties.Get \ + org.gnome.SettingsDaemon.Color NightLightActive) + + gsettings set $Schema night-light-enabled false + + if [ "$active_before" = "(,)" ]; then + notify "is now disabled, but the screen was not tinted anyways" + fi + +else + + gsettings set $Schema night-light-enabled true + + # We could start from our default warmth, as defined + # in [org/gnome/settings-daemon/plugins/color] + # in ~/.config/gnome-settings/misc.ini + # but choose not to do that because this is meant to be + # a plain on/off switch with no side effects + # => Snapping is left to bracketright, which is about + # warmth rather than about the feature being on or off + # + # _DEFAULT_TEMPERATURE=2200 + # current=$(gsettings get $Schema night-light-temperature | awk '{print $2}') + # + # if [ "$current" -gt $_DEFAULT_TEMPERATURE ]; then + # gsettings set $Schema night-light-temperature "uint32 $_DEFAULT_TEMPERATURE" + # fi + + # Give the settings daemon a moment to recompute "NightLightActive" + sleep 0.1 + + active_after=$(gdbus call --session \ + --dest org.gnome.SettingsDaemon.Color \ + --object-path /org/gnome/SettingsDaemon/Color \ + --method org.freedesktop.DBus.Properties.Get \ + org.gnome.SettingsDaemon.Color NightLightActive) + + if [ "$active_after" = "(,)" ]; then + notify "is now enabled, but the schedule keeps it off for now" + fi + +fi diff --git a/.local/bin/night-light-toggle-schedule b/.local/bin/night-light-toggle-schedule new file mode 100755 index 0000000..5465c41 --- /dev/null +++ b/.local/bin/night-light-toggle-schedule @@ -0,0 +1,145 @@ +#!/bin/sh + +# Triggered by backslash +# as defined in [org/gnome/settings-daemon/plugins/media-keys] +# in ~/.config/gnome-settings/key-bindings.ini +# +# Night Light control: +# +# - backslash => "when" toggle +# + "Force" the screen to be warm right now! +# (Turns Night Light on if it was off, and keeps it on all day) +# + "Un-force" the Night Light into the local evening schedule +# - backslash => "if" toggle +# Plain on/off switch for the Night Light feature +# - bracketleft => Make the screen cooler +# - bracketright => Make the screen warmer, and "force" warmness +# +# +# State machine: +# +# +-----------------------+------------------------------+------------------------------+------------------------------+------------------------------+ +# | | "IF" toggle | "WHEN" toggle | "make COOLER" | "make WARMER" | +# | Current STATE | backslash | backslash | bracketleft | bracketright | +# | | | | | | +# +-----------------------+------------------------------+------------------------------+------------------------------+------------------------------+ +# +-----------------------+------------------------------+------------------------------+------------------------------+------------------------------+ +# | DISABLED | ENABLE | enabled | still disabled | enabled | +# | + scheduled | + still scheduled | + FORCED on | + still scheduled | + FORCED on | +# | => no tint | => no or set tint | => set tint | => still no tint | => set tint | +# | | based on time N? | | N! | *1 | +# +-----------------------+------------------------------+------------------------------+------------------------------+------------------------------+ +# | DISABLED | ENABLE | enabled | still disabled | enabled | +# | + forced on | + still forced | + still FORCED on | + still forced | + still FORCED on | +# | => no tint | => set tint | => set tint | => still no tint | => set tint | +# | | | | N! | *1 | +# +-----------------------+------------------------------+------------------------------+------------------------------+------------------------------+ +# | enabled | DISABLE | still enabled | still enabled | still enabled | +# | + SCHEDULED (day) | + still scheduled | + FORCED on | + still scheduled | + FORCED on | +# | => no tint yet | => still no tint | => set tint | => still no tint | => set tint | +# | | N! | | N! | *1 | +# +-----------------------+------------------------------+------------------------------+------------------------------+------------------------------+ +# | enabled | DISABLE | still enabled | still enabled | still enabled | +# | + SCHEDULED (ramping) | + still scheduled | + FORCED on | + still scheduled | + FORCED on | +# | => some tint already | => no tint | => set tint | => no change in tint | => set tint | +# | | | | N! | | +# +-----------------------+------------------------------+------------------------------+------------------------------+------------------------------+ +# | enabled | DISABLE | still enabled | still enabled | still enabled | +# | + SCHEDULED (night) | + still scheduled | + FORCED on | + still scheduled | + FORCED on | +# | => set tint | => no tint | => set tint | => LESS tint | => MORE tint | +# | | | N! | *2 N? | N? | +# +-----------------------+------------------------------+------------------------------+------------------------------+------------------------------+ +# | enabled | DISABLE | still enabled | still enabled | still enabled | +# | + FORCED on | + still forced on | + back to SCHEDULED | + still forced on | + still forced on | +# | => set tint | => no tint | => no or set tint | => LESS tint | => MORE tint | +# | | | based on time N? | *2 N? | N? | +# +-----------------------+------------------------------+------------------------------+------------------------------+------------------------------+ +# Legend: +# - N! => A notification is shown for sure because there is no visible change on the screen +# - N? => A notification is shown if there was no visible change on the screen +# - *1 => The temperature is snapped to 2200K to ensure a visible effect +# - *2 => The tint is turned off entirely when stepping over the maximum temperature +# +# Notes: +# - The main state property or state change is shown in CAPS +# - Steps range from 1200K (warmest) to 4700K (coolest), +# which is a bit more than the 1700K to 4700K in `gnome-control-center` +# - If the range is already exhausted, so that the tint does not change, a notification is shown +# - A 03:00 timer re-enables the Night Light feature and puts it back on the automatic schedule +# with the `$_DEFAULT_TEMPERATURE` set +# + +set -eu + + +notify() { + ID_FILE="${XDG_RUNTIME_DIR}/night-light-notification-id" + _id=$(cat "$ID_FILE" 2>/dev/null || true) + + if [ -n "$_id" ]; then + notify-send -p -t 5000 --replace-id="$_id" "Night Light" "$1" > "$ID_FILE" || rm -f "$ID_FILE" + else + notify-send -p -t 5000 "Night Light" "$1" > "$ID_FILE" || rm -f "$ID_FILE" + fi +} + + +Schema=org.gnome.settings-daemon.plugins.color + + +active_before=$(gdbus call --session \ + --dest org.gnome.SettingsDaemon.Color \ + --object-path /org/gnome/SettingsDaemon/Color \ + --method org.freedesktop.DBus.Properties.Get \ + org.gnome.SettingsDaemon.Color NightLightActive) + +applied=$(gdbus call --session \ + --dest org.gnome.SettingsDaemon.Color \ + --object-path /org/gnome/SettingsDaemon/Color \ + --method org.freedesktop.DBus.Properties.Get \ + org.gnome.SettingsDaemon.Color Temperature \ + | sed -E 's/.*uint32 ([0-9]+).*/\1/') + +automatic=$(gsettings get $Schema night-light-schedule-automatic) +enabled=$(gsettings get $Schema night-light-enabled) +target=$(gsettings get $Schema night-light-temperature | awk '{print $2}') + + +if [ "$automatic" = "true" ] || [ "$enabled" = "false" ]; then + + # Force "Night Light" enabled, enabling it if needed + gsettings set $Schema night-light-enabled true + gsettings set $Schema night-light-schedule-automatic false + gsettings set $Schema night-light-schedule-from 0.0 + gsettings set $Schema night-light-schedule-to 24.0 + + if [ "$active_before" = "(,)" ] && [ "$automatic" = "true" ] \ + && [ $((applied - target)) -le 300 ]; then + notify "is now forced to stay on and no longer on an automatic schedule" + fi + +else + + # Back to the live location's schedule + gsettings set $Schema night-light-schedule-automatic true + gsettings set $Schema night-light-schedule-from 18.0 + gsettings set $Schema night-light-schedule-to 6.0 + + if [ "$active_before" = "(,)" ]; then + + # Give the settings daemon a moment to recompute "NightLightActive" + sleep 0.1 + + active_after=$(gdbus call --session \ + --dest org.gnome.SettingsDaemon.Color \ + --object-path /org/gnome/SettingsDaemon/Color \ + --method org.freedesktop.DBus.Properties.Get \ + org.gnome.SettingsDaemon.Color NightLightActive) + + if [ "$active_after" = "(,)" ]; then + notify "is still on because of an automatic schedule" + fi + + fi + +fi diff --git a/.local/bin/randomize-background b/.local/bin/randomize-background new file mode 100755 index 0000000..38d22c1 --- /dev/null +++ b/.local/bin/randomize-background @@ -0,0 +1,152 @@ +#!/bin/sh + +# Determine the next wallpaper by round robin from the cloud +# and install it as the background files referenced in +# ~/.config/gnome-settings/misc.ini +# +# Candidates: +# - Pairs => Both "-d.jpg" and "-l.jpg" exist +# - Singles => Any other "*.jpg" not following the -d/-l scheme, +# used as dark and light at the same time +# +# The live `gsettings` URIs point at uniquely named copies +# because `gnome-shell` caches background images by file URI; +# `gnome-restore` cleans up that temporary state during the next login + +set -eu + + + +XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}" +XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" +XDG_STATE_HOME="${XDG_STATE_HOME:-$HOME/.local/state}" + +SOURCE_DIR="$HOME/Cloud/Null-Tech/Backgrounds" +TARGET_DIR="$XDG_DATA_HOME/backgrounds" + +LOCAL_INI="$XDG_CONFIG_HOME/gnome-settings/local/temporary_background.ini" +STATE_FILE="$XDG_STATE_HOME/current-background.txt" + + + +[ -d "$SOURCE_DIR" ] || { + echo "No such folder: $SOURCE_DIR" >&2 + exit 1 +} + + + +# One candidate per line, tab-separated: +candidates=$( + for jpg in "$SOURCE_DIR"/*.jpg; do + [ -e "$jpg" ] || continue # because the glob does not expand if empty + + case "$jpg" in + + *-d.jpg) # Complete pairs only + stem="${jpg%-d.jpg}" + [ -f "$stem-l.jpg" ] && printf '%s\t%s\n' "$jpg" "$stem-l.jpg" + ;; + + *-l.jpg) # Already handled via its "-d" counterpart above + ;; + + *) # A single file to be used for both modes + printf '%s\t%s\n' "$jpg" "$jpg" + ;; + + esac + done +) + +if [ -z "$candidates" ]; then + echo "No usable *.jpg files in: $SOURCE_DIR" >&2 + exit 1 +fi + +echo "Found $(printf '%s\n' "$candidates" | wc -l) candidate(s)" + + + +# The glob expands alphabetically => "$candidates" is already sorted +# => Pick the first candidate sorting strictly after the last-used one, +# falling back to the first one if necessary +# (e.g., for start, wrap-around, or missing state file) +last_background=$(cat "$STATE_FILE" 2>/dev/null || true) +next_background=$(printf '%s\n' "$candidates" | awk -F '\t' -v last_background="$last_background" '$1 > last_background { print; exit }') +[ -n "$next_background" ] || next_background=$(printf '%s\n' "$candidates" | head -n 1) + + +dark_source=$(printf '%s' "$next_background" | cut -f 1) +light_source=$(printf '%s' "$next_background" | cut -f 2) +echo "Chosen background: $(basename "$dark_source")" + + + +dark_target="$TARGET_DIR/dark.jpg" +light_target="$TARGET_DIR/light.jpg" + +# Create a unique name per run +# so that the URIs below never repeat +timestamp=$(date +%s) + +dark_live_target="$TARGET_DIR/dark-tmp-$timestamp.jpg" +light_live_target="$TARGET_DIR/light-tmp-$timestamp.jpg" + + + +mkdir -p \ + "$TARGET_DIR" \ + "$(dirname "$LOCAL_INI")" \ + "$(dirname "$STATE_FILE")" + + +# Fixed-name copies, referenced as login-time defaults in the *.ini files +cp -- "$dark_source" "$dark_target" +cp -- "$light_source" "$light_target" + + +# Uniquely named copies for the live `gsettings` URIs +# to ensure `gnome-shell` uses the correct files right away +cp -- "$dark_source" "$dark_live_target" +cp -- "$light_source" "$light_live_target" + + +printf '%s\n' "$dark_source" > "$STATE_FILE" + + + +Background=org.gnome.desktop.background +Screensaver=org.gnome.desktop.screensaver + +gsettings set $Background picture-uri-dark "file://$dark_live_target" +gsettings set $Background picture-uri "file://$light_live_target" +gsettings set $Screensaver picture-uri "file://$dark_live_target" + + + +# Persist the unique URIs for `gnome-restore`, +# which loads local/*.ini after the base *.ini files +cat > "$LOCAL_INI" <Print" does) +# +# The screenshot machinery lives inside `gnome-shell` +# and cannot be called directly because +# its D-Bus methods are restricted to trusted callers +# +# => We synthesize the key press instead, after a short delay +# so that a launcher's keyboard grab (e.g., `rofi`) is released + +set -eu + + +[ "${XDG_SESSION_TYPE:-}" = "x11" ] || { + echo "X11 only (xdotool)" >&2 + exit 1 +} + + +# Optional countdown in seconds (e.g., `screenshot-screen 3`), +# defaulting to 0.2 +countdown="${1:-0.2}" + +case "$countdown" in + ''|.|*[!0-9.]*|*.*.*) + echo "Not a number: $countdown" >&2 + exit 1 + ;; +esac + +sleep "$countdown" + + +xdotool key shift+Print diff --git a/.local/bin/screenshot-ui b/.local/bin/screenshot-ui new file mode 100755 index 0000000..8afa0e9 --- /dev/null +++ b/.local/bin/screenshot-ui @@ -0,0 +1,25 @@ +#!/bin/sh + +# Open GNOME's screenshot UI +# (i.e., what a simple "Print" does) +# +# The screenshot machinery lives inside `gnome-shell` +# and cannot be called directly because +# its D-Bus methods are restricted to trusted callers +# +# => We synthesize the key press instead, after a short delay +# so that a launcher's keyboard grab (e.g., `rofi`) is released + +set -eu + + +[ "${XDG_SESSION_TYPE:-}" = "x11" ] || { + echo "X11 only (xdotool)" >&2 + exit 1 +} + + +sleep 0.2 + + +xdotool key Print diff --git a/.local/bin/screenshot-window b/.local/bin/screenshot-window new file mode 100755 index 0000000..85ba965 --- /dev/null +++ b/.local/bin/screenshot-window @@ -0,0 +1,36 @@ +#!/bin/sh + +# Take a screenshot of the focused window +# (i.e., what "Print" does) +# +# The screenshot machinery lives inside `gnome-shell` +# and cannot be called directly because +# its D-Bus methods are restricted to trusted callers +# +# => We synthesize the key press instead, after a short delay +# so that a launcher's keyboard grab (e.g., `rofi`) is released + +set -eu + + +[ "${XDG_SESSION_TYPE:-}" = "x11" ] || { + echo "X11 only (xdotool)" >&2 + exit 1 +} + + +# Optional countdown in seconds (e.g., `screenshot-window 3`), +# defaulting to 0.2 +countdown="${1:-0.2}" + +case "$countdown" in + ''|.|*[!0-9.]*|*.*.*) + echo "Not a number: $countdown" >&2 + exit 1 + ;; +esac + +sleep "$countdown" + + +xdotool key alt+Print diff --git a/.local/bin/wifi-toggle b/.local/bin/wifi-toggle new file mode 100755 index 0000000..7eb4c1d --- /dev/null +++ b/.local/bin/wifi-toggle @@ -0,0 +1,27 @@ +#!/bin/sh + +# Toggle the WiFi on or off + +set -eu + + +_notify() { + command -v notify-send >/dev/null 2>&1 || return 0 + notify-send 'WiFi' "$1" +} + + +case "$(nmcli -t radio wifi)" in + enabled) + nmcli radio wifi off + _notify 'Radio turned off' + ;; + disabled) + nmcli radio wifi on + _notify 'Radio turned on' + ;; + *) + _notify 'No radio available' + exit 1 + ;; +esac diff --git a/.local/share/applications/README.md b/.local/share/applications/README.md new file mode 100644 index 0000000..4cb1da1 --- /dev/null +++ b/.local/share/applications/README.md @@ -0,0 +1,4 @@ +# Desktop Entries + +This folder contains `*.desktop` launchers + for some scripts in [`~/.local/bin`](../../bin). diff --git a/.local/share/applications/screenshot-screen.desktop b/.local/share/applications/screenshot-screen.desktop new file mode 100644 index 0000000..46f338d --- /dev/null +++ b/.local/share/applications/screenshot-screen.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=Capture the whole screen +Exec=screenshot-screen +Icon=applets-screenshooter +Terminal=false +Categories=Utility; diff --git a/.local/share/applications/screenshot-ui.desktop b/.local/share/applications/screenshot-ui.desktop new file mode 100644 index 0000000..1bb106a --- /dev/null +++ b/.local/share/applications/screenshot-ui.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=Open GNOME's screenshot UI +Exec=screenshot-ui +Icon=applets-screenshooter +Terminal=false +Categories=Utility; diff --git a/.local/share/applications/screenshot-window.desktop b/.local/share/applications/screenshot-window.desktop new file mode 100644 index 0000000..b021a1f --- /dev/null +++ b/.local/share/applications/screenshot-window.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=Capture the focused window +Exec=screenshot-window +Icon=applets-screenshooter +Terminal=false +Categories=Utility; diff --git a/.zshrc b/.zshrc index 5814642..142b155 100644 --- a/.zshrc +++ b/.zshrc @@ -75,6 +75,15 @@ compdef '_arguments "--force[skip safety checks / reinstall]"' dotfiles-install # Complete the `--quieter` flag of the `generate-shell-completions` utility in ~/.local/bin compdef '_arguments "--quieter[suppress the reload hint]"' generate-shell-completions +# Complete the flags of the `color-palette` in ~/.local/bin +# => Keep the (short) descriptions in sync +# with the `argparse` section in ~/.local/bin/color-palette +compdef '_arguments \ + "--scheme[Override the live GNOME color-scheme]:scheme:(dark light)" \ + "--show[Show the derived colors as swatches]" \ + "--write[Write the palette into all consumer files]"' \ + color-palette + # Enable match highlighting and scrolling through long lists, # and provide a different style of menu completion zmodload zsh/complist