Reduce idle CPU use and prioritize compositor rendering (#62)

Avoid background title updates when no shell window list is visible, and skip output and capture invalidation for titles outside compositor decorations. Preserve focused titles, fresh window lists and capture privacy.

Request high EGL context priority with fallback, cover rendering and native title behavior in CI, and keep audio recording flowing while video capture is idle.
This commit is contained in:
Samuel Onoja authored and GitHub committed 2026-10-25 14:54:50 +01:00
1 parent b66b19e4d9
commit 979afbd213
21 files changed
+876 -312

No files matched your search

+118
View File
@@ -0,0 +1,118 @@
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include "xdg-shell-client-protocol.h"
static struct wl_compositor *compositor;
static struct wl_shm *shm;
static struct xdg_wm_base *wm;
static void ping(void *data, struct xdg_wm_base *base, uint32_t serial) {
(void)data;
xdg_wm_base_pong(base, serial);
}
static const struct xdg_wm_base_listener wm_listener = {.ping = ping};
static void global(void *data, struct wl_registry *registry, uint32_t name,
const char *interface, uint32_t version) {
(void)data;
(void)version;
if (!strcmp(interface, "wl_compositor"))
compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
else if (!strcmp(interface, "wl_shm"))
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
else if (!strcmp(interface, "xdg_wm_base")) {
wm = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(wm, &wm_listener, NULL);
}
}
static void removed(void *data, struct wl_registry *registry, uint32_t name) {
(void)data;
(void)registry;
(void)name;
}
static const struct wl_registry_listener registry_listener = {global, removed};
static void configure(void *data, struct xdg_surface *xdg, uint32_t serial) {
(void)data;
xdg_surface_ack_configure(xdg, serial);
}
static const struct xdg_surface_listener surface_listener = {.configure = configure};
static void size(void *data, struct xdg_toplevel *top, int32_t width, int32_t height,
struct wl_array *states) {
(void)data;
(void)top;
(void)width;
(void)height;
(void)states;
}
static void close_window(void *data, struct xdg_toplevel *top) {
(void)data;
(void)top;
exit(0);
}
static const struct xdg_toplevel_listener top_listener = {
.configure = size,
.close = close_window,
};
int main(void) {
struct wl_display *display = wl_display_connect(NULL);
if (!display) return 1;
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
if (wl_display_roundtrip(display) < 0 || !compositor || !shm || !wm) return 2;
int fd = memfd_create("window-titles", MFD_CLOEXEC);
if (fd < 0 || ftruncate(fd, 160 * 120 * 4)) return 3;
uint32_t *pixels = mmap(NULL, 160 * 120 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (pixels == MAP_FAILED) return 4;
for (size_t i = 0; i < 160 * 120; i++) pixels[i] = 0xff3182ce;
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, 160 * 120 * 4);
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, 160, 120, 160 * 4, WL_SHM_FORMAT_XRGB8888);
wl_shm_pool_destroy(pool);
munmap(pixels, 160 * 120 * 4);
close(fd);
struct xdg_toplevel *tops[2];
for (int i = 0; i < 2; i++) {
struct wl_surface *window = wl_compositor_create_surface(compositor);
struct xdg_surface *xdg = xdg_wm_base_get_xdg_surface(wm, window);
xdg_surface_add_listener(xdg, &surface_listener, NULL);
tops[i] = xdg_surface_get_toplevel(xdg);
xdg_toplevel_add_listener(tops[i], &top_listener, NULL);
xdg_toplevel_set_app_id(tops[i], "ferese.test.titles");
xdg_toplevel_set_title(tops[i], i ? "Focused window" : "Background window");
xdg_toplevel_set_min_size(tops[i], 160, 120);
xdg_toplevel_set_max_size(tops[i], 160, 120);
wl_surface_commit(window);
if (wl_display_roundtrip(display) < 0) return 5;
xdg_surface_set_window_geometry(xdg, 0, 0, 160, 120);
wl_surface_attach(window, buffer, 0, 0);
wl_surface_damage(window, 0, 0, 160, 120);
wl_surface_commit(window);
if (wl_display_roundtrip(display) < 0) return 5;
}
puts("ready"); fflush(stdout);
char line[256];
while (fgets(line, sizeof line, stdin)) {
int index = line[0] - '0';
if (index < 0 || index > 1 || line[1] != ' ') return 6;
line[strcspn(line, "\n")] = 0;
xdg_toplevel_set_title(tops[index], line + 2);
if (wl_display_roundtrip(display) < 0) return 5;
puts("changed"); fflush(stdout);
}
wl_display_disconnect(display);
return 0;
}
@@ -113,7 +113,9 @@ class BackgroundPortal(unittest.TestCase):
def snapshot(peer, app):
send(peer, {"version": 1, "generation": 0, "last": True, "type": "snapshot", "desktop": {
"outputs": [], "workspaces": [], "windows": [{"id": 1, "app_id": app, "focused": True}],
"outputs": [], "workspaces": [], "windows": [{
"id": 1, "app_id": app, "title": app, "focused": True,
"workspace": 1, "floating": False, "fullscreen": False}],
"focus": {"window": 1}, "config": {"revision": 0, "error": None}, "theme": None,
"lock": {"phase": "unlocked", "sleeping": False}}})
@@ -0,0 +1,183 @@
"""Keep a real GPU panel idle during background title changes.
FERESE_TEST_SHELL_TITLES=1 python3 scripts/tests/test_shell_title_updates_isolated.py
Requires a Wayland host, cc, wayland-scanner, and dbus-daemon.
"""
import json
import os
from pathlib import Path
import re
import select
import shlex
import signal
import subprocess
import tempfile
import time
import unittest
from session_socket import ipc_environment, ipc_socket
from test_shell_motion_isolated import panel_config
@unittest.skipUnless(os.environ.get("FERESE_TEST_SHELL_TITLES") == "1", "opt-in nested shell test")
class ShellTitleUpdatesTest(unittest.TestCase):
def test_background_titles_only_redraw_visible_window_lists(self):
repo = Path(__file__).resolve().parents[2]
binary = repo / os.environ.get("FERESE_TEST_BINARY", "target/release/ferese")
shell = repo / os.environ.get("FERESE_TEST_SHELL", "target/release/ferese-shell")
ctl = repo / os.environ.get("FERESE_TEST_CTL", "target/release/feresectl")
children = []
with tempfile.TemporaryDirectory(prefix="ferese-shell-titles-") as temporary:
root = Path(temporary)
protocols = subprocess.check_output(
["pkg-config", "--variable=pkgdatadir", "wayland-protocols"], text=True).strip()
xml = str(Path(protocols) / "stable/xdg-shell/xdg-shell.xml")
for kind, output in [("client-header", "xdg-shell-client-protocol.h"),
("private-code", "xdg-shell-protocol.c")]:
subprocess.run(["wayland-scanner", kind, xml, str(root / output)], check=True)
flags = shlex.split(subprocess.check_output(
["pkg-config", "--cflags", "--libs", "wayland-client"], text=True))
subprocess.run(["cc", "-Wall", "-Wextra", "-Werror", "-I", str(root),
str(repo / "scripts/tests/fixtures/window-titles.c"),
str(root / "xdg-shell-protocol.c"), "-o", str(root / "client"), *flags], check=True)
runtime = root / "runtime"
runtime.mkdir(mode=0o700)
config = root / "config/ferese"
config.mkdir(parents=True)
(config / "config.kdl").write_text(
'status { keybinding-guide #false; }\n'
'window-rule app-id="ferese.test.titles" floating=#true\n' + panel_config("flat"))
display = Path(os.environ["WAYLAND_DISPLAY"])
if not display.is_absolute():
display = Path(os.environ["XDG_RUNTIME_DIR"]) / display
env = {key: value for key, value in os.environ.items() if not key.startswith("FERESE_")}
env.update(XDG_RUNTIME_DIR=str(runtime), WAYLAND_DISPLAY=str(display),
XDG_CONFIG_HOME=str(root / "config"), XDG_STATE_HOME=str(root / "state"),
XDG_CACHE_HOME=str(root / "cache"), XDG_DATA_HOME=str(root / "data"),
FERESE_COMPOSITOR_WALLPAPER="1", RUST_LOG="ferese=info")
env.pop("WAYLAND_SOCKET", None)
bus_config = root / "bus.conf"
bus_config.write_text(
'<busconfig><type>session</type><listen>unix:tmpdir=/tmp</listen>'
'<policy context="default"><allow send_destination="*"/>'
'<allow receive_sender="*"/><allow own="*"/></policy></busconfig>')
log_path = root / "session.log"
def launch(command, **kwargs):
process = subprocess.Popen(command, env=ipc_environment(env), start_new_session=True, **kwargs)
children.append(process)
return process
def wait_for(predicate):
deadline = time.monotonic() + 15
while time.monotonic() < deadline:
value = predicate()
if value:
return value
time.sleep(.025)
self.fail("Timed out:\n" + log_path.read_text()[-8000:])
def control(*args):
return subprocess.check_output([str(ctl), *args], env=ipc_environment(env), text=True, timeout=5)
def snapshots():
return log_path.read_text().count(".snapshot_end(")
def title(index, value):
client.stdin.write(f"{index} {value}\n")
client.stdin.flush()
self.assertTrue(select.select([client.stdout], [], [], 5)[0], "title client did not reply")
self.assertEqual(client.stdout.readline().strip(), "changed")
def burst(index, prefix):
for number in range(20):
title(index, f"{prefix} {number}")
time.sleep(.05)
time.sleep(.2)
def wait_title(value):
wait_for(lambda: any(window["title"] == value for window in json.loads(control("-j", "windows"))))
def focused_title():
return next((window["title"] for window in json.loads(control("-j", "windows"))
if window["focused"]), None)
def watch(enabled):
matches = re.findall(r"\.set_background_title_updates\(([01])\)", log_path.read_text())
return matches and matches[-1] == str(int(enabled))
try:
with log_path.open("w") as log:
bus = launch(["dbus-daemon", "--config-file=" + str(bus_config), "--nofork", "--print-address=1"],
stdout=subprocess.PIPE, stderr=log, text=True)
env["DBUS_SESSION_BUS_ADDRESS"] = bus.stdout.readline().strip()
compositor = launch([str(binary), "--backend", "nested", "--grant-effects", "--grant-shell-control",
"--", "env", "WAYLAND_DEBUG=client", "ICED_BACKEND=wgpu", str(shell)],
stdout=log, stderr=log)
wait_for(lambda: ipc_socket(runtime).is_socket() and watch(False))
child_display = wait_for(lambda: next((p for p in runtime.glob("wayland-*") if p.is_socket()), None))
env["WAYLAND_DISPLAY"] = str(child_display)
client = launch([str(root / "client")], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=log, text=True)
self.assertTrue(select.select([client.stdout], [], [], 10)[0], "window client did not map")
self.assertEqual(client.stdout.readline().strip(), "ready")
wait_for(lambda: len(json.loads(control("-j", "windows"))) == 2)
wait_for(lambda: focused_title() == "Focused window")
time.sleep(.5)
before = snapshots()
burst(0, "Hidden spinner")
wait_title("Hidden spinner 19")
self.assertEqual(snapshots(), before, "background titles repainted the closed panel")
print("Background: 20 actual title changes produced no shell snapshots", flush=True)
before = snapshots()
title(1, "Current focused title")
wait_title("Current focused title")
wait_for(lambda: snapshots() > before)
self.assertIn('"Current focused title"', log_path.read_text())
control("focus-last-window")
wait_for(lambda: focused_title() == "Hidden spinner 19")
wait_for(lambda: '"Hidden spinner 19"' in log_path.read_text())
control("focus-last-window")
wait_for(lambda: focused_title() == "Current focused title")
time.sleep(.2)
before = snapshots()
control("launcher", "toggle")
wait_for(lambda: watch(True) and snapshots() > before)
self.assertIn('"Hidden spinner 19"', log_path.read_text())
before = snapshots()
burst(0, "Visible list")
wait_title("Visible list 19")
wait_for(lambda: snapshots() > before)
self.assertIn('"Visible list 19"', log_path.read_text())
print("Launcher: opening fetched the latest title; live updates continued", flush=True)
control("launcher", "toggle")
wait_for(lambda: watch(False))
time.sleep(.5)
before = snapshots()
burst(0, "Closed list")
wait_title("Closed list 19")
self.assertEqual(snapshots(), before, "closing the launcher left a title subscription active")
self.assertIsNone(compositor.poll(), log_path.read_text()[-8000:])
self.assertNotIn("panicked at", log_path.read_text())
finally:
if destination := os.environ.get("FERESE_TEST_LOG"):
Path(destination).write_text(log_path.read_text() if log_path.exists() else "")
for process in reversed(children):
if process.poll() is None:
os.killpg(process.pid, signal.SIGTERM)
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
os.killpg(process.pid, signal.SIGKILL)
process.wait()
for stream in (process.stdin, process.stdout, process.stderr):
if stream is not None:
stream.close()
if __name__ == "__main__":
unittest.main()