PaperWM Window Drag Conflict Analysis #
Problem Summary #
AC Electron app's drag events on side pane tabs don't work in Wayland Fedora when PaperWM is enabled, even though they work on Mac, Windows, and when PaperWM is disabled. The window is set to PaperWM's "scratch layer" for always-on-top behavior.
Root Cause #
AC Electron App Implementation #
File: ac-electron/renderer/flip-view.html:1383-1432
tab.addEventListener('mousedown', (e) => {
// Tracks screen coordinates
startX = e.screenX;
startY = e.screenY;
startWinX = window.screenX;
startWinY = window.screenY;
const onMouseMove = (e) => {
const dx = e.screenX - startX;
const dy = e.screenY - startY;
// Sends move command to main process
ipcRenderer.send('move-window', {
x: startWinX + dx,
y: startWinY + dy
});
};
});
File: ac-electron/main.js:2020-2026
ipcMain.on('move-window', (event, position) => {
const senderWindow = BrowserWindow.fromWebContents(event.sender);
if (senderWindow) {
senderWindow.setPosition(Math.round(position.x), Math.round(position.y));
}
});
PaperWM Implementation #
File: /tmp/PaperWM/grab.js
PaperWM intercepts ALL window move operations through its MoveGrab class:
class MoveGrab {
begin() {
grabbed = true;
global.display.end_grab_op?.(global.get_current_time());
// PaperWM takes control of dragging
}
motion(_actor, event) {
let [gx, gy] = global.get_pointer();
// PaperWM's own positioning logic
clone.x = gx - dx;
clone.y = gy - dy;
}
}
File: /tmp/PaperWM/scratch.js:44-59
Scratch windows (what AC uses for always-on-top) have special animation:
export function easeScratch(metaWindow, targetX, targetY, params = {}) {
// PaperWM animates scratch windows with its own easing
Easer.addEase(metaWindow.get_compositor_private(), {
x: targetX - dx,
y: targetY - dy,
time: Settings.prefs.animation_time,
onComplete: () => {
metaWindow.move_frame(true, targetX, targetY);
},
});
}
File: /tmp/PaperWM/scratch.js:80-81
metaWindow.make_above(); // Always on top
metaWindow.stick(); // Show on all workspaces
The Conflict #
- AC Electron calls
BrowserWindow.setPosition(x, y)which internally calls X11/Wayland window positioning - PaperWM intercepts Wayland window move operations via GNOME Shell's Meta API
- PaperWM's grab system (
grabbed = true) blocks external position changes - Scratch windows get additional special handling that conflicts with programmatic moves
On Wayland, PaperWM has tighter control over window management compared to X11, Mac, or Windows.
Solutions #
Option 1: Bypass PaperWM (Recommended) #
Detect PaperWM and use native GNOME window hints:
// In ac-electron/main.js
async function isPaperWMActive() {
try {
const result = execSync(
'gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell --method org.gnome.Shell.Eval "global.display"',
{ encoding: 'utf8', timeout: 1000 }
);
// Check if PaperWM extension is in the output
return result.includes('paperwm');
} catch {
return false;
}
}
// Modify window creation for PaperWM compatibility
if (await isPaperWMActive()) {
win.setSkipTaskbar(true);
win.setAlwaysOnTop(true);
// Don't use transparent + frameless which confuses PaperWM
// Use native GNOME window hints instead
}
Option 2: Native Drag Handle #
Use Electron's built-in -webkit-app-region CSS for PaperWM compatibility:
<!-- In flip-view.html -->
<style>
.flip-tab {
-webkit-app-region: drag; /* Native drag handle */
}
/* Disable for interactive elements */
button, input {
-webkit-app-region: no-drag;
}
</style>
Remove custom mousedown handlers for drag, let Electron handle it natively. This works with PaperWM because it uses the window's native drag affordances.
Option 3: Detect and Disable Custom Drag #
Check if running under PaperWM and disable custom drag logic:
// In flip-view.html
const isPaperWM = await ipcRenderer.invoke('check-paperwm');
if (!isPaperWM) {
// Only attach custom drag handlers if not PaperWM
flipTabs.forEach(tab => {
tab.addEventListener('mousedown', handleCustomDrag);
});
} else {
// Use native window dragging
flipTabs.forEach(tab => {
tab.style.webkitAppRegion = 'drag';
});
}
Option 4: Work With PaperWM's API #
Instead of fighting PaperWM, use its positioning system:
// Send D-Bus commands to PaperWM
async function movePaperWMWindow(metaWindow, x, y) {
execSync(`gdbus call --session --dest org.gnome.Shell \
--object-path /org/gnome/Shell \
--method org.gnome.Shell.Eval \
"imports.ui.main.extensionManager.lookup('paperwm@hedning:matrix.org').stateObj.scratch.easeScratch(${metaWindow}, ${x}, ${y})"`
);
}
This is fragile but would work perfectly with PaperWM.
Testing the Fix #
-
Detect PaperWM presence:
gnome-extensions list | grep paperwm -
Check if scratch mode interferes:
gdbus call --session --dest org.gnome.Shell \ --object-path /org/gnome/Shell \ --method org.gnome.Shell.Eval \ "global.display.focus_window" -
Test native drag:
- Apply
-webkit-app-region: dragto flip tabs - Verify dragging works
- Check if it breaks flip functionality
- Apply
Recommended Approach #
Use Option 2 (Native Drag) with a progressive enhancement:
- Try
-webkit-app-region: dragfor the flip tabs - Keep flip click detection but remove custom move logic
- Only use custom drag on platforms where native doesn't work
This is cleanest and works across all platforms including PaperWM.
Further Investigation #
Clone PaperWM settings to see if there's a config option:
dconf dump /org/gnome/shell/extensions/paperwm/
Look for:
scratch-window-behaviorwindow-position-mode- Any drag/move related settings
The user might be able to whitelist the AC window or disable scratch positioning for specific windows.