Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 91 additions & 34 deletions clipboard_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,86 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <dlfcn.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>

// syncStatus is a function from the Go side.
extern void syncStatus(uintptr_t handle, int status);

void *libX11;

Display* (*P_XOpenDisplay)(int);
void (*P_XCloseDisplay)(Display*);
Window (*P_XDefaultRootWindow)(Display*);
Window (*P_XCreateSimpleWindow)(Display*, Window, int, int, int, int, int, int, int);
Atom (*P_XInternAtom)(Display*, char*, int);
void (*P_XSetSelectionOwner)(Display*, Atom, Window, unsigned long);
Window (*P_XGetSelectionOwner)(Display*, Atom);
void (*P_XNextEvent)(Display*, XEvent*);
int (*P_XChangeProperty)(Display*, Window, Atom, Atom, int, int, unsigned char*, int);
void (*P_XSendEvent)(Display*, Window, int, long , XEvent*);
int (*P_XGetWindowProperty) (Display*, Window, Atom, long, long, Bool, Atom, Atom*, int*, unsigned long *, unsigned long *, unsigned char **);
void (*P_XFree) (void*);
void (*P_XDeleteProperty) (Display*, Window, Atom);
void (*P_XConvertSelection)(Display*, Atom, Atom, Atom, Window, Time);

int initX11() {
if (libX11) {
return 1;
}
libX11 = dlopen("libX11.so", RTLD_LAZY);
if (!libX11) {
return -1;
}
P_XOpenDisplay = (Display* (*)(int)) dlsym(libX11, "XOpenDisplay");
P_XCloseDisplay = (void (*)(Display*)) dlsym(libX11, "XCloseDisplay");
P_XDefaultRootWindow = (Window (*)(Display*)) dlsym(libX11, "XDefaultRootWindow");
P_XCreateSimpleWindow = (Window (*)(Display*, Window, int, int, int, int, int, int, int)) dlsym(libX11, "XCreateSimpleWindow");
P_XInternAtom = (Atom (*)(Display*, char*, int)) dlsym(libX11, "XInternAtom");
P_XSetSelectionOwner = (void (*)(Display*, Atom, Window, unsigned long)) dlsym(libX11, "XSetSelectionOwner");
P_XGetSelectionOwner = (Window (*)(Display*, Atom)) dlsym(libX11, "XGetSelectionOwner");
P_XNextEvent = (void (*)(Display*, XEvent*)) dlsym(libX11, "XNextEvent");
P_XChangeProperty = (int (*)(Display*, Window, Atom, Atom, int, int, unsigned char*, int)) dlsym(libX11, "XChangeProperty");
P_XSendEvent = (void (*)(Display*, Window, int, long , XEvent*)) dlsym(libX11, "XSendEvent");
P_XGetWindowProperty = (int (*)(Display*, Window, Atom, long, long, Bool, Atom, Atom*, int*, unsigned long *, unsigned long *, unsigned char **)) dlsym(libX11, "XGetWindowProperty");
P_XFree = (void (*)(void*)) dlsym(libX11, "XFree");
P_XDeleteProperty = (void (*)(Display*, Window, Atom)) dlsym(libX11, "XDeleteProperty");
P_XConvertSelection = (void (*)(Display*, Atom, Atom, Atom, Window, Time)) dlsym(libX11, "XConvertSelection");
return 1;
}

int clipboard_test() {
if (!initX11()) {
return -1;
}

Display* d = NULL;
for (int i = 0; i < 42; i++) {
d = XOpenDisplay(0);
d = (*P_XOpenDisplay)(0);
if (d == NULL) {
continue;
}
break;
}

if (d == NULL) {
return -1;
}
XCloseDisplay(d);
(*P_XCloseDisplay)(d);
return 0;
}

