Skip to content

Commit f9ed68b

Browse files
committed
clipmenud: Track partial merges per selection
store_clip maintains a single last_text and last_text_time across all selections. If clipboard and primary both receive text in quick succession, a clip arriving on one selection could be incorrectly identified as a partial of the most recent clip on a different selection and replace it rather than being stored as a new entry. This may not be a problem in practice, but it's certainly weird. Let's track the last text and timestamp independently per selection index so that partial-merge comparisons are only ever made within the same selection.
1 parent 9d64de8 commit f9ed68b

1 file changed

Lines changed: 21 additions & 15 deletions

File tree

src/clipmenud.c

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ struct clip_text {
5757
enum clip_text_source source;
5858
};
5959

60+
static struct clip_text last_text[CM_SEL_MAX] = {
61+
{NULL, CLIP_TEXT_SOURCE_MALLOC},
62+
{NULL, CLIP_TEXT_SOURCE_MALLOC},
63+
{NULL, CLIP_TEXT_SOURCE_MALLOC},
64+
};
65+
static time_t last_text_time[CM_SEL_MAX];
66+
6067
static void free_clip_text(struct clip_text *ct) {
6168
expect(ct->source != CLIP_TEXT_SOURCE_INVALID);
6269

@@ -338,31 +345,30 @@ static void maybe_trim(void) {
338345
}
339346

340347
/**
341-
* Store the clipboard text. If the text is a possible partial of the last clip
342-
* and it was received shortly afterwards, replace instead of adding.
348+
* Store the clipboard text for a selection. If the text is a possible partial
349+
* of the last clip on the same selection and it was received shortly
350+
* afterwards, replace instead of adding.
343351
*/
344-
static uint64_t store_clip(struct clip_text *ct) {
345-
static struct clip_text last_text = {NULL, CLIP_TEXT_SOURCE_MALLOC};
346-
static time_t last_text_time;
347-
352+
static uint64_t store_clip(enum selection_type sel, struct clip_text *ct) {
348353
dbg("Clipboard text is considered salient, storing\n");
349354
time_t current_time = time(NULL);
350355
uint64_t hash;
351356

352-
if (cfg.partial_merge_secs > 0 && last_text.data &&
353-
difftime(current_time, last_text_time) <= cfg.partial_merge_secs &&
354-
is_possible_partial(last_text.data, ct->data)) {
355-
dbg("Possible partial of last clip, replacing\n");
357+
if (cfg.partial_merge_secs > 0 && last_text[sel].data &&
358+
difftime(current_time, last_text_time[sel]) <= cfg.partial_merge_secs &&
359+
is_possible_partial(last_text[sel].data, ct->data)) {
360+
dbg("Possible partial of last clip on %s, replacing\n",
361+
cfg.selections[sel].name);
356362
expect(cs_replace(&cs, CS_ITER_NEWEST_FIRST, 0, ct->data, &hash) == 0);
357363
} else {
358364
expect(cs_add(&cs, ct->data, &hash,
359365
cfg.deduplicate ? CS_DUPE_KEEP_LAST : CS_DUPE_KEEP_ALL) ==
360366
0);
361367
}
362368

363-
free_clip_text(&last_text);
364-
last_text = *ct;
365-
last_text_time = current_time;
369+
free_clip_text(&last_text[sel]);
370+
last_text[sel] = *ct;
371+
last_text_time[sel] = current_time;
366372

367373
// The caller no longer owns this data.
368374
ct->data = NULL;
@@ -395,7 +401,7 @@ static void incr_receive_finish(struct incr_transfer *it) {
395401
it_dbg(it, "First line: %s\n", line);
396402

397403
if (is_salient_text(ct.data)) {
398-
uint64_t hash = store_clip(&ct);
404+
uint64_t hash = store_clip(sel, &ct);
399405
maybe_trim();
400406
if (cfg.own_clipboard && has_owned_selections()) {
401407
run_clipserve(hash, cfg.owned_selections);
@@ -532,7 +538,7 @@ static int handle_property_notify(const XPropertyEvent *pe) {
532538
dbg("First line: %s\n", line);
533539

534540
if (is_salient_text(ct.data)) {
535-
uint64_t hash = store_clip(&ct);
541+
uint64_t hash = store_clip(sel, &ct);
536542
maybe_trim();
537543
/* We only own CLIPBOARD because otherwise the behaviour is wonky:
538544
*

0 commit comments

Comments
 (0)