suckless/clipmenu/src/x.c

121 lines
4.2 KiB
C

#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <stddef.h>
#include "x.h"
/**
* Fetch the title of the window with the specified window ID.
*/
char *get_window_title(Display *dpy, Window owner) {
Atom props[] = {XInternAtom(dpy, "_NET_WM_NAME", False), XA_WM_NAME};
Atom utf8_string = XInternAtom(dpy, "UTF8_STRING", False);
Atom actual_type;
int format;
unsigned long nr_items, bytes_after;
unsigned char *prop = NULL;
for (size_t i = 0; i < arrlen(props); i++) {
if (XGetWindowProperty(dpy, owner, props[i], 0, (~0L), False,
(props[i] == XA_WM_NAME) ? AnyPropertyType
: utf8_string,
&actual_type, &format, &nr_items, &bytes_after,
&prop) == Success &&
prop) {
return (char *)prop;
}
}
return NULL;
}
/**
* Certain X11 operations may fail in expected ways. For example, when
* attempting to interact with a window that has been closed. This handler
* avoids the application terminating in such cases.
*
* The cppcheck suppression is for a false positive: this is a callback and
* cannot be changed.
*/
// cppcheck-suppress [constParameterPointer,unmatchedSuppression]
int xerror_handler(Display *dpy _unused_, XErrorEvent *ee) {
if (ee->error_code == BadWindow ||
(ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) ||
(ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) ||
(ee->request_code == X_PolyFillRectangle &&
ee->error_code == BadDrawable) ||
(ee->request_code == X_PolySegment && ee->error_code == BadDrawable) ||
(ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) ||
(ee->request_code == X_GrabButton && ee->error_code == BadAccess) ||
(ee->request_code == X_GrabKey && ee->error_code == BadAccess) ||
(ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) {
return 0;
}
die("X error with request code=%d, error code=%d\n", ee->request_code,
ee->error_code);
}
#define FALLBACK_CHUNK_BYTES 4 * 1024
/**
* Return a quarter of the maximum request size in bytes.
*
* XMaxRequestSize and XExtendedMaxRequestSize return units of 4-byte words, so
* the word count already equals 1/4 of the maximum request size in bytes.
*/
size_t get_incr_threshold(Display *dpy) {
size_t max_request_words = XExtendedMaxRequestSize(dpy);
if (max_request_words == 0) {
max_request_words = XMaxRequestSize(dpy);
}
return max_request_words ? max_request_words : FALLBACK_CHUNK_BYTES;
}
/**
* Calculate an appropriate INCR payload size.
*
* The request size word count is a sensible threshold for deciding when to use
* INCR at all, but using that same value as the payload of each
* XChangeProperty request is too aggressive in practice.
*
* This is an interoperability constraint, not a protocol-limit one, because:
*
* - XMaxRequestSize/XExtendedMaxRequestSize only tell us what request size the
* X server will accept.
* - INCR has no chunk-size negotiation, so if a requestor dislikes large
* payloads it can simply stop deleting the property, leaving the sender
* waiting forever.
*
* Other implementations do not agree on a single rule here either: xclip uses
* a heuristic, some clipboard managers use a fixed cap, and Emacs couples
* large chunks with a timeout.
*/
size_t get_chunk_size(Display *dpy) {
size_t incr_threshold = get_incr_threshold(dpy);
return incr_threshold / 4 ? incr_threshold / 4 : FALLBACK_CHUNK_BYTES;
}
/**
* Add a new INCR transfer to the active list.
*/
void it_add(struct incr_transfer **it_list, struct incr_transfer *it) {
if (*it_list) {
(*it_list)->prev = it;
}
it->next = *it_list;
it->prev = NULL;
*it_list = it;
}
/**
* Remove an INCR transfer from the active list.
*/
void it_remove(struct incr_transfer **it_list, struct incr_transfer *it) {
if (it->prev) {
it->prev->next = it->next;
}
if (it->next) {
it->next->prev = it->prev;
}
if (*it_list == it) {
*it_list = it->next;
}
}