Files

107 lines
4.5 KiB
C

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
#include "commands_native.h"
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <string.h>
static int steam_active_window(Display *display, Window *window) {
Atom type; int format;
unsigned long count, remaining;
unsigned char *data = NULL;
int known = XGetWindowProperty(display, DefaultRootWindow(display), XInternAtom(display, "_NET_ACTIVE_WINDOW", False),
0, 1, False, XA_WINDOW, &type, &format, &count, &remaining, &data) == Success &&
data != NULL && type == XA_WINDOW && format == 32 && count == 1;
if (known) *window = ((Window *)data)[0];
if (data != NULL) XFree(data);
return known;
}
static int steam_window_pid(Display *display, Window window) {
Atom type; int format;
unsigned long count, remaining;
unsigned char *data = NULL;
int pid = 0;
if (XGetWindowProperty(display, window, XInternAtom(display, "_NET_WM_PID", False),
0, 1, False, XA_CARDINAL, &type, &format, &count,
&remaining, &data) == Success && data != NULL &&
type == XA_CARDINAL && format == 32 && count == 1) {
pid = (int)((unsigned long *)data)[0];
}
if (data != NULL) XFree(data);
return pid;
}
static int steam_wrapper(Display *display, Window window) {
XClassHint hint = {0};
int matches = XGetClassHint(display, window, &hint) &&
hint.res_name != NULL && hint.res_class != NULL &&
strcmp(hint.res_name, "steamwebhelper") == 0 && strcmp(hint.res_class, "steam") == 0;
if (hint.res_name != NULL) XFree(hint.res_name);
if (hint.res_class != NULL) XFree(hint.res_class);
return matches;
}
static int steam_browser_child(Display *display, Window wrapper, int pid, Window child) {
XWindowAttributes attributes;
if (child == None || child == PointerRoot || child == wrapper ||
!XGetWindowAttributes(display, child, &attributes) || attributes.map_state != IsViewable ||
attributes.class != InputOutput || steam_window_pid(display, child) != pid) return 0;
for (int depth = 0; depth < 32; depth++) {
Window root, parent, *children = NULL;
unsigned int count;
if (!XQueryTree(display, child, &root, &parent, &children, &count)) return 0;
if (children != NULL) XFree(children);
if (parent == wrapper) return 1;
if (parent == None || parent == root) return 0;
child = parent;
}
return 0;
}
CommandFocusSample cthulhu_read_command_focus(Display *display, int steam_enabled) {
CommandFocusSample result = {0};
SteamFocusSample *sample = &result.steam;
cthulhu_x11_begin_read(display);
result.xterm = cthulhu_focus_metadata_unchecked(display, steam_enabled ? sample : NULL);
if (sample->steam) {
sample->pid = steam_window_pid(display, sample->window);
sample->steam = sample->pid > 0;
int revert;
XGetInputFocus(display, &sample->focus, &revert);
if (sample->steam) sample->innerFocus = steam_browser_child(display, sample->window, sample->pid, sample->focus);
}
if (cthulhu_x11_end_read(display)) result = (CommandFocusSample){0};
return result;
}
int cthulhu_restore_steam_focus(Display *display, Window wrapper, int pid, Window target) {
cthulhu_x11_begin_read(display);
int restored = 0, revert;
Window focus, active = None;
XGetInputFocus(display, &focus, &revert);
char heldKeys[32];
XQueryKeymap(display, heldKeys);
int keyHeld = 0;
for (int i = 0; i < 32; i++) keyHeld |= heldKeys[i];
if (!keyHeld && focus == wrapper && steam_active_window(display, &active) && active == wrapper &&
steam_wrapper(display, wrapper) &&
steam_window_pid(display, wrapper) == pid && steam_browser_child(display, wrapper, pid, target)) {
// Recheck foreground immediately before changing input focus: cached
// children must never pull keyboard input out of a different client.
if (steam_active_window(display, &active) && active == wrapper) {
XGetInputFocus(display, &focus, &revert);
if (focus == wrapper) {
XSetInputFocus(display, target, RevertToParent, CurrentTime);
XSync(display, False);
XGetInputFocus(display, &focus, &revert);
restored = focus == target;
}
}
}
if (cthulhu_x11_end_read(display)) restored = 0;
return restored;
}