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;