1+ // Copyright 2021 The golang.design Initiative authors.
2+ // All rights reserved. Use of this source code is governed
3+ // by a GNU GPL-3 license that can be found in the LICENSE file.
4+ //
5+ // Written by Changkun Ou <changkun.de>
6+
7+ #include <stdlib.h>
8+ #include <stdio.h>
9+ #include <string.h>
10+ #include <X11/Xlib.h>
11+ #include <X11/Xatom.h>
12+
13+ int clipboard_test () {
14+ Display * d = XOpenDisplay (0 );
15+ if (d == NULL ) {
16+ return -1 ;
17+ }
18+ XCloseDisplay (d );
19+ return 0 ;
20+ }
21+
22+ // clipboard_write writes the given buf of size n as type typ.
23+ // if start is provided, the value of start will be changed to 1 to indicate
24+ // if the write is availiable for reading.
25+ int clipboard_write (char * typ , unsigned char * buf , size_t n , int * start ) {
26+ Display * d = XOpenDisplay (0 );
27+ if (d == NULL ) {
28+ if (start != NULL && * start == 0 ) * start = -1 ;
29+ return -1 ;
30+ }
31+
32+ Window w = XCreateSimpleWindow (d , DefaultRootWindow (d ), 0 , 0 , 1 , 1 , 0 , 0 , 0 );
33+ Atom sel = XInternAtom (d , "CLIPBOARD" , True );
34+ Atom atomString = XInternAtom (d , "UTF8_STRING" , True );
35+ Atom atomImage = XInternAtom (d , "image/png" , True );
36+ Atom targetsAtom = XInternAtom (d , "TARGETS" , True );
37+
38+ Atom target = XInternAtom (d , typ , True );
39+ if (target == None ) {
40+ XCloseDisplay (d );
41+ if (start != NULL && * start == 0 ) * start = -2 ;
42+ return -2 ;
43+ }
44+
45+ XSetSelectionOwner (d , sel , w , CurrentTime );
46+ if (XGetSelectionOwner (d , sel ) != w ) {
47+ XCloseDisplay (d );
48+ if (start != NULL && * start == 0 ) * start = -3 ;
49+ return -3 ;
50+ }
51+
52+ XEvent event ;
53+ XSelectionRequestEvent * xsr ;
54+ for (;;) {
55+ // FIXME: this should race with the code on the Go side, start
56+ // should use an atomic version, and use atomic_store.
57+ if (start != NULL && * start == 0 )
58+ * start = 1 ; // notify Go side
59+
60+ XNextEvent (d , & event );
61+ switch (event .type ) {
62+ case SelectionClear :
63+ // For debugging:
64+ // printf("x11write: lost ownership of clipboard selection.\n");
65+ // fflush(stdout);
66+ XCloseDisplay (d );
67+ return 0 ;
68+ case SelectionNotify :
69+ // For debugging:
70+ // printf("x11write: notify.\n");
71+ // fflush(stdout);
72+ break ;
73+ case SelectionRequest :
74+ if (event .xselectionrequest .selection != sel ) {
75+ break ;
76+ }
77+
78+ XSelectionRequestEvent * xsr = & event .xselectionrequest ;
79+ XSelectionEvent ev = {0 };
80+ int R = 0 ;
81+
82+ ev .type = SelectionNotify ;
83+ ev .display = xsr -> display ;
84+ ev .requestor = xsr -> requestor ;
85+ ev .selection = xsr -> selection ;
86+ ev .time = xsr -> time ;
87+ ev .target = xsr -> target ;
88+ ev .property = xsr -> property ;
89+
90+ if (ev .target == atomString && ev .target == target ) {
91+ R = XChangeProperty (ev .display , ev .requestor , ev .property ,
92+ atomString , 8 , PropModeReplace , buf , n );
93+ } else if (ev .target == atomImage && ev .target == target ) {
94+ R = XChangeProperty (ev .display , ev .requestor , ev .property ,
95+ atomImage , 8 , PropModeReplace , buf , n );
96+ } else if (ev .target == targetsAtom ) {
97+ // Reply atoms for supported targets, other clients should
98+ // request the clipboard again and obtain the data if their
99+ // implementation is correct.
100+ Atom targets [] = { atomString , atomImage };
101+ R = XChangeProperty (ev .display , ev .requestor , ev .property ,
102+ XA_ATOM , 32 , PropModeReplace ,
103+ (unsigned char * )& targets , sizeof (targets )/sizeof (Atom ));
104+ } else {
105+ ev .property = None ;
106+ }
107+
108+ if ((R & 2 ) == 0 ) XSendEvent (d , ev .requestor , 0 , 0 , (XEvent * )& ev );
109+ break ;
110+ }
111+ }
112+ }
113+
114+ // read_data reads the property of a selection if the target atom matches
115+ // the actual atom.
116+ unsigned long read_data (XSelectionEvent * sev , Atom sel , Atom prop , Atom target , char * * buf ) {
117+ unsigned char * data ;
118+ Atom actual ;
119+ int format ;
120+ unsigned long n = 0 ;
121+ unsigned long size = 0 ;
122+ if (sev -> property == None || sev -> selection != sel || sev -> property != prop ) {
123+ return 0 ;
124+ }
125+
126+ int ret = XGetWindowProperty (sev -> display , sev -> requestor , sev -> property ,
127+ 0L , (~0L ), 0 , AnyPropertyType , & actual , & format , & size , & n , & data );
128+ if (ret != Success ) {
129+ return 0 ;
130+ }
131+
132+ if (actual == target && buf != NULL ) {
133+ * buf = (char * )malloc (size * sizeof (char ));
134+ memcpy (* buf , data , size * sizeof (char ));
135+ }
136+ XFree (data );
137+ XDeleteProperty (sev -> display , sev -> requestor , sev -> property );
138+ return size * sizeof (char );
139+ }
140+
141+ // clipboard_read reads the clipboard selection in given mime type typ.
142+ // the readed bytes is written into buf and returns the size of the buffer.
143+ //
144+ // The caller of this function should responsible for the free of the buf.
145+ unsigned long clipboard_read (char * typ , char * * buf ) {
146+ Display * d = XOpenDisplay (0 );
147+ if (d == NULL ) {
148+ return -1 ;
149+ }
150+
151+ Window w = XCreateSimpleWindow (d , DefaultRootWindow (d ), 0 , 0 , 1 , 1 , 0 , 0 , 0 );
152+ Atom sele = XInternAtom (d , "CLIPBOARD" , True );
153+ Atom prop = XInternAtom (d , "GOLANG_DESIGN_DATA" , False );
154+ Atom target = XInternAtom (d , typ , True );
155+ if (target == None ) {
156+ XCloseDisplay (d );
157+ return -1 ;
158+ }
159+ XConvertSelection (d , sele , target , prop , w , CurrentTime );
160+ XEvent event ;
161+ for (;;) {
162+ XNextEvent (d , & event );
163+ if (event .type != SelectionNotify ) continue ;
164+ break ;
165+ }
166+ unsigned long n = read_data ((XSelectionEvent * )& event .xselection , sele , prop , target , buf );
167+ XCloseDisplay (d );
168+ return n ;
169+ }
0 commit comments