AppKit/X11: Robust multi-monitor mouse boundary confinement and clean stale Darling/Riot state

This commit is contained in:
Andras Laszlo committed 2026-10-16 16:55:22 +02:00
1 parent 6084371a67
commit 30383d6462
2 files changed
+94 -16

No files matched your search

+71 -16
View File
@@ -2305,7 +2305,7 @@ index 18189404..5b3a34bc 100644
+- (NSRect) frameForRootRect: (NSRect) rect;
@end
diff --git a/AppKit/X11.backend/X11Display.m b/AppKit/X11.backend/X11Display.m
index 5ae29bf4..5105e7d8 100644
index 5ae29bf4..5403cfa3 100644
--- a/AppKit/X11.backend/X11Display.m
+++ b/AppKit/X11.backend/X11Display.m
@@ -48,12 +48,271 @@
@@ -3355,7 +3355,38 @@ index 5ae29bf4..5105e7d8 100644
[self postEvent: event atStart: NO];
break;
@@ -1164,45 +1834,82 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
@@ -1160,49 +1830,113 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
// ev->xmotion.x, ev->xmotion.y, ev->xmotion.x_root,
// ev->xmotion.y_root);
+ /* darling-lol: Coalesce pending MotionNotify events in the X11 queue to eliminate input lag. */
+ XEvent nextEv;
+ while (XCheckTypedWindowEvent(_display, ev->xmotion.window, MotionNotify, &nextEv)) {
+ *ev = nextEv;
+ }
+
+ if (_confinedWindow == [window windowHandle]) {
+ XWindowAttributes attrs;
+ if (XGetWindowAttributes(_display, _confinedWindow, &attrs)) {
+ int clampedX = ev->xmotion.x;
+ int clampedY = ev->xmotion.y;
+ BOOL needWarp = NO;
+ if (clampedX < 0) { clampedX = 0; needWarp = YES; }
+ if (clampedX >= attrs.width) { clampedX = attrs.width - 1; needWarp = YES; }
+ if (clampedY < 0) { clampedY = 0; needWarp = YES; }
+ if (clampedY >= attrs.height) { clampedY = attrs.height - 1; needWarp = YES; }
+ if (needWarp) {
+ XWarpPointer(_display, None, _confinedWindow, 0, 0, 0, 0, clampedX, clampedY);
+ XFlush(_display);
+ ev->xmotion.x = clampedX;
+ ev->xmotion.y = clampedY;
+ }
+ }
+ } else if (!_relativeMouse.active && window != nil) {
+ [self updateMouseConfinementForWindow: window];
+ }
+
CGPoint lastMotionPos = [window mouseLocationOutsideOfEventStream];
pos = [window
transformPoint: NSMakePoint(ev->xmotion.x, ev->xmotion.y)];
@@ -3464,7 +3495,7 @@ index 5ae29bf4..5105e7d8 100644
mouseEventWithType: type
location: pos
modifierFlags:
@@ -1211,28 +1918,49 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
@@ -1211,28 +1945,61 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
clickCount: 1
deltaX: deltaX
deltaY: deltaY];
@@ -3499,6 +3530,18 @@ index 5ae29bf4..5105e7d8 100644
case LeaveNotify:
NSLog(@"LeaveNotify");
+ if (_confinedWindow == [window windowHandle]) {
+ Window win = [window windowHandle];
+ XWindowAttributes attrs;
+ if (XGetWindowAttributes(_display, win, &attrs)) {
+ int cx = ev->xcrossing.x < 0 ? 0 : (ev->xcrossing.x >= attrs.width ? attrs.width - 1 : ev->xcrossing.x);
+ int cy = ev->xcrossing.y < 0 ? 0 : (ev->xcrossing.y >= attrs.height ? attrs.height - 1 : ev->xcrossing.y);
+ XWarpPointer(_display, None, win, 0, 0, 0, 0, cx, cy);
+ XFlush(_display);
+ }
+ [self updateMouseConfinementForWindow: window];
+ break;
+ }
+ if (!_relativeMouse.active && ev->xcrossing.detail != NotifyInferior) {
+ [window setLastKnownCursorPosition:
+ [window transformPoint: NSMakePoint(ev->xcrossing.x, ev->xcrossing.y)]];
@@ -3516,7 +3559,7 @@ index 5ae29bf4..5105e7d8 100644
NSLog(@"FocusIn");
if ([delegate attachedSheet]) {
[[delegate attachedSheet] makeKeyAndOrderFront: delegate];
@@ -1243,28 +1971,55 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
@@ -1243,28 +2010,67 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
checkForAppDeactivation: NO];
lastFocusedWindow = nil;
}
@@ -3544,6 +3587,18 @@ index 5ae29bf4..5105e7d8 100644
+ rclolFocusDetailName(ev->xfocus.detail), _relativeRequested, _relativeMouse.active,
+ (unsigned long) _confinedWindow,
+ [NSApp isActive]);
+ if (ev->xfocus.detail == NotifyPointer || ev->xfocus.mode == NotifyGrab || ev->xfocus.mode == NotifyUngrab) {
+ /* Pointer boundary crossing or grab transition in XWayland: do NOT release confinement or deactivate window */
+ break;
+ }
+ {
+ NSString *procName = [[NSProcessInfo processInfo] processName];
+ BOOL isGame = (procName && [procName rangeOfString:@"League" options:NSCaseInsensitiveSearch].location != NSNotFound);
+ if (isGame && [NSApp isActive]) {
+ /* While League is the active app, do not drop confinement on transient XWayland focus events */
+ break;
+ }
+ }
NSLog(@"FocusOut");
+ [self reconcileModifierKeys:NULL window:delegate];
+ _lastKeySym = 0;
@@ -3574,7 +3629,7 @@ index 5ae29bf4..5105e7d8 100644
O2Rect rect = NSMakeRect(ev->xexpose.x, ev->xexpose.y,
ev->xexpose.width, ev->xexpose.height);
@@ -1301,11 +2056,23 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
@@ -1301,11 +2107,23 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
break;
case UnmapNotify:
@@ -3602,7 +3657,7 @@ index 5ae29bf4..5105e7d8 100644
break;
case MapRequest:
@@ -1321,6 +2088,7 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
@@ -1321,6 +2139,7 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
[delegate platformWindow: window
frameChanged: [window frame]
didSize: YES];
@@ -3610,7 +3665,7 @@ index 5ae29bf4..5105e7d8 100644
break;
case ConfigureRequest:
@@ -1344,6 +2112,8 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
@@ -1344,6 +2163,8 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
break;
case PropertyNotify:
@@ -3619,7 +3674,7 @@ index 5ae29bf4..5105e7d8 100644
if ([window respondsToSelector: @selector(propertyNotify:)]) {
[window propertyNotify: &ev->xproperty];
}
@@ -1399,7 +2169,9 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
@@ -1399,7 +2220,9 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
break;
default:
@@ -3630,7 +3685,7 @@ index 5ae29bf4..5105e7d8 100644
[self _invalidateRRCache];
break;
}
@@ -1433,6 +2205,10 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
@@ -1433,6 +2256,10 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) {
NSLog(@"Request code: %d:%d", errorEvent->request_code,
errorEvent->minor_code);
NSLog(@"Error code: %d", errorEvent->error_code);
@@ -3641,7 +3696,7 @@ index 5ae29bf4..5105e7d8 100644
return 0;
}
@@ -1476,54 +2252,271 @@ void CGNativeBorderFrameWidthsForStyle(NSUInteger styleMask, CGFloat *top,
@@ -1476,54 +2303,271 @@ void CGNativeBorderFrameWidthsForStyle(NSUInteger styleMask, CGFloat *top,
return frame;
}
@@ -3687,8 +3742,8 @@ index 5ae29bf4..5105e7d8 100644
- position.x, position.y);
- XSync(_display, False);
+ [self warpPointerToRootX: x y: y];
+}
+
}
+/* Confinement preserves absolute motion and edge panning. Relative mode holds
+ * its own grab and is left alone while requested. */
+- (void) updateMouseConfinementForWindow:(X11Window *)window {
@@ -3803,7 +3858,7 @@ index 5ae29bf4..5105e7d8 100644
+ }
+ if (getenv("RCLOL_INPUT_TRACE")) fprintf(stderr,"CONFINEMENT focus=%lu target=%lu ancestor=%lu fullscreen=%d\n",focus,win,ancestor,fullscreen);
+
+ BOOL isFocused = (ancestor == win || focus == win || (isGame && (focus == PointerRoot || focus == None || focus == DefaultRootWindow(_display))));
+ BOOL isFocused = (ancestor == win || focus == win || (isGame && ([NSApp isActive] || focus == PointerRoot || focus == None || focus == DefaultRootWindow(_display))));
+ if ((!fullscreen && !isGame) || attrs.map_state != IsViewable || !isFocused) {
+ if (rclolTraceOn())
+ rclolTraceRated(&rclolConfineChannel, "no confinement window=%lu fullscreen=%d ewmh=%d titled=%d size=%dx%d map_state=%d %s",
@@ -3826,9 +3881,9 @@ index 5ae29bf4..5105e7d8 100644
+ rclolTraceRated(&rclolConfineChannel, "confine window=%lu fullscreen=%d ewmh=%d size=%dx%d XGrabPointer=%d(%s)",
+ (unsigned long) win, fullscreen, rclolEwmhFullscreen, attrs.width, attrs.height, status,
+ rclolGrabResultName(status));
+ if (status == GrabSuccess) _confinedWindow=win;
}
+ if (status == GrabSuccess || status == AlreadyGrabbed) _confinedWindow=win;
+}
+
+/* Takes the relative-mode pointer grab on the key (else main) window, starting with the virtual cursor at the
+ * current pointer position. Returns the XGrabPointer result, or -1 without a window. */
+- (int) beginRelativeMouseWithWindow: (NSWindow **) outWindow pick: (const char **) outPick {
+23
View File
@@ -22,3 +22,26 @@ except (OSError, StopIteration):
if dead:
os.replace(marker, prefix / f'.init.pid.stale-{pid}-{time.time_ns()}')
print(f'Recovered stale Darling server marker for PID {pid}', flush=True)
# Clean up stale Riot temporary files and shared memory frames from previous crashes
try:
tmp_dir = prefix / 'private/tmp'
if tmp_dir.is_dir():
for f in tmp_dir.glob('.com.riotgames.*'):
try:
f.unlink(missing_ok=True)
except OSError:
pass
except OSError:
pass
try:
shm_dir = Path('/dev/shm')
if shm_dir.is_dir():
for f in shm_dir.glob('darling-*'):
try:
f.unlink(missing_ok=True)
except OSError:
pass
except OSError:
pass