Synopsis:       cwm: X selections cannot be pasted into menus
Category:       user
Environment:
        System      : OpenBSD 7.5
        Details     : OpenBSD 7.5 (GENERIC.MP) #82: Wed Mar 20 15:48:40 MDT 2024
                         
dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP

   Architecture: OpenBSD.amd64
   Machine     : amd64
Description:
Attempting to paste an X selection into any of the cwm menus results in display of a character placeholder, rather than the selection text. This bug is found in both 7.5 and -current.
How-To-Repeat:
Within cwm, add text to an X selection buffer. Recall a menu using a key or mouse binding, and attempt to paste the selection.
Fix:
Add to menu.c:menu_handle_key() a CTL_PASTE keysym for the XK_V and XK_v control characters (and maybe others), and add a handler to the menu that gets the selection.

Below is a first attempt at this approach, based on a quick look at sselp (https://tools.suckless.org/x/sselp/) and xclip (https://github.com/astrand/xclip).


Index: menu.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/menu.c,v
retrieving revision 1.110
diff -u -p -u -p -r1.110 menu.c
--- menu.c      15 Oct 2022 16:06:07 -0000      1.110
+++ menu.c      1 Jun 2024 06:04:18 -0000
@@ -44,7 +44,7 @@
enum ctltype {
        CTL_NONE = -1,
        CTL_ERASEONE = 0, CTL_WIPE, CTL_UP, CTL_DOWN, CTL_RETURN,
-       CTL_TAB, CTL_ABORT, CTL_ALL
+       CTL_TAB, CTL_ABORT, CTL_ALL, CTL_PASTE
};

struct menu_ctx {
@@ -78,6 +78,62 @@ static void           menu_draw_entry(struct me
static int               menu_calc_entry(struct menu_ctx *, int, int);
static struct menu      *menu_complete_path(struct menu_ctx *);
static int               menu_keycode(XKeyEvent *, enum ctltype *, char *);
+void                    getsel(struct menu_ctx *, char[]);
+static size_t           mach_itemsize(int);
+
+static size_t
+mach_itemsize(int format)
+{
+       if (format == 8)
+               return sizeof(char);
+       if (format == 16)
+               return sizeof(short);
+       if (format == 32)
+               return sizeof(long);
+       return 0;
+}
+
+void
+getsel(struct menu_ctx *mc, char result[])
+{
+       XEvent ev;
+       Window w;
+       Atom typeret;
+       static Atom utf8_string;
+       static Atom xa_clip_string;
+       unsigned long items, len, remain;
+       int format, rc;
+       unsigned char *data = NULL;
+
+       w = mc->win;
+
+       if (!utf8_string)
+               utf8_string = XInternAtom(X_Dpy, "UTF8_STRING", False);
+       if (!xa_clip_string)
+               xa_clip_string = XInternAtom(X_Dpy, "_SELOUT_STRING", False);
+
+       XConvertSelection(X_Dpy, XA_PRIMARY, utf8_string, xa_clip_string, w,
+           CurrentTime);
+
+       do {
+               XNextEvent(X_Dpy, &ev);
+               if (ev.type == SelectionNotify) {
+                       rc = XGetWindowProperty(X_Dpy, w,
+                           ev.xselection.property, 0, 4096L, False,
+                           AnyPropertyType, &typeret, &format, &items,
+                           &remain, &data);
+                       XDeleteProperty(X_Dpy, w, ev.xselection.property);
+                       if (rc == Success) {
+                               len = MIN(items * mach_itemsize(format),
+                                   sizeof(mc->searchstr));
+                               memcpy(result, data, len);
+                               XFree(data);
+                               result[len] = '\0';
+                       }
+                       break;
+               }
+       } while (ev.xselection.property != None);
+}

struct menu *
menu_filter(struct screen_ctx *sc, struct menu_q *menuq, const char *prompt,
@@ -221,11 +277,12 @@ menu_handle_key(XEvent *e, struct menu_c
    struct menu_q *resultq)
{
        struct menu     *mi;
+       size_t           len;
        enum ctltype     ctl;
+       int              clen, i;
+ char seltext[sizeof(mc->searchstr)]; + wchar_t wc;
        char             chr[32];
-       size_t           len;
-       int              clen, i;
-       wchar_t          wc;

        if (menu_keycode(&e->xkey, &ctl, chr) < 0)
                return NULL;
@@ -304,6 +361,11 @@ menu_handle_key(XEvent *e, struct menu_c
        case CTL_ALL:
                mc->list = !mc->list;
                break;
+       case CTL_PASTE:
+               getsel(mc, seltext);
+               (void)strlcat(mc->searchstr, seltext, sizeof(mc->searchstr));
+               mc->changed = 1;
+               break;
        case CTL_ABORT:
                mi = xmalloc(sizeof(*mi));
                mi->text[0] = '\0';
@@ -557,6 +619,10 @@ menu_keycode(XKeyEvent *ev, enum ctltype
                case XK_a:
                case XK_A:
                        *ctl = CTL_ALL;
+                       break;
+               case XK_v:
+               case XK_V:
+                       *ctl = CTL_PASTE;
                        break;
                case XK_bracketleft:
                        *ctl = CTL_ABORT;

Reply via email to