commit f726076

uint  ·  2026-06-09 21:12:29 +0000 UTC
parent f133f7a
Clipboard support X11
2 files changed,  +120, -0
M maus.h
M maus.h
+8, -0
 1@@ -94,6 +94,7 @@ typedef struct {
 2 
 3 typedef struct {
 4 	MausBackend    backend;
 5+	char*          clipboard;
 6 
 7 	uint32_t*      fb;
 8 	uint32_t       stride;
 9@@ -112,6 +113,13 @@ typedef struct {
10 	bool           mouse_buttons[MAUS_MOUSE_BUTTON_LAST];
11 } Maus;
12 
13+
14+/* set text to system clipboard */
15+void maus_clipboard_set_text(Maus* mw, const char* text);
16+
17+/* get text from system clipboard */
18+char* maus_clipboard_get_text(Maus* mw);
19+
20 /* close a Maus. returns false on fail */
21 void maus_close(Maus* mw);
22 
+112, -0
  1@@ -1,5 +1,7 @@
  2+#include <limits.h>
  3 #include <stdbool.h>
  4 #include <stdlib.h>
  5+#include <string.h>
  6 #include <sys/shm.h>
  7 #include <sys/ipc.h>
  8 
  9@@ -12,6 +14,7 @@
 10 #include "maus.h"
 11 #include "maus_input.h"
 12 #include "maus_x11.h"
 13+#include "utils.h"
 14 
 15 typedef struct {
 16 	KeySym  x11;
 17@@ -295,6 +298,42 @@ static bool handle_event(XEvent* xev, MausEvent* ev, Maus* mw)
 18 			ev->resize.width = xev->xconfigure.width;
 19 			ev->resize.height = xev->xconfigure.height;
 20 			return true;
 21+
 22+		case SelectionRequest: {
 23+			XSelectionRequestEvent* req = &xev->xselectionrequest;
 24+			XEvent rsp; /* response */
 25+
 26+			Atom utf8_string = XInternAtom(be->display, "UTF8_STRING", False);
 27+			Atom clipboard = XInternAtom(be->display, "CLIPBOARD", False);
 28+
 29+			rsp.type = SelectionNotify;
 30+			rsp.xselection.display = req->display;
 31+			rsp.xselection.requestor = req->requestor;
 32+			rsp.xselection.selection = req->selection;
 33+			rsp.xselection.target = req->target;
 34+			rsp.xselection.property = None; 
 35+			rsp.xselection.time = req->time;
 36+
 37+			/* assign clipboard text to requesting programs
 38+			   window property */
 39+			if (req->selection == clipboard &&
 40+			    req->target == utf8_string &&
 41+			    mw->clipboard) {
 42+			        XChangeProperty(
 43+					req->display, req->requestor,
 44+					req->property, utf8_string, 8,
 45+					PropModeReplace, (unsigned char*)mw->clipboard, 
 46+					strlen(mw->clipboard)
 47+			 	);
 48+				rsp.xselection.property = req->property; 
 49+			}
 50+
 51+			/* send reply back to the prog requesting paste */
 52+			XSendEvent(req->display, req->requestor, False, 0, &rsp);
 53+			XFlush(be->display);
 54+
 55+			return false; /* skip maus polling it */
 56+		}
 57 	}
 58 
 59 	return false;
 60@@ -341,10 +380,83 @@ static int xerr(Display* dpy, XErrorEvent* ev)
 61 	return 0;
 62 }
 63 
 64+void maus_clipboard_set_text(Maus* mw, const char* text)
 65+{
 66+	MausBackend* be = &mw->backend;
 67+	if (!be->display || be->win == None)
 68+		return;
 69+
 70+	/* clear old entry */
 71+	if (mw->clipboard) {
 72+		free(mw->clipboard);
 73+		mw->clipboard = NULL;
 74+	}
 75+
 76+	if (text)
 77+		mw->clipboard = strdup(text);
 78+
 79+	Atom clipboard = XInternAtom(be->display, "CLIPBOARD", False);
 80+	XSetSelectionOwner(be->display, clipboard, be->win, CurrentTime);
 81+	XFlush(be->display);
 82+}
 83+
 84+char* maus_clipboard_get_text(Maus* mw)
 85+{
 86+	MausBackend* be = &mw->backend;
 87+	Atom clipboard = XInternAtom(be->display, "CLIPBOARD", False);
 88+	Atom utf8_string = XInternAtom(be->display, "UTF8_STRING", False);
 89+	Atom maus_clipboard_prop = XInternAtom(be->display, "MAUS_CLIPBOARD_PROP", False);
 90+
 91+	/* fetch clipboard data to `maus_clipboard_prop`
 92+	   window property */
 93+	XConvertSelection(
 94+		be->display, clipboard, utf8_string,
 95+		maus_clipboard_prop, be->win, CurrentTime
 96+	);
 97+	XFlush(be->display);
 98+
 99+	/* check for selection response */
100+	XEvent xev;
101+	bool ok = false;
102+	for (int timeout = 0; timeout < 10000; timeout++) { 
103+		if (XCheckTypedWindowEvent(be->display, be->win, SelectionNotify, &xev)) {
104+			if (xev.xselection.property != None)
105+				ok = true;
106+			break;
107+		}
108+	}
109+	if (!ok)
110+		return NULL;
111+
112+	/* read string off property */
113+	Atom actual_type;
114+	int actual_fmt;
115+	unsigned long nitems;
116+	unsigned long bytes_after;
117+	unsigned char* prop_data = NULL;
118+	XGetWindowProperty(
119+		be->display, be->win, maus_clipboard_prop, 0,LONG_MAX,
120+		True, utf8_string, &actual_type, &actual_fmt, &nitems,
121+		&bytes_after, &prop_data
122+	);
123+
124+	char* res = NULL;
125+	if (prop_data) {
126+		res = strdup((char*)prop_data);
127+		XFree(prop_data);
128+	}
129+
130+	return res;
131+}
132+
133 void maus_close(Maus* mw)
134 {
135 	MausBackend* be = &mw->backend;
136 	fb_destroy(mw);
137+	if (mw->clipboard) {
138+		free(mw->clipboard);
139+		mw->clipboard = NULL;
140+	}
141 	if (be->win != None) {
142 		(void) maus_close_window(mw);
143 	}