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 }