// clipboard_write writes the given buf of size n as type typ.
// if start is provided, the value of start will be changed to 1 to indicate
// if the write is availiable for reading.
int clipboard_write(char *typ, unsigned char *buf, size_t n, uintptr_t handle) {
if (!initX11()) {
return -1;
}

Display* d = NULL;
for (int i = 0; i < 42; i++) {
d = XOpenDisplay(0);
d = (*P_XOpenDisplay)(0);
if (d == NULL) {
continue;
}
Expand All @@ -50,26 +100,25 @@ int clipboard_write(char *typ, unsigned char *buf, size_t n, uintptr_t handle) {
syncStatus(handle, -1);
return -1;
}

Window w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, 1, 1, 0, 0, 0);
Window w = (*P_XCreateSimpleWindow)(d, (*P_XDefaultRootWindow)(d), 0, 0, 1, 1, 0, 0, 0);

// Use False because these may not available for the first time.
Atom sel = XInternAtom(d, "CLIPBOARD", False);
Atom atomString = XInternAtom(d, "UTF8_STRING", False);
Atom atomImage = XInternAtom(d, "image/png", False);
Atom targetsAtom = XInternAtom(d, "TARGETS", False);
Atom sel = (*P_XInternAtom)(d, "CLIPBOARD", 0);
Atom atomString = (*P_XInternAtom)(d, "UTF8_STRING", 0);
Atom atomImage = (*P_XInternAtom)(d, "image/png", 0);
Atom targetsAtom = (*P_XInternAtom)(d, "TARGETS", 0);

// Use True to makesure the requested type is a valid type.
Atom target = XInternAtom(d, typ, True);
Atom target = (*P_XInternAtom)(d, typ, 1);
if (target == None) {
XCloseDisplay(d);
(*P_XCloseDisplay)(d);
syncStatus(handle, -2);
return -2;
}

XSetSelectionOwner(d, sel, w, CurrentTime);
if (XGetSelectionOwner(d, sel) != w) {
XCloseDisplay(d);
(*P_XSetSelectionOwner)(d, sel, w, CurrentTime);
if ((*P_XGetSelectionOwner)(d, sel) != w) {
(*P_XCloseDisplay)(d);
syncStatus(handle, -3);
return -3;
}
Expand All @@ -83,13 +132,13 @@ int clipboard_write(char *typ, unsigned char *buf, size_t n, uintptr_t handle) {
notified = 1;
}

XNextEvent(d, &event);
(*P_XNextEvent)(d, &event);
switch (event.type) {
case SelectionClear:
// For debugging:
// printf("x11write: lost ownership of clipboard selection.\n");
// fflush(stdout);
XCloseDisplay(d);
(*P_XCloseDisplay)(d);
return 0;
case SelectionNotify:
// For debugging:
Expand All @@ -114,24 +163,24 @@ int clipboard_write(char *typ, unsigned char *buf, size_t n, uintptr_t handle) {
ev.property = xsr->property;

if (ev.target == atomString && ev.target == target) {
R = XChangeProperty(ev.display, ev.requestor, ev.property,
R = (*P_XChangeProperty)(ev.display, ev.requestor, ev.property,
atomString, 8, PropModeReplace, buf, n);
} else if (ev.target == atomImage && ev.target == target) {
R = XChangeProperty(ev.display, ev.requestor, ev.property,
R = (*P_XChangeProperty)(ev.display, ev.requestor, ev.property,
atomImage, 8, PropModeReplace, buf, n);
} else if (ev.target == targetsAtom) {
// Reply atoms for supported targets, other clients should
// request the clipboard again and obtain the data if their
// implementation is correct.
Atom targets[] = { atomString, atomImage };
R = XChangeProperty(ev.display, ev.requestor, ev.property,
R = (*P_XChangeProperty)(ev.display, ev.requestor, ev.property,
XA_ATOM, 32, PropModeReplace,
(unsigned char *)&targets, sizeof(targets)/sizeof(Atom));
} else {
ev.property = None;
}

if ((R & 2) == 0) XSendEvent(d, ev.requestor, 0, 0, (XEvent *)&ev);
if ((R & 2) == 0) (*P_XSendEvent)(d, ev.requestor, 0, 0, (XEvent *)&ev);
break;
}
}
Expand All @@ -140,6 +189,10 @@ int clipboard_write(char *typ, unsigned char *buf, size_t n, uintptr_t handle) {
// read_data reads the property of a selection if the target atom matches
// the actual atom.
unsigned long read_data(XSelectionEvent *sev, Atom sel, Atom prop, Atom target, char **buf) {
if (!initX11()) {
return -1;
}

unsigned char *data;
Atom actual;
int format;
Expand All @@ -149,7 +202,7 @@ unsigned long read_data(XSelectionEvent *sev, Atom sel, Atom prop, Atom target,
return 0;
}

int ret = XGetWindowProperty(sev->display, sev->requestor, sev->property,
int ret = (*P_XGetWindowProperty)(sev->display, sev->requestor, sev->property,
0L, (~0L), 0, AnyPropertyType, &actual, &format, &size, &n, &data);
if (ret != Success) {
return 0;
Expand All @@ -159,8 +212,8 @@ unsigned long read_data(XSelectionEvent *sev, Atom sel, Atom prop, Atom target,
*buf = (char *)malloc(size * sizeof(char));
memcpy(*buf, data, size*sizeof(char));
}
XFree(data);
XDeleteProperty(sev->display, sev->requestor, sev->property);
(*P_XFree)(data);
(*P_XDeleteProperty)(sev->display, sev->requestor, sev->property);
return size * sizeof(char);
}

Expand All @@ -169,9 +222,13 @@ unsigned long read_data(XSelectionEvent *sev, Atom sel, Atom prop, Atom target,
//
// The caller of this function should responsible for the free of the buf.
unsigned long clipboard_read(char* typ, char **buf) {
if (!initX11()) {
return -1;
}

Display* d = NULL;
for (int i = 0; i < 42; i++) {
d = XOpenDisplay(0);
d = (*P_XOpenDisplay)(0);
if (d == NULL) {
continue;
}
Expand All @@ -181,27 +238,27 @@ unsigned long clipboard_read(char* typ, char **buf) {
return -1;
}

Window w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, 1, 1, 0, 0, 0);
Window w = (*P_XCreateSimpleWindow)(d, (*P_XDefaultRootWindow)(d), 0, 0, 1, 1, 0, 0, 0);

// Use False because these may not available for the first time.
Atom sel = XInternAtom(d, "CLIPBOARD", False);
Atom prop = XInternAtom(d, "GOLANG_DESIGN_DATA", False);
Atom sel = (*P_XInternAtom)(d, "CLIPBOARD", False);
Atom prop = (*P_XInternAtom)(d, "GOLANG_DESIGN_DATA", False);

// Use True to makesure the requested type is a valid type.
Atom target = XInternAtom(d, typ, True);
Atom target = (*P_XInternAtom)(d, typ, True);
if (target == None) {
XCloseDisplay(d);
(*P_XCloseDisplay)(d);
return -2;
}

XConvertSelection(d, sel, target, prop, w, CurrentTime);
(*P_XConvertSelection)(d, sel, target, prop, w, CurrentTime);
XEvent event;
for (;;) {
XNextEvent(d, &event);
(*P_XNextEvent)(d, &event);
if (event.type != SelectionNotify) continue;
break;
}
unsigned long n = read_data((XSelectionEvent *)&event.xselection, sel, prop, target, buf);
XCloseDisplay(d);
(*P_XCloseDisplay)(d);
return n;
}
}
5 changes: 1 addition & 4 deletions clipboard_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@
package clipboard

/*
#cgo LDFLAGS: -lX11
#cgo LDFLAGS: -ldl
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdatomic.h>

int clipboard_test();
int clipboard_write(
Expand Down