You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
7.3 KiB
C
222 lines
7.3 KiB
C
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <X11/Xlib.h>
|
|
#include <assert.h>
|
|
#include <stdbool.h>
|
|
|
|
/* Separable PDC clipboard functions for X11. These can be used
|
|
without bringing in the rest of PDCurses. They can also be used
|
|
on the VT platform (if you've got X11 going) and in wmcbrine's
|
|
'x11new' platform. In both cases, you have to add -pthreads to
|
|
the Makefile.
|
|
|
|
Programs using PDC_setclipboard should link with -lX11 -lpthread.
|
|
Programs only using PDC_getclipboard will only need -lX11 (no threading).
|
|
|
|
Much of this was copied from :
|
|
|
|
https://stackoverflow.com/questions/27378318/c-get-string-from-clipboard-on-linux
|
|
https://github.com/exebook/x11clipboard
|
|
|
|
The X11 clipboard is display-specific, so our first task is to open
|
|
the root display. The clipboard is "owned" by a window, so we make a
|
|
dummy window, and try to set it as the selection owner. That may
|
|
conceivably fail, so we check to make sure we really got it.
|
|
|
|
Then, if copying, we go into a loop waiting for somebody to request
|
|
the selection. If they do, and they're looking for UTF8 or ASCII text,
|
|
we give them what they're looking for. If we're notified that the
|
|
selection has been cleared (somebody else wants to own it), we're done
|
|
and return 0.
|
|
|
|
Ideally, this whole looping-waiting-for-requests should take place in
|
|
a separate thread so that the rest of our program can go on with its
|
|
work, and the following 'XCopy_threaded' does just that. The loop
|
|
exits when we get a 'selection cleared' message. Close down your program,
|
|
and the thread shuts down and the selection is lost... but that's par for
|
|
the course in X; to evade it, use a clipboard manager. (Which will
|
|
notice right away that you own the selection; it will then request the
|
|
selection, make a copy, and claim ownership itself. So our thread will
|
|
have a very short life.)
|
|
|
|
'cliptype' should be either PRIMARY or CLIPBOARD. */
|
|
|
|
static int XCopy( char *text, const char *cliptype, long length);
|
|
int XCopy_threaded( const char *text, const char *cliptype);
|
|
|
|
static int XCopy( char *text, const char *cliptype, long length)
|
|
{
|
|
Display *display = XOpenDisplay( 0);
|
|
int N = DefaultScreen( display);
|
|
unsigned long color = BlackPixel( display, N);
|
|
Window window = XCreateSimpleWindow( display, RootWindow( display, N),
|
|
0, 0, 1, 1, 0, color, color);
|
|
Atom UTF8 = XInternAtom( display, "UTF8_STRING", 1);
|
|
const Atom XA_ATOM = 4, XA_STRING = 31;
|
|
const Atom targets_atom = XInternAtom( display, "TARGETS", 0);
|
|
const Atom text_atom = XInternAtom(display, "TEXT", 0);
|
|
const Atom selection = XInternAtom(display, cliptype, 0);
|
|
XEvent event;
|
|
int rval = 0;
|
|
|
|
if (UTF8 == None)
|
|
UTF8 = XA_STRING;
|
|
XSetSelectionOwner( display, selection, window, 0);
|
|
if( XGetSelectionOwner( display, selection) != window)
|
|
rval = -1;
|
|
else if( text) do
|
|
{
|
|
XNextEvent( display, &event);
|
|
if( event.type == SelectionRequest &&
|
|
event.xselectionrequest.selection == selection)
|
|
{
|
|
XSelectionRequestEvent * xsr = &event.xselectionrequest;
|
|
XSelectionEvent ev = {0};
|
|
int R = 0;
|
|
|
|
ev.type = SelectionNotify, ev.display = xsr->display,
|
|
ev.requestor = xsr->requestor, ev.selection = xsr->selection,
|
|
ev.time = xsr->time, ev.target = xsr->target, ev.property = xsr->property;
|
|
if (ev.target == targets_atom)
|
|
R = XChangeProperty (ev.display, ev.requestor, ev.property, XA_ATOM, 32,
|
|
PropModeReplace, (unsigned char*)&UTF8, 1);
|
|
else if (ev.target == XA_STRING || ev.target == text_atom)
|
|
R = XChangeProperty(ev.display, ev.requestor, ev.property, XA_STRING, 8,
|
|
PropModeReplace, (const unsigned char *)text, length);
|
|
else if (ev.target == UTF8)
|
|
R = XChangeProperty(ev.display, ev.requestor, ev.property, UTF8, 8,
|
|
PropModeReplace, (const unsigned char *)text, length);
|
|
else
|
|
ev.property = None;
|
|
if ((R & 2) == 0)
|
|
XSendEvent (display, ev.requestor, 0, 0, (XEvent *)&ev);
|
|
}
|
|
} while( event.type != SelectionClear);
|
|
XDestroyWindow( display, window);
|
|
XCloseDisplay( display);
|
|
free( text);
|
|
return( rval);
|
|
}
|
|
|
|
#include <pthread.h>
|
|
|
|
static void *XCopy_thread_func( void *clip_request)
|
|
{
|
|
char **args = (char **)clip_request;
|
|
const long local_length = (const long)args[2];
|
|
|
|
assert( args);
|
|
assert( args[0]);
|
|
assert( args[1]);
|
|
XCopy( args[0], args[1], local_length);
|
|
return( NULL);
|
|
}
|
|
|
|
static int setclipboard( const char *contents, const long length,
|
|
const bool is_select)
|
|
{
|
|
int rval = 0;
|
|
static pthread_t thread_id;
|
|
static char *args[4];
|
|
|
|
if( contents)
|
|
{
|
|
args[0] = (char *)malloc( length + 1);
|
|
args[0][length] = '\0';
|
|
memcpy( args[0], contents, length);
|
|
}
|
|
else
|
|
args[0] = NULL;
|
|
args[1] = (is_select ? "PRIMARY" : "CLIPBOARD");
|
|
args[2] = (char *)length;
|
|
args[3] = NULL;
|
|
if( thread_id)
|
|
{
|
|
XCopy( NULL, args[1], 0);
|
|
pthread_join( thread_id, NULL);
|
|
thread_id = (pthread_t)0;
|
|
}
|
|
if( contents)
|
|
rval = pthread_create( &thread_id, NULL, XCopy_thread_func, args);
|
|
return( rval);
|
|
}
|
|
|
|
int PDC_setclipboard( const char *contents, long length)
|
|
{
|
|
return( setclipboard( contents, length, false));
|
|
}
|
|
|
|
int PDC_setselection( const char *contents, long length)
|
|
{
|
|
return( setclipboard( contents, length, true));
|
|
}
|
|
|
|
static int getclipboard( char **contents, long *length, const bool is_select)
|
|
{
|
|
Display *display = XOpenDisplay( 0);
|
|
int N = DefaultScreen( display);
|
|
unsigned long color = BlackPixel( display, N);
|
|
Window window = XCreateSimpleWindow( display, RootWindow( display, N),
|
|
0, 0, 1, 1, 0, color, color);
|
|
Atom UTF8 = XInternAtom( display, "UTF8_STRING", 1);
|
|
const Atom XA_STRING = 31;
|
|
char *result, *rval = NULL;
|
|
unsigned long ressize = 0, restail;
|
|
int resbits;
|
|
const Atom bufid = XInternAtom(display,
|
|
(is_select ? "PRIMARY" : "CLIPBOARD"), False),
|
|
propid = XInternAtom(display, "XSEL_DATA", False),
|
|
incrid = XInternAtom(display, "INCR", False);
|
|
XEvent event;
|
|
|
|
if (UTF8 == None)
|
|
UTF8 = XA_STRING;
|
|
XConvertSelection(display, bufid, UTF8, propid, window, CurrentTime);
|
|
do {
|
|
XNextEvent(display, &event);
|
|
} while (event.type != SelectionNotify || event.xselection.selection != bufid);
|
|
|
|
if (event.xselection.property)
|
|
{
|
|
XGetWindowProperty(display, window, propid, 0, LONG_MAX/4, False, AnyPropertyType,
|
|
&UTF8, &resbits, &ressize, &restail, (unsigned char**)&result);
|
|
|
|
if( UTF8 != incrid)
|
|
{
|
|
rval = (char *)malloc( ressize + 1);
|
|
memcpy( rval, result, ressize);
|
|
rval[ressize] = '\0';
|
|
}
|
|
XFree(result);
|
|
XDeleteProperty( display, window, propid);
|
|
}
|
|
XDestroyWindow(display, window);
|
|
XCloseDisplay(display);
|
|
*contents = rval;
|
|
*length = (long)ressize;
|
|
return( rval ? 0 : -1);
|
|
}
|
|
|
|
int PDC_getclipboard( char **contents, long *length)
|
|
{
|
|
return( getclipboard( contents, length, false));
|
|
}
|
|
|
|
int PDC_getselection( char **contents, long *length)
|
|
{
|
|
return( getclipboard( contents, length, true));
|
|
}
|
|
|
|
int PDC_freeclipboard( char *contents)
|
|
{
|
|
free( contents);
|
|
return( 0);
|
|
}
|
|
|
|
int PDC_clearclipboard( void)
|
|
{
|
|
return( 0);
|
|
}
|