fix call xseterrorhandler while gdk error trap is up

This commit is contained in:
2026-06-01 03:13:59 +02:00
parent 0be3662a93
commit 1776aa251a
2 changed files with 69 additions and 20 deletions

View File

@@ -24,10 +24,6 @@ import java.nio.file.Path;
import java.util.List;
public final class Main extends Application {
// Mouse pointer location captured in main() before JavaFX loads GTK; used to pick the
// startup monitor. Reading it later (after GTK) makes AWT's X11 init clash with GDK.
private static java.awt.Point startupPointer;
private Compositor compositor;
private TerminalMetrics metrics;
private AppConfig config;
@@ -75,10 +71,10 @@ public final class Main extends Application {
}
private static Screen activeScreen() {
java.awt.Point at = startupPointer;
int[] at = X11Pointer.query();
if (at != null) {
// AWT and JavaFX share a coordinate space on the X11 virtual screen.
List<Screen> screens = Screen.getScreensForRectangle(at.x, at.y, 1.0, 1.0);
// libX11 and JavaFX share a coordinate space on the X11 virtual screen.
List<Screen> screens = Screen.getScreensForRectangle(at[0], at[1], 1.0, 1.0);
if (!screens.isEmpty()) {
return screens.get(0);
}
@@ -230,19 +226,6 @@ public final class Main extends Application {
public static void main(String[] args) {
System.setProperty("prism.order", System.getProperty("prism.order", "es2,sw"));
// Initialise AWT and read the pointer here, before launch() loads GTK. Done afterwards,
// AWT's X11 init calls XSetErrorHandler while GDK has an error trap pushed and warns.
startupPointer = readPointerLocation();
launch(Main.class, args);
}
private static java.awt.Point readPointerLocation() {
try {
java.awt.PointerInfo pointer = java.awt.MouseInfo.getPointerInfo();
return pointer != null ? pointer.getLocation() : null;
} catch (Throwable ignored) {
// Headless or AWT unavailable — the startup monitor falls back to the primary screen.
return null;
}
}
}

View File

@@ -0,0 +1,66 @@
package com.gregor.jprototerm;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SymbolLookup;
import java.lang.invoke.MethodHandle;
import static java.lang.foreign.ValueLayout.ADDRESS;
import static java.lang.foreign.ValueLayout.JAVA_INT;
import static java.lang.foreign.ValueLayout.JAVA_LONG;
/**
* Reads the X11 pointer location directly via libX11 ({@code XQueryPointer}). Unlike AWT's
* {@code MouseInfo}, this never calls {@code XSetErrorHandler}, so it doesn't trip GDK's
* "XSetErrorHandler called with a GDK error trap pushed" warning when JavaFX's GTK backend is
* already up. Returns {@code null} when not on X11 or libX11 can't be loaded.
*/
final class X11Pointer {
private X11Pointer() {
}
/** {@code {x, y}} of the pointer in X root-window (virtual screen) space, or {@code null}. */
static int[] query() {
try (Arena arena = Arena.ofConfined()) {
Linker linker = Linker.nativeLinker();
SymbolLookup x11 = SymbolLookup.libraryLookup("libX11.so.6", arena);
MethodHandle openDisplay = linker.downcallHandle(x11.find("XOpenDisplay").orElseThrow(),
FunctionDescriptor.of(ADDRESS, ADDRESS));
MethodHandle defaultRootWindow = linker.downcallHandle(x11.find("XDefaultRootWindow").orElseThrow(),
FunctionDescriptor.of(JAVA_LONG, ADDRESS));
MethodHandle queryPointer = linker.downcallHandle(x11.find("XQueryPointer").orElseThrow(),
FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_LONG,
ADDRESS, ADDRESS, ADDRESS, ADDRESS, ADDRESS, ADDRESS, ADDRESS));
MethodHandle closeDisplay = linker.downcallHandle(x11.find("XCloseDisplay").orElseThrow(),
FunctionDescriptor.of(JAVA_INT, ADDRESS));
MemorySegment display = (MemorySegment) openDisplay.invoke(MemorySegment.NULL);
if (display.address() == 0) {
return null;
}
try {
long root = (long) defaultRootWindow.invoke(display);
MemorySegment rootReturn = arena.allocate(JAVA_LONG);
MemorySegment childReturn = arena.allocate(JAVA_LONG);
MemorySegment rootX = arena.allocate(JAVA_INT);
MemorySegment rootY = arena.allocate(JAVA_INT);
MemorySegment winX = arena.allocate(JAVA_INT);
MemorySegment winY = arena.allocate(JAVA_INT);
MemorySegment mask = arena.allocate(JAVA_INT);
int onSameScreen = (int) queryPointer.invoke(display, root,
rootReturn, childReturn, rootX, rootY, winX, winY, mask);
if (onSameScreen == 0) {
return null;
}
return new int[] { rootX.get(JAVA_INT, 0), rootY.get(JAVA_INT, 0) };
} finally {
closeDisplay.invoke(display);
}
} catch (Throwable ignored) {
// Not X11, libX11 missing, or the call failed — caller falls back to the primary screen.
return null;
}
}
}