From d981cbe8c3bf909da289bdad2860475db4b9e8fc Mon Sep 17 00:00:00 2001 From: Andras Laszlo Date: Fri, 16 Oct 2026 17:40:51 +0200 Subject: [PATCH] Fix in-game keyboard input, initial fullscreen black screen, and desktop launcher icon --- patches/cocotron.patch | 123 ++++++++++++++++++++++++------------ tools/riot-desktop-entry.py | 23 +++++-- 2 files changed, 99 insertions(+), 47 deletions(-) diff --git a/patches/cocotron.patch b/patches/cocotron.patch index 2afffdc..caa7a26 100644 --- a/patches/cocotron.patch +++ b/patches/cocotron.patch @@ -2307,7 +2307,7 @@ index 18189404..c683f96a 100644 +- (NSRect) frameForRootRect: (NSRect) rect; @end diff --git a/AppKit/X11.backend/X11Display.m b/AppKit/X11.backend/X11Display.m -index 5ae29bf4..c0361b56 100644 +index 5ae29bf4..516decf3 100644 --- a/AppKit/X11.backend/X11Display.m +++ b/AppKit/X11.backend/X11Display.m @@ -48,12 +48,271 @@ @@ -3232,7 +3232,20 @@ index 5ae29bf4..c0361b56 100644 NSEventModifierFlags modifierFlags = [self modifierFlagsForState: ev->xkey.state]; char buf[20] = {0}; KeySym keySym; -@@ -1038,6 +1674,18 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1030,7 +1666,11 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { + break; + + if (ev->type == KeyPress) { +- strLen = Xutf8LookupString(window->_xic, (XKeyPressedEvent *) ev, buf, sizeof(buf) - 1, &keySym, NULL); ++ if (window && window->_xic) { ++ strLen = Xutf8LookupString(window->_xic, (XKeyPressedEvent *) ev, buf, sizeof(buf) - 1, &keySym, NULL); ++ } else { ++ strLen = XLookupString((XKeyEvent*) ev, buf, sizeof(buf) - 1, &keySym, NULL); ++ } + buf[strLen] = 0; + } else { + // Xutf8LookupString() may not be used with KeyRelease +@@ -1038,6 +1678,18 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { buf[strLen] = 0; } @@ -3251,12 +3264,30 @@ index 5ae29bf4..c0361b56 100644 id str = [[NSString alloc] initWithCString: buf encoding: NSUTF8StringEncoding]; NSPoint pos = -@@ -1077,6 +1725,33 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1077,6 +1729,51 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { break; case ButtonPress:; -+ if (window && !_confinedWindow) { -+ [self updateMouseConfinementForWindow: window]; ++ if (window) { ++ Window win = [window windowHandle]; ++ XSetInputFocus(_display, win, RevertToPointerRoot, ev->xbutton.time); ++ if (window->_xic) ++ XSetICFocus(window->_xic); ++ Atom activeAtom = XInternAtom(_display, "_NET_ACTIVE_WINDOW", False); ++ XClientMessageEvent act = {0}; ++ act.type = ClientMessage; ++ act.window = win; ++ act.message_type = activeAtom; ++ act.format = 32; ++ act.data.l[0] = 1; ++ act.data.l[1] = ev->xbutton.time; ++ XSendEvent(_display, DefaultRootWindow(_display), False, ++ SubstructureNotifyMask | SubstructureRedirectMask, ++ (XEvent *)&act); ++ XFlush(_display); ++ if (!_confinedWindow) { ++ [self updateMouseConfinementForWindow: window]; ++ } + } + if (ev->xbutton.button >= Button4 && ev->xbutton.button <= 7) { + pos = [self locationInWindow: window x: ev->xbutton.x y: ev->xbutton.y @@ -3285,7 +3316,7 @@ index 5ae29bf4..c0361b56 100644 NSTimeInterval now = [[NSDate date] timeIntervalSinceReferenceDate]; if (now - lastClickTimeStamp < [self doubleClickInterval]) { -@@ -1086,8 +1761,8 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1086,8 +1783,8 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { } lastClickTimeStamp = now; @@ -3296,7 +3327,7 @@ index 5ae29bf4..c0361b56 100644 switch (ev->xbutton.button) { case Button1: -@@ -1096,10 +1771,6 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1096,10 +1793,6 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { case Button3: type = NSRightMouseDown; break; @@ -3307,7 +3338,7 @@ index 5ae29bf4..c0361b56 100644 default: type = NSOtherMouseDown; } -@@ -1113,14 +1784,22 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1113,14 +1806,22 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { clickCount: clickCount deltaX: 0.0 deltaY: 0.0]; @@ -3334,7 +3365,7 @@ index 5ae29bf4..c0361b56 100644 CGFloat deltaY = 0.0; switch (ev->xbutton.button) { -@@ -1130,14 +1809,6 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1130,14 +1831,6 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { case Button3: type = NSRightMouseUp; break; @@ -3349,7 +3380,7 @@ index 5ae29bf4..c0361b56 100644 default: type = NSOtherMouseUp; } -@@ -1151,7 +1822,9 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1151,7 +1844,9 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { clickCount: clickCount deltaX: 0.0 deltaY: deltaY]; @@ -3360,7 +3391,7 @@ index 5ae29bf4..c0361b56 100644 [self postEvent: event atStart: NO]; break; -@@ -1160,49 +1833,113 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1160,49 +1855,113 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { // ev->xmotion.x, ev->xmotion.y, ev->xmotion.x_root, // ev->xmotion.y_root); @@ -3500,7 +3531,7 @@ index 5ae29bf4..c0361b56 100644 mouseEventWithType: type location: pos modifierFlags: -@@ -1211,28 +1948,58 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1211,28 +1970,58 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { clickCount: 1 deltaX: deltaX deltaY: deltaY]; @@ -3561,7 +3592,7 @@ index 5ae29bf4..c0361b56 100644 NSLog(@"FocusIn"); if ([delegate attachedSheet]) { [[delegate attachedSheet] makeKeyAndOrderFront: delegate]; -@@ -1243,28 +2010,67 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1243,28 +2032,59 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { checkForAppDeactivation: NO]; lastFocusedWindow = nil; } @@ -3592,14 +3623,6 @@ index 5ae29bf4..c0361b56 100644 + 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]; @@ -3631,7 +3654,7 @@ index 5ae29bf4..c0361b56 100644 O2Rect rect = NSMakeRect(ev->xexpose.x, ev->xexpose.y, ev->xexpose.width, ev->xexpose.height); -@@ -1301,11 +2107,23 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1301,11 +2121,23 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { break; case UnmapNotify: @@ -3659,7 +3682,7 @@ index 5ae29bf4..c0361b56 100644 break; case MapRequest: -@@ -1317,10 +2135,15 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1317,10 +2149,15 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { break; case ConfigureNotify: @@ -3675,7 +3698,7 @@ index 5ae29bf4..c0361b56 100644 break; case ConfigureRequest: -@@ -1344,6 +2167,8 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1344,6 +2181,8 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { break; case PropertyNotify: @@ -3684,7 +3707,7 @@ index 5ae29bf4..c0361b56 100644 if ([window respondsToSelector: @selector(propertyNotify:)]) { [window propertyNotify: &ev->xproperty]; } -@@ -1399,7 +2224,9 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1399,7 +2238,9 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { break; default: @@ -3695,7 +3718,7 @@ index 5ae29bf4..c0361b56 100644 [self _invalidateRRCache]; break; } -@@ -1433,6 +2260,10 @@ static NSDictionary *modeInfoToDictionary(const XRRModeInfo *mi, int depth) { +@@ -1433,6 +2274,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); @@ -3706,7 +3729,7 @@ index 5ae29bf4..c0361b56 100644 return 0; } -@@ -1476,54 +2307,286 @@ void CGNativeBorderFrameWidthsForStyle(NSUInteger styleMask, CGFloat *top, +@@ -1476,54 +2321,286 @@ void CGNativeBorderFrameWidthsForStyle(NSUInteger styleMask, CGFloat *top, return frame; } @@ -3891,7 +3914,7 @@ index 5ae29bf4..c0361b56 100644 + return; + } + unsigned mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask; -+ int status=XGrabPointer(_display, win, False, mask, GrabModeAsync, GrabModeAsync, ++ int status=XGrabPointer(_display, win, True, mask, GrabModeAsync, GrabModeAsync, + win, None, CurrentTime); + if (getenv("RCLOL_INPUT_TRACE")) fprintf(stderr,"CONFINEMENT fullscreen=%d window=%lu result=%d\n",fullscreen,win,status); + if (rclolTraceOn()) @@ -4429,7 +4452,7 @@ index bfe3839a..dbc974a0 100644 - (void) frameChanged; - (void) setLastKnownCursorPosition: (CGPoint) point; diff --git a/AppKit/X11.backend/X11Window.m b/AppKit/X11.backend/X11Window.m -index f4925b66..04fa8146 100644 +index f4925b66..af6df163 100644 --- a/AppKit/X11.backend/X11Window.m +++ b/AppKit/X11.backend/X11Window.m @@ -38,6 +38,34 @@ @@ -4580,7 +4603,23 @@ index f4925b66..04fa8146 100644 } XSetWMSizeHints(_display, _window, sh, XA_WM_NORMAL_HINTS); -@@ -338,7 +423,6 @@ static NSData *makeWindowIcon() { +@@ -304,7 +389,14 @@ static NSData *makeWindowIcon() { + states[states_cnt++] = + (long) XInternAtom(_display, "_NET_WM_STATE_MODAL", False); + } +- if (mask & NSWindowStyleMaskFullScreen) { ++ NSString *procName = [[NSProcessInfo processInfo] processName]; ++ BOOL isGame = (procName && [procName rangeOfString:@"League" options:NSCaseInsensitiveSearch].location != NSNotFound && ++ [procName rangeOfString:@"Ux" options:NSCaseInsensitiveSearch].location == NSNotFound); ++ BOOL makeFullscreen = (mask & NSWindowStyleMaskFullScreen) != 0; ++ if (!makeFullscreen && isGame && (mask == NSBorderlessWindowMask || !(mask & NSTitledWindowMask))) { ++ makeFullscreen = YES; ++ } ++ if (makeFullscreen) { + states[states_cnt++] = (long) XInternAtom( + _display, "_NET_WM_STATE_FULLSCREEN", False); + } +@@ -338,7 +430,6 @@ static NSData *makeWindowIcon() { } + (void) removeDecorationForWindow: (Window) w onDisplay: (Display *) dpy { @@ -4588,7 +4627,7 @@ index f4925b66..04fa8146 100644 struct { unsigned long flags; unsigned long functions; -@@ -358,6 +442,7 @@ static NSData *makeWindowIcon() { +@@ -358,6 +449,7 @@ static NSData *makeWindowIcon() { if (!_mapped) { XMapWindow(_display, _window); _mapped = YES; @@ -4596,7 +4635,7 @@ index f4925b66..04fa8146 100644 } } -@@ -377,15 +462,27 @@ static NSData *makeWindowIcon() { +@@ -377,15 +469,27 @@ static NSData *makeWindowIcon() { - (void) syncDelegateProperties { long mask = KeyPressMask | KeyReleaseMask | ExposureMask | StructureNotifyMask | EnterWindowMask | LeaveWindowMask | @@ -4629,7 +4668,7 @@ index f4925b66..04fa8146 100644 // TODO: background color } -@@ -393,6 +490,28 @@ static NSData *makeWindowIcon() { +@@ -393,6 +497,28 @@ static NSData *makeWindowIcon() { // This is essentially dealloc; we release our contexts // and windows, but unlike dealloc, this method can be called // several times, so set everything to nil/NULL/0. @@ -4658,7 +4697,7 @@ index f4925b66..04fa8146 100644 [_context release]; _context = nil; -@@ -421,6 +540,388 @@ static NSData *makeWindowIcon() { +@@ -421,6 +547,388 @@ static NSData *makeWindowIcon() { } } @@ -5047,7 +5086,7 @@ index f4925b66..04fa8146 100644 - (Window) windowHandle { return _window; } -@@ -470,14 +971,31 @@ static NSData *makeWindowIcon() { +@@ -470,14 +978,31 @@ static NSData *makeWindowIcon() { const char *text = [title cString]; XStringListToTextProperty((char **) &text, 1, &prop); XSetWMName(_display, _window, &prop); @@ -5080,7 +5119,7 @@ index f4925b66..04fa8146 100644 } - (void) setHasShadow: (BOOL) value { -@@ -488,6 +1006,25 @@ static NSData *makeWindowIcon() { +@@ -488,6 +1013,25 @@ static NSData *makeWindowIcon() { _level = value; } @@ -5106,7 +5145,7 @@ index f4925b66..04fa8146 100644 - (void) setOpaque: (BOOL) value { _isOpaque = value; } -@@ -543,6 +1080,18 @@ static NSData *makeWindowIcon() { +@@ -543,6 +1087,18 @@ static NSData *makeWindowIcon() { - (void) makeKey { [self ensureMapped]; XRaiseWindow(_display, _window); @@ -5125,7 +5164,7 @@ index f4925b66..04fa8146 100644 } - (void) makeMain { -@@ -559,7 +1108,29 @@ static NSData *makeWindowIcon() { +@@ -559,7 +1115,29 @@ static NSData *makeWindowIcon() { XRaiseWindow(_display, _window); } @@ -5155,7 +5194,7 @@ index f4925b66..04fa8146 100644 Atom wmState = XInternAtom(_display, "_NET_WM_STATE", FALSE); Atom hiddenState = XInternAtom(_display, "_NET_WM_STATE_HIDDEN", FALSE); -@@ -615,6 +1186,12 @@ static NSData *makeWindowIcon() { +@@ -615,6 +1193,12 @@ static NSData *makeWindowIcon() { } - (void) openGLFlushBuffer { @@ -5168,7 +5207,7 @@ index f4925b66..04fa8146 100644 CGLError error; CGLContextObj prevContext = CGLGetCurrentContext(); -@@ -652,7 +1229,7 @@ static NSData *makeWindowIcon() { +@@ -652,7 +1236,7 @@ static NSData *makeWindowIcon() { } - (O2Rect) frame { @@ -5177,7 +5216,7 @@ index f4925b66..04fa8146 100644 } static int ignoreBadWindow(Display *display, XErrorEvent *errorEvent) { -@@ -666,6 +1243,7 @@ static int ignoreBadWindow(Display *display, XErrorEvent *errorEvent) { +@@ -666,6 +1250,7 @@ static int ignoreBadWindow(Display *display, XErrorEvent *errorEvent) { } - (void) frameChanged { @@ -5185,7 +5224,7 @@ index f4925b66..04fa8146 100644 XErrorHandler previousHandler = XSetErrorHandler(ignoreBadWindow); @try { Window root, parent; -@@ -710,14 +1288,6 @@ static int ignoreBadWindow(Display *display, XErrorEvent *errorEvent) { +@@ -710,14 +1295,6 @@ static int ignoreBadWindow(Display *display, XErrorEvent *errorEvent) { [_deviceDictionary addEntriesFromDictionary: entries]; } diff --git a/tools/riot-desktop-entry.py b/tools/riot-desktop-entry.py index 1458bfb..c4c1dd8 100755 --- a/tools/riot-desktop-entry.py +++ b/tools/riot-desktop-entry.py @@ -148,12 +148,13 @@ Name=League of Legends GenericName=MOBA Game Comment=Play League of Legends on Linux with darling-lol Exec={run_script} +Path={PROJECT_ROOT} Icon=league-of-legends Terminal=false Categories=Game;ActionGame;StrategyGame; Keywords=riot;league;lol;darling;games; -StartupWMClass=League of Legends -StartupNotify=true +StartupWMClass=Riot Client +StartupNotify=false """ desktop_file = apps_base / "league-of-legends.desktop" @@ -161,11 +162,23 @@ StartupNotify=true os.chmod(desktop_file, 0o755) print(f"Created application launcher: {desktop_file}") - if desktop_dir.is_dir(): - desktop_shortcut = desktop_dir / "league-of-legends.desktop" + target_desktop_dirs = [] + try: + res = subprocess.run(["xdg-user-dir", "DESKTOP"], capture_output=True, text=True, check=False) + if res.returncode == 0 and res.stdout.strip(): + d = Path(res.stdout.strip()) + if d.is_dir() and d not in target_desktop_dirs: + target_desktop_dirs.append(d) + except Exception: + pass + for cand in [HOST_HOME / "Desktop", HOST_HOME / "Asztal"]: + if cand.is_dir() and cand not in target_desktop_dirs: + target_desktop_dirs.append(cand) + + for ddir in target_desktop_dirs: + desktop_shortcut = ddir / "league-of-legends.desktop" desktop_shortcut.write_text(desktop_content) os.chmod(desktop_shortcut, 0o755) - # Mark as trusted on GNOME Desktop if gio is available gio = shutil.which("gio") if gio: subprocess.run([gio, "set", str(desktop_shortcut), "metadata::trusted", "true"], check=False)