aboutsummaryrefslogtreecommitdiff
path: root/gsr-window-name/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'gsr-window-name/main.c')
-rw-r--r--gsr-window-name/main.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/gsr-window-name/main.c b/gsr-window-name/main.c
new file mode 100644
index 0000000..a46aff2
--- /dev/null
+++ b/gsr-window-name/main.c
@@ -0,0 +1,190 @@
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef enum {
+ CAPTURE_TYPE_FOCUSED,
+ CAPTURE_TYPE_CURSOR
+} capture_type;
+
+static bool window_has_atom(Display *dpy, Window window, Atom atom) {
+ Atom type;
+ unsigned long len, bytes_left;
+ int format;
+ unsigned char *properties = NULL;
+ if(XGetWindowProperty(dpy, window, atom, 0, 1024, False, AnyPropertyType, &type, &format, &len, &bytes_left, &properties) < Success)
+ return false;
+
+ if(properties)
+ XFree(properties);
+
+ return type != None;
+}
+
+static bool window_is_user_program(Display *dpy, Window window) {
+ const Atom net_wm_state_atom = XInternAtom(dpy, "_NET_WM_STATE", False);
+ const Atom wm_state_atom = XInternAtom(dpy, "WM_STATE", False);
+ return window_has_atom(dpy, window, net_wm_state_atom) || window_has_atom(dpy, window, wm_state_atom);
+}
+
+static Window get_window_at_cursor_position(Display *dpy) {
+ Window root_window = None;
+ Window window = None;
+ int dummy_i;
+ unsigned int dummy_u;
+ int cursor_pos_x = 0;
+ int cursor_pos_y = 0;
+ XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, &window, &dummy_i, &dummy_i, &cursor_pos_x, &cursor_pos_y, &dummy_u);
+ return window;
+}
+
+static Window get_focused_window(Display *dpy, capture_type cap_type) {
+ const Atom net_active_window_atom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+ Window focused_window = None;
+
+ if(cap_type == CAPTURE_TYPE_FOCUSED) {
+ Atom type = None;
+ int format = 0;
+ unsigned long num_items = 0;
+ unsigned long bytes_left = 0;
+ unsigned char *data = NULL;
+ XGetWindowProperty(dpy, DefaultRootWindow(dpy), net_active_window_atom, 0, 1, False, XA_WINDOW, &type, &format, &num_items, &bytes_left, &data);
+
+ if(type == XA_WINDOW && num_items == 1 && data)
+ return *(Window*)data;
+
+ int revert_to = 0;
+ XGetInputFocus(dpy, &focused_window, &revert_to);
+ if(focused_window && focused_window != DefaultRootWindow(dpy) && window_is_user_program(dpy, focused_window))
+ return focused_window;
+ }
+
+ focused_window = get_window_at_cursor_position(dpy);
+ if(focused_window && focused_window != DefaultRootWindow(dpy) && window_is_user_program(dpy, focused_window))
+ return focused_window;
+
+ return None;
+}
+
+static char* get_window_title(Display *dpy, Window window) {
+ const Atom net_wm_name_atom = XInternAtom(dpy, "_NET_WM_NAME", False);
+ const Atom wm_name_atom = XInternAtom(dpy, "_NET_WM_NAME", False);
+ const Atom utf8_string_atom = XInternAtom(dpy, "UTF8_STRING", False);
+
+ Atom type = None;
+ int format = 0;
+ unsigned long num_items = 0;
+ unsigned long bytes_left = 0;
+ unsigned char *data = NULL;
+ XGetWindowProperty(dpy, window, net_wm_name_atom, 0, 1024, False, utf8_string_atom, &type, &format, &num_items, &bytes_left, &data);
+
+ if(type == utf8_string_atom && format == 8 && data)
+ return (char*)data;
+
+ type = None;
+ format = 0;
+ num_items = 0;
+ bytes_left = 0;
+ data = NULL;
+ XGetWindowProperty(dpy, window, wm_name_atom, 0, 1024, False, 0, &type, &format, &num_items, &bytes_left, &data);
+
+ if((type == XA_STRING || type == utf8_string_atom) && data)
+ return (char*)data;
+
+ return NULL;
+}
+
+static bool is_steam_game(Display *dpy, Window window) {
+ return window_has_atom(dpy, window, XInternAtom(dpy, "STEAM_GAME", False));
+}
+
+static const char* strip(const char *str, int *len) {
+ int str_len = strlen(str);
+ for(int i = 0; i < str_len; ++i) {
+ if(str[i] != ' ') {
+ str += i;
+ str_len -= i;
+ break;
+ }
+ }
+
+ for(int i = str_len - 1; i >= 0; --i) {
+ if(str[i] != ' ') {
+ str_len = i + 1;
+ break;
+ }
+ }
+
+ *len = str_len;
+ return str;
+}
+
+static void print_str_strip(const char *str) {
+ int len = 0;
+ str = strip(str, &len);
+ printf("%.*s", len, str);
+}
+
+static int x11_ignore_error(Display *dpy, XErrorEvent *error_event) {
+ (void)dpy;
+ (void)error_event;
+ return 0;
+}
+
+static void usage(void) {
+ fprintf(stderr, "usage: gsr-window-name <focused|cursor>\n");
+ fprintf(stderr, "options:\n");
+ fprintf(stderr, " focused The class/name of the focused window is returned. If no window is focused then the window beneath the cursor is returned instead\n");
+ fprintf(stderr, " cursor The class/name of the window beneath the cursor is returned\n");
+ exit(1);
+}
+
+int main(int argc, char **argv) {
+ if(argc != 2)
+ usage();
+
+ const char *cap_type_str = argv[1];
+ capture_type cap_type = CAPTURE_TYPE_FOCUSED;
+ if(strcmp(cap_type_str, "focused") == 0) {
+ cap_type = CAPTURE_TYPE_FOCUSED;
+ } else if(strcmp(cap_type_str, "cursor") == 0) {
+ cap_type = CAPTURE_TYPE_CURSOR;
+ } else {
+ fprintf(stderr, "error: invalid option '%s', expected either 'focused' or 'cursor'\n", cap_type_str);
+ usage();
+ }
+
+ Display *dpy = XOpenDisplay(NULL);
+ if(!dpy) {
+ fprintf(stderr, "Error: failed to connect to the X11 server\n");
+ exit(1);
+ }
+
+ XSetErrorHandler(x11_ignore_error);
+
+ const Window focused_window = get_focused_window(dpy, cap_type);
+ if(focused_window == None)
+ exit(2);
+
+ if(!is_steam_game(dpy, focused_window)) {
+ XClassHint class_hint = {0};
+ XGetClassHint(dpy, focused_window, &class_hint);
+ if(class_hint.res_class) {
+ print_str_strip(class_hint.res_class);
+ exit(0);
+ }
+ }
+
+ char *window_title = get_window_title(dpy, focused_window);
+ if(window_title) {
+ print_str_strip(window_title);
+ exit(0);
+ }
+
+ return 2;
+}