Skip to content

Commit

Permalink
x: Dynamically configure INCR chunk size
Browse files Browse the repository at this point in the history
  • Loading branch information
cdown committed Nov 9, 2024
1 parent 78ed2b5 commit 9c41fa8
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 10 deletions.
19 changes: 10 additions & 9 deletions src/clipserve.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
#include "util.h"
#include "x.h"

#define INCR_CHUNK_BYTES 4096

static struct incr_transfer *it_list = NULL;
static Display *dpy;
static Atom incr_atom;

static size_t chunk_size;

/**
* Start an INCR transfer.
*/
Expand All @@ -37,7 +37,6 @@ static void incr_send_start(XSelectionRequestEvent *req,
.data = (char *)content->data,
.data_size = content->size,
.offset = 0,
.chunk_size = INCR_CHUNK_BYTES,
};

it_dbg(it, "Starting transfer\n");
Expand Down Expand Up @@ -70,19 +69,19 @@ static void incr_send_chunk(const XPropertyEvent *pe) {
while (it) {
if (it->requestor == pe->window && it->property == pe->atom) {
size_t remaining = it->data_size - it->offset;
size_t chunk_size =
(remaining > it->chunk_size) ? it->chunk_size : remaining;
size_t this_chunk_size =
(remaining > chunk_size) ? chunk_size : remaining;

it_dbg(it,
"Sending chunk (bytes sent: %zu, bytes remaining: %zu)\n",
it->offset, remaining);

if (chunk_size > 0) {
if (this_chunk_size > 0) {
XChangeProperty(dpy, it->requestor, it->property, it->target,
it->format, PropModeReplace,
(unsigned char *)(it->data + it->offset),
chunk_size);
it->offset += chunk_size;
this_chunk_size);
it->offset += this_chunk_size;
} else {
incr_send_finish(it);
}
Expand All @@ -107,6 +106,8 @@ static void _nonnull_ serve_clipboard(uint64_t hash,
dpy = XOpenDisplay(NULL);
expect(dpy);

chunk_size = get_chunk_size(dpy);

win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 1, 1, 0, 0, 0);
XStoreName(dpy, win, "clipserve");
targets = XInternAtom(dpy, "TARGETS", False);
Expand Down Expand Up @@ -147,7 +148,7 @@ static void _nonnull_ serve_clipboard(uint64_t hash,
arrlen(available_targets));
} else if (req->target == utf8_string ||
req->target == XA_STRING) {
if (content->size <= INCR_CHUNK_BYTES) {
if (content->size < (off_t)chunk_size) {
// Data size is small enough, send directly
XChangeProperty(dpy, req->requestor, req->property,
req->target, 8, PropModeReplace,
Expand Down
18 changes: 18 additions & 0 deletions src/x.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ int xerror_handler(Display *dpy _unused_, XErrorEvent *ee) {
ee->error_code);
}

#define FALLBACK_CHUNK_BYTES 4 * 1024

/**
* Calculate and cache an appropriate INCR chunk size.
*
* We consider selections larger than a quarter of the maximum request size to
* be "large". That's what others (like xclip) do, so it's clearly ok in
* practice.
*/
size_t get_chunk_size(Display *dpy) {
// Units are 4-byte words, so this is 1/4 in bytes
size_t chunk_size = XExtendedMaxRequestSize(dpy);
if (chunk_size == 0) {
chunk_size = XMaxRequestSize(dpy);
}
return chunk_size ? chunk_size / 4 : FALLBACK_CHUNK_BYTES;
}

/**
* Add a new INCR transfer to the active list.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/x.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

DEFINE_DROP_FUNC_VOID(XFree)

size_t _nonnull_ get_chunk_size(Display *dpy);
char _nonnull_ *get_window_title(Display *dpy, Window owner);
int xerror_handler(Display *dpy _unused_, XErrorEvent *ee);

Expand All @@ -21,7 +22,6 @@ struct incr_transfer {
size_t data_size;
size_t data_capacity;
size_t offset;
size_t chunk_size;
};

#define it_dbg(it, fmt, ...) \
Expand Down

0 comments on commit 9c41fa8

Please sign in to comment.