X11 clipboard synchronization with blacklisted apps

If you are X11 user, you have two main selection types 1 (often called clipboards) to your disposal:

I’m sure that this is useful for some, but if you are anything like me, single clipboard would be sufficient and easier to use. Thankfully, this is easily achievable with autocutsel 2 which I start in my .xinitrc:

1
2
autocutsel -fork &
autocutsel -selection PRIMARY -fork &

With this daemons running, there is both way synchronization between clipboards. Anything selected with mouse can be pasted with ctrl+v, anything copied with ctrl+c can be pasted with middle mouse button.

That’s great but there is one catch! Some applications may automatically select text. In such case, you may unwillingly loose your last copied data. For me, this major annoyance was caused by KiCad, which automatically pre-selects footprint’s reference designator in its properties window. Anytime I wanted to paste new property, my selection was overridden.

This issue can be solved by applying this patch to autocutsel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
--- autocutsel.c
+++ autocutsel.c
@@ -208,6 +208,28 @@
   XFree(value);
 }

+static int OwnerIgnored(Widget w, Atom *selection)
+{
+  int status = 0;
+  Window selection_owner = XGetSelectionOwner(XtDisplay(w), *selection);
+  // Print the selection owner window ID
+  if (selection_owner != None) {
+      char* name = NULL;
+      XFetchName(dpy, selection_owner, &name);
+      if (options.debug) {
+          printf("The current selection owner is: %s\n", name);
+      }
+      if (name && strcmp(name, "kicad") == 0) {
+          if (options.debug) {
+              printf("Selection ignored\n");
+          }
+          status = 1;
+      }
+      free((void*)name);
+  }
+  return status;
+}
+
 // Called when the requested selection value is availiable
 static void SelectionReceived(Widget w, XtPointer client_data, Atom *selection,
                               Atom *type, XtPointer value,
@@ -217,6 +239,11 @@

   if (*type != 0 && *type != XT_CONVERT_FAIL) {
     if (length > 0 && ValueDiffers(value, length)) {
+      if (strcmp(options.selection_name, "PRIMARY") == 0 &&
+          OwnerIgnored(w, selection)) {
+        XtFree(value);
+        return;
+      }
       if (options.debug) {
         printf("Selection changed: ");
         PrintValue((char*)value, length);

The way it works is simple. Each new PRIMARY selection is filtered by its original owner name before it is further processed. When owner happens to be equal kicad it is just ignored.