Auto switch between light and dark mode on GNOME

I recently got a Framework laptop and installed Ubuntu on it to give Linux for laptops a chance after using Windows and Mac for work for years. One thing I wanted was to be able to switch between light mode and dark mode automatically depending on the time of day. GNOME had a blue-light filter mode that could automatically turn on, but it didn’t appear to have a way to switch between light mode and dark mode at the same time.

The GNOME settings window showing the ability to automatically enable/disable a blue light filter at specific times of the day.
The GNOME settings window showing the ability to automatically enable/disable a blue light filter at specific times of the day.

Research

Sort of Working

The following command seemed to switch the theme, but not all apps would follow. The terminal and GNOME would follow, but Firefox wouldn’t.

1
2
gsettings set org.gnome.desktop.interface gtk-theme Yaru-dark
gsettings set org.gnome.desktop.interface gtk-theme Yaru

Digging through Code

The GNOME settings window showing light and dark mode, but no option to schedule it

To figure this out, I dove into the source code for the GNOME settings tool (GitHub GNOME/gnome-control-center). Searching for strings like “light” and “dark” led me to the file panels/background/cc-background-panel.c which implemented this settings page.

These methods suggested there was a different setting that controlled the light/dark mode:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
static void
on_color_scheme_toggle_active_cb (CcBackgroundPanel *self)
{
  if (gtk_toggle_button_get_active (self->default_toggle))
    set_color_scheme (self, G_DESKTOP_COLOR_SCHEME_DEFAULT);
  else if (gtk_toggle_button_get_active (self->dark_toggle))
    set_color_scheme (self, G_DESKTOP_COLOR_SCHEME_PREFER_DARK);
}

static void
set_color_scheme (CcBackgroundPanel   *self,
                  GDesktopColorScheme  color_scheme)
{
  // Removed

  g_settings_set_enum (self->interface_settings,
                       INTERFACE_COLOR_SCHEME_KEY,
                       color_scheme);
}

Then using the following commands it actually worked and fully changed the color in Firefox and across my desktop environment.

1
2
gsettings set org.gnome.desktop.interface color-scheme prefer-dark
gsettings set org.gnome.desktop.interface color-scheme prefer-light

Scripts

I started with the script from here and changed the gsettings key

/usr/bin/gnome-theme-switcher.sh:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/bash
set_theme() {
  if [[ "$1" == "dark" ]]; then
    new_gtk_theme="prefer-dark"
  elif [[ "$1" == "light" ]]; then
    new_gtk_theme="prefer-light"
  else
    echo "[!] Unsupported theme: $1"
    return
  fi

  export DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS
  echo "$DBUS_SESSION_BUS_ADDRESS"
  current_gtk_theme=$(gsettings get org.gnome.desktop.interface color-scheme)
  # echo "[.] Currently using ${current_gtk_theme}"
  if [[ "${current_gtk_theme}" == "'${new_gtk_theme}'" ]]; then
    echo "[i] Already using gtk '${new_gtk_theme}' theme"
  else
    echo "[-] Setting gtk theme to ${new_gtk_theme}"
    gsettings set org.gnome.desktop.interface color-scheme ${new_gtk_theme}
    echo "[✓] gtk theme changed to ${new_gtk_theme}"
  fi
}

# If script run without argument
if [[ -z "$1" ]]; then
  currenttime=$(date +%H:%M)
  if [[ "$currenttime" > "17:00" || "$currenttime" < "09:00" ]]; then
    set_theme dark
  else
    set_theme light
  fi
else
  set_theme $1
fi

Move the file into a safe location. I use /usr/bin/ so it’s not user-writable by default:

1
2
sudo mv ~/gnome-theme-switcher.sh /usr/bin/
sudo chmod +x /usr/bin/gnome-theme-switcher.sh

Scheduling using systemd

Next up is getting systemd to run the command. The service unit defines what command to run.

~/.config/systemd/users/auto-theme-switcher.service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[Unit]
Description=Auto adjusts GNOME theme between dark and light to match the time
After=suspend.target

[Service]
Type=oneshot
ExecStart=/usr/bin/gnome-theme-switcher.sh
TimeoutSec=30
StandardOutput=journal
PrivateTmp=true
ProtectSystem=full
ProtectHome=read-only

[Install]
WantedBy=multi-user.target

The timer defines when the command should run. I choose to run mine at 8pm and 9am. If you change it, make sure to update the script above.

~/.config/systemd/users/auto-theme-switcher.timer:

1
2
3
4
5
6
7
8
9
[Unit]
Description=Auto adjusts GNOME theme between dark and light to match the time

[Timer]
OnCalendar=*-*-* 20:00:00 # 8pm
OnCalendar=*-*-* 09:00:00 # 9am

[Install]
WantedBy=timers.target

Then run the following to enable it

1
2
systemctl --user enable auto-theme.service
systemctl --user enable auto-theme.timer

You can test the command by running:

1
systemctl --user start auto-theme.service

References

Copyright - All Rights Reserved

Comments

Comments are currently unavailable while I move to this new blog platform. To give feedback, send an email to adam [at] this website url.