maxrave-dev / maxrave-dev/SimpMusic

Desktop (Linux/XWayland): UI freezes permanently when the window is moved to another monitor

Open
#2,503 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Kotlin
Stars
11.4k
Forks
599
Avg merge
3d 18h
Merged PRs (30d)
7

Description

Duplicate Check
  • I confirm I searched for existing reports and found no duplicates.
Describe the Bug

On Linux desktop (GNOME on Wayland, so the app runs under XWayland) with two monitors, moving the SimpMusic window from one monitor to the other freezes the UI permanently. Audio keeps playing (mpv is on its own native thread) and some clicks still reach the app (they trigger state changes, even open new windows like the lyrics pop-up), but the main window is never repainted again. The only way out is killing the process.

I spent some time digging into it, here is what I found, hoping it saves the maintainers a few hours.

Diagnosis

  • Thread dumps (jcmd <pid> Thread.print) taken while frozen show no deadlock and no busy thread: AWT-EventQueue-0 is idle in EventQueue.getNextEvent, AWT-XAWT is idle in XToolkit.waitForEvents, CPU is 0%. The JVM simply never gets asked to draw again.
  • With -Dskiko.fps.enabled=true the FPS counter runs at ~35 fps and stops exactly at the moment the window crosses the monitor boundary. Never resumes.
  • Under XWayland Java uses Xinerama, so each monitor is a separate GraphicsDevice (:0.0 and :0.1). When the window crosses over, XWindowPeer.checkIfOnNewScreen triggers a display change and the window visibly "closes and reopens" for an instant (peer recreated). Skiko's SkiaLayer gets the hide but apparently never the show again, so redrawer.setVisible(true) / needRedraw are never called again.
  • It is not the render API: it happens both with LinuxOpenGLRedrawer (default) and with -Dskiko.renderApi=SOFTWARE. It is also not the Java2D pipeline (-Dsun.java2d.xrender=false changes nothing).

Workaround that works for me

Make Java see a single screen, so there is no "new screen" event at all. I built a 4-function stub libXinerama.so.1 that reports Xinerama as inactive and put it first in LD_LIBRARY_PATH for the app only:

int   XineramaQueryExtension(void *d, int *ev, int *err) { return 0; }
int   XineramaQueryVersion(void *d, int *maj, int *min)  { return 0; }
int   XineramaIsActive(void *d)                          { return 0; }
void *XineramaQueryScreens(void *d, int *n)              { if (n) *n = 0; return 0; }

With that, GraphicsEnvironment.getScreenDevices() returns one 3840x1080 device, the window can be dragged between monitors freely, and the OpenGL redrawer stays at full speed. GTK (tray/file dialogs) is unaffected since it uses RandR.

Side note for anyone trying this with the AppImage/Conveyor build: the Conveyor launcher discards JAVA_TOOL_OPTIONS, and the bundled jlink runtime has no bin/java, so to pass extra JVM flags I had to dump the launcher's arguments with CONVEYOR_DUMP_JVM_ARGS=1 bin/simpmusic and start the JVM directly (copying bin/java from a system JDK 21 into lib/runtime/bin/). It would be nice if the launcher honoured JAVA_TOOL_OPTIONS or an app.jvm.options override, so users could try flags without this dance.

Possible fix on the app side

Either re-arm the Skiko layer after a display change (e.g. listen for graphicsConfiguration property changes on the ComposeWindow and call needRedraw() / re-run checkShowing), or ship the app with Xinerama disabled for AWT when running under XWayland. Happy to test a build.

Steps to Reproduce
  1. GNOME Wayland session with two monitors (both 1920x1080, scale 1, side by side).
  2. Start SimpMusic desktop (AppImage 2.1.0) and play something.
  3. Drag the window to the other monitor.
  4. UI freezes at the moment the window crosses the boundary; audio continues.
Expected Behavior

The window keeps rendering after being moved to another monitor.

App Version

2.1.0 (desktop, AppImage / Conveyor build, bundled JDK 21.0.2, Skiko 0.148.1)

Android Version

N/A - CachyOS (Arch), kernel 7.2.4, GNOME on Wayland (XWayland for the app)

Device Model

Laptop, AMD Radeon 680M (Mesa), 2 external/internal monitors

Custom ROM
  • I am using a custom ROM
Custom ROM Name (if applicable)

No response

Screenshots (optional)

No response

Log / Error Details

Nothing is printed on stderr or in the journal at the moment of the freeze. Relevant thread dump excerpt while frozen:

"AWT-EventQueue-0" ... waiting on condition
   java.lang.Thread.State: WAITING (parking)
	at java.awt.EventQueue.getNextEvent(EventQueue.java:565)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:190)

"AWT-XAWT" ... runnable
	at sun.awt.X11.XToolkit.waitForEvents(Native Method)
	at sun.awt.X11.XToolkit.run(XToolkit.java:687)

Skiko FPS log (-Dskiko.fps.enabled=true -Dskiko.fps.periodSeconds=2), last lines before the drag and then silence:

[11930842] FPS 36 (31-38)
[11932845] FPS 34 (32-37)

GraphicsEnvironment.getScreenDevices() on this setup, without / with the stub:

:0.0 java.awt.Rectangle[x=1920,y=0,width=1920,height=1080]
:0.1 java.awt.Rectangle[x=0,y=0,width=1920,height=1080]
---
:0.0 java.awt.Rectangle[x=0,y=0,width=3840,height=1080]

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the desktop ComposeWindow and SkiaLayer integration, focusing on the graphicsConfiguration change when XWayland moves the window between monitors. Reproduce the two-monitor drag with the supplied Skiko flags and thread observations; done means the UI resumes rendering after crossing the monitor boundary without disabling Xinerama.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin, linux
Domain
desktop, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.