Personalise GNOME

I switched from openSUSE to Debian stable a week ago. As great as Debian is, it unfortunately has the disadvantage that I could no longer use Niri. That left SwayWM or GNOME.

After a few days, I realised that I don't really need a tiling window manager, I just like working with workspaces.

So the choice fell on GNOME, which I have grown to like very much and, above all, which looks really good. I never warmed to KDE, but everyone has their own preferences.

Since I only learned programming with Turbo Pascal and DBase, the AI had to help out a bit.

I would be delighted if some experts would take a look at the scripts. In my opinion, what the AI has written looks good, but perhaps it could be even better with a real brain.

Files:

You can find all scripts of the following instructions on Codeberg in my dotfiles.

Problem 1: Start apps on a certain workspace

GNOME has workspaces, but I missed being able to start a programme on a workspace with a keyboard shortcut. For example, Tuba (Mastodon) is always on workspace two.

Unfortunately, GNOME does not offer this by default.

Solution 1: Extensions, bash and keybindings

Update: 21.09.2025

I changed everything to a combination of the Auto Move Extension and changing the shortcuts of the dash app via dconf.

For an example:

In the extension I assigned Tuba to Workspace 2 and changed the shortcut of application 4 in the dash to SUPER+SHIFT+T.

Now every time I press SUPER+SHIFT+T either Tuba starts or the app is focused on workspace 2.

Problem 2: Sync Settings

I use two computers: a desktop on my desk and a Framework 12 laptop. GNOME stores its settings in dconf. So I needed a tool that would export certain settings (e.g., keyboard shortcuts), push them to git, and import them back onto the other computer.

Solution 2: Export dconf and sync over git

The AI also helped after I spent a long time explaining what I needed.

#!/bin/bash
set -euo pipefail

# ---------- CONFIG ----------
GITHUB="$HOME/.dotdrop"			
REMOTE="$GITHUB/dotfiles/Gnome"	
mkdir -p "$REMOTE"

DCONFPATHS=(
  "/org/gnome/desktop/wm/keybindings/"
  "/org/gnome/mutter/keybindings/"
  "/org/gnome/settings-daemon/plugins/media-keys/"
  "/org/gnome/shell/"
)
# ----------------------------

# Build readable path list
PATH_LIST=""
for p in "${DCONFPATHS[@]}"; do PATH_LIST+="$p"$'\n'; done

# ---------- ACTION HANDLERS ----------
action_export() {
  cd "$GITHUB"
  git pull --ff-only || { echo "Git pull failed – aborting." >&2; exit 1; }

  for path in "${DCONFPATHS[@]}"; do
    filename=$(echo "$path" | tr '/' '_').conf
    dconf dump "$path" > "$REMOTE/$filename"
  done
  echo "Exported to $REMOTE"

  # exportierte Dateien committen
  git add "$REMOTE/*"
  git commit -m "Export GNOME shortcuts $(date +%F-%T)"
  git push
}

action_import() {
  cd "$GITHUB"
  git pull --ff-only || { echo "Git pull failed – aborting." >&2; exit 1; }

  for path in "${DCONFPATHS[@]}"; do
    filename=$(echo "$path" | tr '/' '_').conf
    file="$REMOTE/$filename"
    [[ -f "$file" ]] && dconf load "$path" < "$file" || echo "File not found: $file" >&2
  done
  echo "Imported from $REMOTE"
}

action_open() {
  echo "Storage location: $REMOTE"
  xdg-open "$REMOTE" || true
}

# ---------- CLI ENTRY ----------
case "${1:-}" in
  export)  action_export ;;
  import)  action_import ;;
  open)    action_open ;;
  *)       echo "Usage: $0 {export|import|open}" >&2; exit 1 ;;
esac

In my case, I export to the .dotdrop directory and sync this with Codeberg via git. Since I also use the dotdrop tool, this directory was the obvious choice.

Problem 3: Power-Screen

I was used to using wlogout in niri or swaywm. Of course, in GNOME wlogout works too, but it didn't really function properly. For example, there is no transparent background.

Solution 3: Simple Python Script

After a long dialogue, the AI created a simple Python script that uses images from the Papyrus icon set.

Set as a shortcut to SUPER+SHIFT+E... Works.

#!/usr/bin/env python3
# coded with the help of AI

import gi, subprocess, os
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, Gdk, GdkPixbuf

ICON_DIR = "/usr/share/icons/Papirus-Dark/24x24@2x/actions"

class PowerPanel(Gtk.Application):
    def __init__(self):
        super().__init__(application_id='org.nasendackel.Powerpanel')
        self.win   = None
        self.cmds  = {
            'system-suspend.svg':     'systemctl suspend',
            'system-shutdown.svg':    'systemctl poweroff',
            'system-reboot.svg':      'systemctl reboot',
            'system-lock-screen.svg': 'dbus-send --type=method_call --dest=org.gnome.ScreenSaver /org/gnome/ScreenSaver org.gnome.ScreenSaver.Lock',
            'system-log-out.svg':   'gnome-session-quit --no-prompt'
        }

    def do_activate(self):
        self.win = Gtk.ApplicationWindow(application=self)
        self.win.set_default_size(530, 120)
        self.win.set_resizable(False)
        self.win.set_decorated(False)

        esc = Gtk.EventControllerKey()
        esc.connect('key-pressed', self.on_esc)
        self.win.add_controller(esc)

        css = Gtk.CssProvider()
        css.load_from_data(b'''
        window { background: transparent; }
        button {
            background: #1a1a1a;
            border: none;
            outline: none;
            border-radius:12px;
            border:1px solid #fff;
            }
        button:focus { background: #A51D2D; }
        ''')
        display = Gdk.Display.get_default()
        Gtk.StyleContext.add_provider_for_display(
            display, css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
        for name in ['system-suspend.svg','system-shutdown.svg','system-reboot.svg',
                     'system-lock-screen.svg','system-log-out.svg']:
            pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
                        os.path.join(ICON_DIR, name), 120, 120, True)
            texture = Gdk.Texture.new_for_pixbuf(pixbuf)
            picture = Gtk.Picture.new_for_paintable(texture)
            picture.set_can_shrink(True)
            btn = Gtk.Button()
            btn.set_child(picture)
            btn.set_size_request(120,80)
            btn.connect('clicked', self.on_clicked, self.cmds[name])
            box.append(btn)
        self.win.set_child(box)
        self.win.present()

    def on_esc(self, ctrl, keyval, keycode, state):
        if keyval == Gdk.KEY_Escape:
            self.quit()
            return Gdk.EVENT_STOP
        return Gdk.EVENT_PROPAGATE

    def on_clicked(self, btn, cmd):
        subprocess.Popen(cmd, shell=True)
        self.quit()

if __name__ == '__main__':
    PowerPanel().run()