From 078f9acf718db90701c730de4a5516b1193ef550 Mon Sep 17 00:00:00 2001 From: Josue Zamudio Date: Tue, 27 Oct 2026 18:32:21 -0700 Subject: [PATCH] fix(display): target the Framework 13 Pro panel Migration from the ThinkPad X1 Carbon (2880x1800) to the Framework Laptop 13 Pro, whose panel is CSOT MND508ZB1-1 = 2880x1920 3:2 @120Hz and exposes only 2880x1920@120000Hz / @60000Hz. shikane was requesting 2880x1800@60.001Hz, a mode this panel cannot provide, so the variant could never be satisfied: it sat in NoVariantApplied for the whole session, with 61 historical "Discarded" events. Both profiles now use 2880x1920@120Hz (including the disabled eDP-1 block, whose mode string still has to resolve). Verified: profile now reaches VariantApplied. Scale is 1.75 -> 1645x1097 logical, ~147 effective DPI, chosen to reproduce the ~145 DPI of the old panel at 1.67. 1.75 is exactly representable as float32, which sway stores scale in; 1.6 and 1.67 are not. 2.0 would be the only integer (1:1, unresampled) scale but yields just 1440x960 logical. The .config/sway/config hunk also adds the matching `output $laptop` baseline so sway does not start at scale 2.0 and re-flow when shikane applies 1.75, and carries previously-uncommitted session wiring for the scripts below. shikane's config.toml was previously unversioned; it now lives in this repo and ~/.config/shikane is a symlink, matching the existing convention. --- .config/shikane/config.toml | 47 +++++++++++++++++++++++++++++++ .config/sway/config | 56 +++++++++++++++++++++++++++++-------- 2 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 .config/shikane/config.toml diff --git a/.config/shikane/config.toml b/.config/shikane/config.toml new file mode 100644 index 0000000..12127ca --- /dev/null +++ b/.config/shikane/config.toml @@ -0,0 +1,47 @@ +[[profile]] +name = "laptop" +exec = ["notify-send shikane \"Profile $SHIKANE_PROFILE_NAME has been applied\""] + [[profile.output]] + enable = true + search = "eDP-1" + # Panel on the Framework Laptop 13 Pro is CSOT MND508ZB1-1 = 2880x1920 (3:2) + # @120Hz. /sys/class/drm/card0-eDP-1/modes exposes ONLY 2880x1920@120000Hz + # and @60000Hz. The previous value here, 2880x1800@60.001Hz, was the OLD + # ThinkPad's panel mode and does not exist on this panel, so shikane could + # never satisfy the variant -- the most likely root cause of the + # VariantInProgress deadlocks. + mode = "2880x1920@120Hz" + position = "0,0" + # Scale 1.75 chosen deliberately (2026-10-27). + # Panel: CSOT MND508ZB1-1, 2880x1920 on 13.5in = 256 PPI. What matters is + # effective DPI = PPI/scale; 1.75 -> 147 DPI, which matches the 145 DPI this + # laptop's owner previously used on the old ThinkPad (2880x1800 on 14in at + # 1.67). The exact equivalent would be 1.765; 1.75 is the nearest value that + # is exactly representable as float32 (sway stores scale as float). + # 2.0 is the only integer scale (1:1 pixel mapping, no resampling, sharpest) + # but gives just 1440x960 logical. 1.5 = max workspace at 171 DPI (small). + # History: 1.67/1.5/1.6 all tried; 1.6 was not float-exact (1.600000024). + scale = 1.75 + transform = "normal" + adaptive_sync = false + +[[profile]] +name = "External Display" +exec = ["notify-send shikane \"Profile $SHIKANE_PROFILE_NAME has been applied\""] + [[profile.output]] + enable = false + search = "eDP-1" + # Even a disabled block carries a mode string that shikane must resolve + # against the panel's mode list, so keep it valid. + mode = "2880x1920@120Hz" + position = "0,0" + scale = 1.75 + transform = "normal" + adaptive_sync = false + + [[profile.output]] + enable = true + search = "n/DP-(.*)" + scale = 1.3 + transform = "normal" + adaptive_sync = false diff --git a/.config/sway/config b/.config/sway/config index 48028c9..44555df 100644 --- a/.config/sway/config +++ b/.config/sway/config @@ -28,15 +28,20 @@ include ~/.config/sway/config.d/* # You can get the names of your outputs by running: swaymsg -t get_outputs # https://gitlab.com/w0lff/shikane for multitple display handling #output * bg /home/jozamudi/Documents/Wallpapers/windows-11-stock-official-colorful-3840x2160-5658.jpg fill -#output eDP-1 scale 1.7 #output 'BNQ BenQ PD3200U S2N01233019' scale 1.5 +# Baseline for the built-in panel so sway starts at the same geometry shikane +# will apply. Panel is CSOT MND508ZB1-1: 2880x1920 (3:2) with only 120Hz/60Hz +# modes available. Without this, sway defaults to scale 2.0 and the desktop +# visibly re-flows when shikane applies 1.5 a moment after login. +output $laptop mode 2880x1920@120Hz scale 1.75 #exec_always shikanectl reload bindswitch --reload --locked lid:on output $laptop disable bindswitch --reload --locked lid:off output $laptop enable exec_always --no-startup-id '/home/work/.config/sway/scripts/clamshell-mode.sh' -# Re-apply shikane display profile (scaling) after every sway reload -exec_always --no-startup-id shikanectl reload +# Re-apply shikane display profile after every sway reload; +# falls back to a service restart if the daemon deadlocked +exec_always --no-startup-id /home/work/.config/sway/scripts/shikane-recover-on-reload.sh #input "1267:12792:ELAN067B:00_04F3:31F8_Touchpad" { # dwt enabled @@ -54,18 +59,33 @@ exec_always --no-startup-id shikanectl reload seat seat0 xcursor_theme Adwaita 35 +# Portal implementations are pinned in ~/.config/xdg-desktop-portal/portals.conf +# (default=gtk, ScreenCast/Screenshot=wlr, Inhibit=none, GlobalShortcuts=hyprland). exec systemctl --user start xdg-desktop-portal-wlr.service #exec pipewire -exec swaync -exec_always --no-startup-id pkill -x waybar; waybar -c ~/.config/sway/waybar/config.jsonc -s ~/.config/sway/waybar/style.css +# swaync is started by its systemd user unit / D-Bus activation; starting it +# here too caused "already running" crash-loops (start-limit-hit). +#exec swaync +# NOTE: the command must be quoted -- sway splits unquoted ';' into separate +# sway commands and fails with "Unknown/invalid command 'waybar'" +exec_always --no-startup-id "pkill -x waybar; waybar -c ~/.config/sway/waybar/config.jsonc -s ~/.config/sway/waybar/style.css" exec nm-applet exec blueman-applet exec hypridle +# hyprlock can outlive a sway session; a stale instance then makes hypridle's +# "pidof hyprlock || hyprlock" guard a permanent no-op. Clear leftovers at login. +exec --no-startup-id pkill -x hyprlock -exec systemctl --user start hyperolkitagent +# Polkit authentication agent. +# The original line was a typo ("hyperolkitagent"), so no agent ever started. +# hyprpolkitagent is installed but aborts on this machine: hyprtoolkit vendors +# aquamarine, which refuses to create a GBM allocator because the Intel Iris Xe +# iGPU has no PRIME export (drmGetCap DRM_CAP_PRIME -> unsupported) and then +# asserts "Couldn't open a gbm fd" instead of falling back -- 5/5 core dumps. +# Keep hyprpolkitagent.service disabled; polkit-gnome is used instead. +exec /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 exec flatpak run com.github.wwmm.easyeffects -w exec xrdb -merge ~/.Xresources -exec wl-paste -t text --watch clipman store --no-persist exec tlpctl power-saver @@ -74,13 +94,25 @@ exec tlpctl power-saver #exec mpd #exec syncthing -# Auto start .desktop files using dex -exec dex -a +# Auto start .desktop files using dex -- dex is not installed (and there is no +# ~/.config/autostart), so this was a no-op. +#exec dex -a #exec wl-paste -t text --watch clipman store --no-persist -# There are two wl-paste processes running, one for the primary and one for the normal clipboard -exec wl-paste -t text --watch clipman store -exec wl-paste -p -t text --watch clipman store +# There are two wl-paste watchers on purpose: one for the regular clipboard, +# one for the primary selection (clipman keeps a single shared history, the +# --primary flag on `pick` only decides where the picked item is served). +# Guarded with pkill because `exec` re-runs on every `swaymsg reload`: a stale +# `store --no-persist` instance from an older config survived three reloads and +# ran alongside the current pair. +# NOTE: `pkill -x` matches the process *name* only. Do NOT use `pkill -f` here -- +# sway runs exec lines through `sh -c`, so the wrapper's own command line +# contains the pattern and pkill would kill the shell that is about to start +# the replacements (learned the hard way). +exec_always --no-startup-id "pkill -x wl-paste; sleep 0.3; \ + wl-paste -t text --watch clipman store & \ + wl-paste -p -t text --watch clipman store & \ + wait" # The keybind to select a clipping bindsym $mod+y exec clipman --primary pick --tool wofi # Clear all clippings