GNOME Shell extension for Microsoft Surface hardware.
Control your Surface device directly from the Quick Settings panel — switch power profiles, check discrete GPU state, and manage Surface Book detachment without opening a terminal.
Switch between all four power modes from the Quick Settings menu:
| Profile | Description |
|---|---|
| Low Power | Maximum battery savings for reading, writing, light tasks |
| Balanced | Default everyday use — good balance of performance and battery |
| Balanced Performance | Extra headroom for demanding apps without full performance draw |
| Performance | Maximum CPU/GPU throughput, plugged-in use |
The tile shows the active profile. Clicking it toggles between Balanced and your last non-default profile. The chevron opens the full menu for direct selection. Changes made externally are reflected instantly via a kernel sysfs monitor — no polling.
A contextual icon appears in the top bar whenever you're on a non-Balanced profile — a quick visual reminder without permanent clutter.
Shows current dGPU power state and runtime PM mode in the menu. Hidden automatically on devices without a discrete GPU.
Request or cancel clipboard detachment directly from the Quick Settings panel. Hidden automatically on devices without DTX hardware.
Cap charging at 60%, 80%, or 100% (no limit) to reduce long-term battery wear, right from the menu. Hidden automatically on devices whose kernel doesn't expose charge_control_end_threshold.
- GNOME Shell 45 or later
- Linux Surface kernel (linux-surface)
- Battery charge limit requires kernel support for
charge_control_end_threshold, which is model-dependent — see linux-surface/linux-surface#1580. The section is simply hidden if your model doesn't expose it.
The kernel exposes the power profile at /sys/firmware/acpi/platform_profile, but only root can write it by default. Run the included setup script once to fix this permanently:
bash setup-permissions.shThis creates /etc/tmpfiles.d/surface-control.conf which runs at every boot via systemd-tmpfiles, granting your user group write access before the session starts. No reboot needed — it applies immediately.
If the extension loads without this step, the tile will show "Setup required" and profile switching will be disabled until you run the script.
The same script also grants write access to BAT*/charge_control_end_threshold if your kernel exposes it. This is non-blocking: if it's missing or still not writable, only the "Battery limit" section shows "Setup required" — platform profiles keep working normally.
The easy way — one click on extensions.gnome.org.
Or manually, from source:
git clone https://github.com/scaccogatto/gse-surface.git
cp -r gse-surface/surface-control@scaccogatto.github.com \
~/.local/share/gnome-shell/extensions/Log out and back in, then enable:
gnome-extensions enable surface-control@scaccogatto.github.com- Profile reads — watches
/sys/firmware/acpi/platform_profileviaGio.FileMonitorfor instant, zero-overhead change detection - Profile writes — writes directly to sysfs via
Gio.File; no subprocess, no PATH dependency - Battery limit — same
Gio.FileMonitor+ direct-write approach againstBAT*/charge_control_end_threshold, gated on a startup sysfs probe - Permission check — probes write access at startup via
Gio.Filemetadata (no read/write attempt); shows an in-menu error with instructions if access is missing - Hardware detection — probes
surface dgpuandsurface dtxat startup; sections unavailable on your device are simply not shown - Clean teardown — all monitors cancelled, all GObject references nulled on
disable()
# Clone
git clone https://github.com/scaccogatto/gse-surface.git
cd gse-surface
# Symlink for live editing
ln -sf "$PWD/surface-control@scaccogatto.github.com" \
~/.local/share/gnome-shell/extensions/
# Reload extension without logout (disable + enable via DBus)
gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell \
--method org.gnome.Shell.Extensions.DisableExtension \
"surface-control@scaccogatto.github.com"
gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell \
--method org.gnome.Shell.Extensions.EnableExtension \
"surface-control@scaccogatto.github.com"
# Watch logs
journalctl -f -o cat /usr/bin/gnome-shell | grep -i surfacesurface-control@scaccogatto.github.com/
├── metadata.json # Extension identity and supported shell versions
├── extension.js # Entry point — enable() / disable() lifecycle
├── indicator.js # SystemIndicator + QuickMenuToggle UI
├── profileManager.js # sysfs file monitor, permission check, profile read/write
├── batteryManager.js # BAT0/BAT1 threshold probe, sysfs file monitor, permission check, read/write
├── dgpuManager.js # dGPU detection and state
├── dtxManager.js # DTX detection and detachment controls
└── utils.js # Profile definitions, icons, subprocess helpers
setup-permissions.sh # One-time setup: grants write access to sysfs profile file
| GNOME Shell | Status |
|---|---|
| 50 | Tested |
| 45 – 49 | Should work (ESModules + Quick Settings API stable since 45) |
| < 45 | Not supported |
GPL-2.0-or-later — required for GNOME Shell extensions (GNOME Shell is GPL-2.0-or-later).
