mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-10 01:01:30 +00:00
[client] clipboard: add transport provider interface
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
Route clipboard exchange through a provider interface. Connected transports take priority while SPICE remains the fallback. Track asynchronous X11 and Wayland transfers by request, preserving clipboard ownership across provider changes. Use PureSpice clipboard status notifications for capability and lifecycle updates.
This commit is contained in:
@@ -31,16 +31,29 @@
|
||||
#include "app.h"
|
||||
#include "common/array.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/locking.h"
|
||||
|
||||
struct X11ClipboardRead
|
||||
{
|
||||
Window window;
|
||||
LG_ClipboardRequest request;
|
||||
LG_ClipboardData type;
|
||||
bool incremental;
|
||||
uint8_t * buffer;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
struct X11ClipboardState
|
||||
{
|
||||
LG_Lock lock;
|
||||
Atom aCurSelection;
|
||||
Atom aTypes[LG_CLIPBOARD_DATA_NONE];
|
||||
Window targetsWindow;
|
||||
LG_ClipboardData type;
|
||||
bool haveRequest;
|
||||
|
||||
bool incrStart;
|
||||
unsigned int lowerBound;
|
||||
struct X11ClipboardRead read;
|
||||
};
|
||||
|
||||
static const char * atomTypes[] =
|
||||
@@ -60,6 +73,8 @@ static void x11CBSelectionClear(const XSelectionClearEvent e);
|
||||
static void x11CBSelectionIncr(const XPropertyEvent e);
|
||||
static void x11CBSelectionNotify(const XSelectionEvent e);
|
||||
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e);
|
||||
static LG_ClipboardRequest cancelReadNL(void);
|
||||
static void clearTargetsNL(void);
|
||||
|
||||
bool x11CBEventThread(const XEvent * xe)
|
||||
{
|
||||
@@ -83,9 +98,6 @@ bool x11CBEventThread(const XEvent * xe)
|
||||
|
||||
if (xe->xproperty.atom == x11atoms.SEL_DATA)
|
||||
{
|
||||
if (x11cb.lowerBound == 0)
|
||||
return true;
|
||||
|
||||
x11CBSelectionIncr(xe->xproperty);
|
||||
return true;
|
||||
}
|
||||
@@ -106,6 +118,7 @@ bool x11CBEventThread(const XEvent * xe)
|
||||
|
||||
bool x11CBInit(void)
|
||||
{
|
||||
LG_LOCK_INIT(x11cb.lock);
|
||||
x11cb.aCurSelection = BadValue;
|
||||
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
{
|
||||
@@ -131,7 +144,7 @@ bool x11CBInit(void)
|
||||
}
|
||||
|
||||
static void x11CBReplyFn(void * opaque, LG_ClipboardData type,
|
||||
uint8_t * data, uint32_t size)
|
||||
const uint8_t * data, uint32_t size)
|
||||
{
|
||||
XEvent *s = (XEvent *)opaque;
|
||||
|
||||
@@ -169,7 +182,12 @@ static void x11CBSelectionRequest(const XSelectionRequestEvent e)
|
||||
s->xselection.property = e.property;
|
||||
s->xselection.time = e.time;
|
||||
|
||||
if (!x11cb.haveRequest)
|
||||
LG_LOCK(x11cb.lock);
|
||||
const bool haveRequest = x11cb.haveRequest;
|
||||
const LG_ClipboardData requestType = x11cb.type;
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
|
||||
if (!haveRequest)
|
||||
goto nodata;
|
||||
|
||||
// target list requested
|
||||
@@ -177,7 +195,7 @@ static void x11CBSelectionRequest(const XSelectionRequestEvent e)
|
||||
{
|
||||
Atom targets[2];
|
||||
targets[0] = x11atoms.TARGETS;
|
||||
targets[1] = x11cb.aTypes[x11cb.type];
|
||||
targets[1] = x11cb.aTypes[requestType];
|
||||
|
||||
XChangeProperty(
|
||||
e.display,
|
||||
@@ -195,10 +213,10 @@ static void x11CBSelectionRequest(const XSelectionRequestEvent e)
|
||||
|
||||
// look to see if we can satisfy the data type
|
||||
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
if (x11cb.aTypes[i] == e.target && x11cb.type == i)
|
||||
if (x11cb.aTypes[i] == e.target && requestType == i)
|
||||
{
|
||||
// request the data
|
||||
if (app_clipboardRequest(x11CBReplyFn, s))
|
||||
if (app_clipboardRequest(requestType, x11CBReplyFn, s))
|
||||
return;
|
||||
goto nodata;
|
||||
}
|
||||
@@ -215,20 +233,53 @@ send:
|
||||
|
||||
static void x11CBSelectionClear(const XSelectionClearEvent e)
|
||||
{
|
||||
if (e.selection != x11atoms.CLIPBOARD)
|
||||
(void)e;
|
||||
}
|
||||
|
||||
/* x11cb.lock must be held. */
|
||||
static struct X11ClipboardRead clearReadNL(void)
|
||||
{
|
||||
const struct X11ClipboardRead read = x11cb.read;
|
||||
x11cb.read = (struct X11ClipboardRead) { 0 };
|
||||
if (read.window)
|
||||
XDestroyWindow(x11.display, read.window);
|
||||
return read;
|
||||
}
|
||||
|
||||
/* x11cb.lock must be held. */
|
||||
static LG_ClipboardRequest cancelReadNL(void)
|
||||
{
|
||||
const struct X11ClipboardRead read = clearReadNL();
|
||||
free(read.buffer);
|
||||
return read.request;
|
||||
}
|
||||
|
||||
/* x11cb.lock must be held. */
|
||||
static void clearTargetsNL(void)
|
||||
{
|
||||
if (!x11cb.targetsWindow)
|
||||
return;
|
||||
|
||||
x11cb.aCurSelection = BadValue;
|
||||
app_clipboardRelease();
|
||||
return;
|
||||
XDestroyWindow(x11.display, x11cb.targetsWindow);
|
||||
x11cb.targetsWindow = 0;
|
||||
}
|
||||
|
||||
static void x11CBSelectionIncr(const XPropertyEvent e)
|
||||
{
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long itemCount, after;
|
||||
unsigned char *data;
|
||||
unsigned long itemCount;
|
||||
unsigned long after;
|
||||
unsigned char * data = NULL;
|
||||
|
||||
LG_LOCK(x11cb.lock);
|
||||
if (!x11cb.read.window || !x11cb.read.incremental ||
|
||||
e.window != x11cb.read.window ||
|
||||
e.atom != x11atoms.SEL_DATA)
|
||||
{
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (XGetWindowProperty(
|
||||
e.display,
|
||||
@@ -236,49 +287,7 @@ static void x11CBSelectionIncr(const XPropertyEvent e)
|
||||
e.atom,
|
||||
0, ~0L, // start and length
|
||||
True, // delete the property
|
||||
x11atoms.INCR,
|
||||
&type,
|
||||
&format,
|
||||
&itemCount,
|
||||
&after,
|
||||
&data) != Success)
|
||||
{
|
||||
DEBUG_INFO("GetProp Failed");
|
||||
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
LG_ClipboardData dataType;
|
||||
for(dataType = 0; dataType < LG_CLIPBOARD_DATA_NONE; ++dataType)
|
||||
if (x11cb.aTypes[dataType] == type)
|
||||
break;
|
||||
|
||||
if (dataType == LG_CLIPBOARD_DATA_NONE)
|
||||
{
|
||||
DEBUG_WARN("clipboard data (%s) not in a supported format",
|
||||
XGetAtomName(x11.display, type));
|
||||
|
||||
x11cb.lowerBound = 0;
|
||||
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (x11cb.incrStart)
|
||||
{
|
||||
app_clipboardNotifySize(dataType, x11cb.lowerBound);
|
||||
x11cb.incrStart = false;
|
||||
}
|
||||
|
||||
XFree(data);
|
||||
data = NULL;
|
||||
|
||||
if (XGetWindowProperty(
|
||||
e.display,
|
||||
e.window,
|
||||
e.atom,
|
||||
0, ~0L, // start and length
|
||||
True, // delete the property
|
||||
type,
|
||||
AnyPropertyType,
|
||||
&type,
|
||||
&format,
|
||||
&itemCount,
|
||||
@@ -286,49 +295,233 @@ static void x11CBSelectionIncr(const XPropertyEvent e)
|
||||
&data) != Success)
|
||||
{
|
||||
DEBUG_ERROR("XGetWindowProperty Failed");
|
||||
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
|
||||
goto out;
|
||||
const struct X11ClipboardRead read = clearReadNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
free(read.buffer);
|
||||
app_clipboardAbort(read.request);
|
||||
return;
|
||||
}
|
||||
|
||||
app_clipboardData(dataType, data, itemCount);
|
||||
x11cb.lowerBound -= itemCount;
|
||||
LG_ClipboardData dataType;
|
||||
for(dataType = 0; dataType < LG_CLIPBOARD_DATA_NONE; ++dataType)
|
||||
if (x11cb.aTypes[dataType] == type)
|
||||
break;
|
||||
|
||||
out:
|
||||
if ((itemCount && !data) || dataType == LG_CLIPBOARD_DATA_NONE ||
|
||||
dataType != x11cb.read.type || format != 8)
|
||||
{
|
||||
DEBUG_WARN("Invalid incremental clipboard data");
|
||||
const struct X11ClipboardRead read = clearReadNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
if (data)
|
||||
XFree(data);
|
||||
free(read.buffer);
|
||||
app_clipboardAbort(read.request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemCount == 0)
|
||||
{
|
||||
const struct X11ClipboardRead read = clearReadNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
if (data)
|
||||
XFree(data);
|
||||
app_clipboardData(
|
||||
read.request, read.type, read.buffer, read.size);
|
||||
free(read.buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemCount > SIZE_MAX - x11cb.read.size)
|
||||
{
|
||||
const struct X11ClipboardRead read = clearReadNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
XFree(data);
|
||||
free(read.buffer);
|
||||
app_clipboardAbort(read.request);
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t required = x11cb.read.size + itemCount;
|
||||
if (required > x11cb.read.capacity)
|
||||
{
|
||||
size_t capacity = x11cb.read.capacity;
|
||||
while (capacity < required)
|
||||
{
|
||||
if (capacity > SIZE_MAX / 2)
|
||||
{
|
||||
capacity = required;
|
||||
break;
|
||||
}
|
||||
capacity *= 2;
|
||||
}
|
||||
|
||||
uint8_t * buffer = realloc(x11cb.read.buffer, capacity);
|
||||
if (!buffer)
|
||||
{
|
||||
const struct X11ClipboardRead read = clearReadNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
XFree(data);
|
||||
free(read.buffer);
|
||||
app_clipboardAbort(read.request);
|
||||
return;
|
||||
}
|
||||
x11cb.read.buffer = buffer;
|
||||
x11cb.read.capacity = capacity;
|
||||
}
|
||||
|
||||
memcpy(x11cb.read.buffer + x11cb.read.size, data, itemCount);
|
||||
x11cb.read.size += itemCount;
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
if (data)
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e)
|
||||
{
|
||||
// check if the selection is valid and it isn't ourself
|
||||
if (e.selection != x11atoms.CLIPBOARD ||
|
||||
e.owner == x11.window || e.owner == 0)
|
||||
if (e.selection != x11atoms.CLIPBOARD || e.owner == x11.window)
|
||||
return;
|
||||
|
||||
LG_LOCK(x11cb.lock);
|
||||
XGrabServer(x11.display);
|
||||
if (XGetSelectionOwner(x11.display, x11atoms.CLIPBOARD) != e.owner)
|
||||
{
|
||||
XUngrabServer(x11.display);
|
||||
XFlush(x11.display);
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
return;
|
||||
}
|
||||
|
||||
const LG_ClipboardRequest oldRequest = x11cb.read.window ?
|
||||
cancelReadNL() : LG_CLIPBOARD_REQUEST_INVALID;
|
||||
clearTargetsNL();
|
||||
|
||||
if (e.owner == 0)
|
||||
{
|
||||
x11cb.aCurSelection = BadValue;
|
||||
XUngrabServer(x11.display);
|
||||
XFlush(x11.display);
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
if (oldRequest != LG_CLIPBOARD_REQUEST_INVALID)
|
||||
app_clipboardAbort(oldRequest);
|
||||
app_clipboardRelease();
|
||||
return;
|
||||
}
|
||||
|
||||
// remember which selection we are working with
|
||||
x11cb.aCurSelection = e.selection;
|
||||
x11cb.targetsWindow = XCreateSimpleWindow(
|
||||
x11.display, x11.window, 0, 0, 1, 1, 0, 0, 0);
|
||||
if (!x11cb.targetsWindow)
|
||||
{
|
||||
x11cb.aCurSelection = BadValue;
|
||||
XUngrabServer(x11.display);
|
||||
XFlush(x11.display);
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
if (oldRequest != LG_CLIPBOARD_REQUEST_INVALID)
|
||||
app_clipboardAbort(oldRequest);
|
||||
app_clipboardRelease();
|
||||
return;
|
||||
}
|
||||
|
||||
XConvertSelection(
|
||||
x11.display,
|
||||
e.selection,
|
||||
x11atoms.TARGETS,
|
||||
x11atoms.TARGETS,
|
||||
x11.window,
|
||||
x11cb.targetsWindow,
|
||||
CurrentTime);
|
||||
XUngrabServer(x11.display);
|
||||
XFlush(x11.display);
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
|
||||
return;
|
||||
if (oldRequest != LG_CLIPBOARD_REQUEST_INVALID)
|
||||
app_clipboardAbort(oldRequest);
|
||||
}
|
||||
|
||||
static void x11CBSelectionNotify(const XSelectionEvent e)
|
||||
{
|
||||
if (e.property == None)
|
||||
return;
|
||||
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long itemCount, after;
|
||||
unsigned char *data;
|
||||
unsigned long itemCount;
|
||||
unsigned long after;
|
||||
unsigned char * data = NULL;
|
||||
|
||||
LG_LOCK(x11cb.lock);
|
||||
const bool targetReply = x11cb.targetsWindow &&
|
||||
e.requestor == x11cb.targetsWindow && e.target == x11atoms.TARGETS;
|
||||
if (targetReply)
|
||||
{
|
||||
if (e.property == None)
|
||||
{
|
||||
clearTargetsNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
app_clipboardRelease();
|
||||
return;
|
||||
}
|
||||
|
||||
if (XGetWindowProperty(
|
||||
e.display,
|
||||
e.requestor,
|
||||
e.property,
|
||||
0, ~0L, // start and length
|
||||
True, // delete the property
|
||||
AnyPropertyType,
|
||||
&type,
|
||||
&format,
|
||||
&itemCount,
|
||||
&after,
|
||||
&data) != Success)
|
||||
{
|
||||
clearTargetsNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
app_clipboardRelease();
|
||||
return;
|
||||
}
|
||||
clearTargetsNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
|
||||
// the format is 32-bit and we must have data
|
||||
// this is technically incorrect however as it's
|
||||
// an array of padded 64-bit values
|
||||
if (!data || format != 32)
|
||||
{
|
||||
app_clipboardRelease();
|
||||
goto out;
|
||||
}
|
||||
|
||||
int typeCount = 0;
|
||||
LG_ClipboardData types[LG_CLIPBOARD_DATA_NONE];
|
||||
|
||||
// see if we support any of the targets listed
|
||||
const Atom * targets = (const Atom *)data;
|
||||
for(int n = 0; n < LG_CLIPBOARD_DATA_NONE; ++n)
|
||||
for(unsigned long i = 0; i < itemCount; ++i)
|
||||
if (x11cb.aTypes[n] == targets[i])
|
||||
{
|
||||
types[typeCount++] = n;
|
||||
break;
|
||||
}
|
||||
|
||||
app_clipboardNotifyTypes(types, typeCount);
|
||||
goto out;
|
||||
}
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
|
||||
LG_LOCK(x11cb.lock);
|
||||
if (!x11cb.read.window || e.requestor != x11cb.read.window)
|
||||
{
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.property == None)
|
||||
{
|
||||
const LG_ClipboardRequest request = cancelReadNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
app_clipboardAbort(request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (XGetWindowProperty(
|
||||
e.display,
|
||||
@@ -343,60 +536,65 @@ static void x11CBSelectionNotify(const XSelectionEvent e)
|
||||
&after,
|
||||
&data) != Success)
|
||||
{
|
||||
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
|
||||
goto out;
|
||||
const LG_ClipboardRequest request = cancelReadNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
app_clipboardAbort(request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == x11atoms.INCR)
|
||||
{
|
||||
x11cb.incrStart = true;
|
||||
x11cb.lowerBound = *(unsigned int *)data;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// the target list
|
||||
if (e.property == x11atoms.TARGETS)
|
||||
{
|
||||
// the format is 32-bit and we must have data
|
||||
// this is technically incorrect however as it's
|
||||
// an array of padded 64-bit values
|
||||
if (!data || format != 32)
|
||||
goto out;
|
||||
|
||||
int typeCount = 0;
|
||||
LG_ClipboardData types[itemCount];
|
||||
|
||||
// see if we support any of the targets listed
|
||||
const uint64_t * targets = (const uint64_t *)data;
|
||||
for(unsigned long i = 0; i < itemCount; ++i)
|
||||
if (!data || format != 32 || itemCount < 1)
|
||||
{
|
||||
for(int n = 0; n < LG_CLIPBOARD_DATA_NONE; ++n)
|
||||
if (x11cb.aTypes[n] == targets[i])
|
||||
types[typeCount++] = n;
|
||||
const LG_ClipboardRequest request = cancelReadNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
if (data)
|
||||
XFree(data);
|
||||
app_clipboardAbort(request);
|
||||
return;
|
||||
}
|
||||
|
||||
app_clipboardNotifyTypes(types, typeCount);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (e.property == x11atoms.SEL_DATA)
|
||||
{
|
||||
LG_ClipboardData dataType;
|
||||
for(dataType = 0; dataType < LG_CLIPBOARD_DATA_NONE; ++dataType)
|
||||
if (x11cb.aTypes[dataType] == type)
|
||||
break;
|
||||
|
||||
if (dataType == LG_CLIPBOARD_DATA_NONE)
|
||||
size_t capacity = *(const unsigned long *)data;
|
||||
if (capacity < 4096)
|
||||
capacity = 4096;
|
||||
if (capacity > 1048576)
|
||||
capacity = 1048576;
|
||||
x11cb.read.buffer = malloc(capacity);
|
||||
if (!x11cb.read.buffer)
|
||||
{
|
||||
DEBUG_WARN("clipboard data (%s) not in a supported format",
|
||||
XGetAtomName(x11.display, type));
|
||||
goto out;
|
||||
const LG_ClipboardRequest request = cancelReadNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
XFree(data);
|
||||
app_clipboardAbort(request);
|
||||
return;
|
||||
}
|
||||
x11cb.read.capacity = capacity;
|
||||
x11cb.read.incremental = true;
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
XFree(data);
|
||||
return;
|
||||
}
|
||||
|
||||
app_clipboardData(dataType, data, itemCount);
|
||||
LG_ClipboardData dataType;
|
||||
for(dataType = 0; dataType < LG_CLIPBOARD_DATA_NONE; ++dataType)
|
||||
if (x11cb.aTypes[dataType] == type)
|
||||
break;
|
||||
|
||||
const LG_ClipboardRequest request = x11cb.read.request;
|
||||
const bool valid = dataType != LG_CLIPBOARD_DATA_NONE &&
|
||||
dataType == x11cb.read.type && format == 8;
|
||||
cancelReadNL();
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
DEBUG_WARN("Invalid clipboard data");
|
||||
app_clipboardAbort(request);
|
||||
goto out;
|
||||
}
|
||||
|
||||
app_clipboardData(request, dataType, data, itemCount);
|
||||
|
||||
out:
|
||||
if (data)
|
||||
XFree(data);
|
||||
@@ -404,29 +602,79 @@ out:
|
||||
|
||||
void x11CBNotice(LG_ClipboardData type)
|
||||
{
|
||||
x11cb.haveRequest = true;
|
||||
x11cb.type = type;
|
||||
LG_LOCK(x11cb.lock);
|
||||
const LG_ClipboardRequest oldRequest = x11cb.read.window ?
|
||||
cancelReadNL() : LG_CLIPBOARD_REQUEST_INVALID;
|
||||
clearTargetsNL();
|
||||
x11cb.aCurSelection = BadValue;
|
||||
x11cb.haveRequest = true;
|
||||
x11cb.type = type;
|
||||
XSetSelectionOwner(x11.display, x11atoms.CLIPBOARD, x11.window, CurrentTime);
|
||||
XFlush(x11.display);
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
if (oldRequest != LG_CLIPBOARD_REQUEST_INVALID)
|
||||
app_clipboardAbort(oldRequest);
|
||||
}
|
||||
|
||||
void x11CBRelease(void)
|
||||
{
|
||||
LG_LOCK(x11cb.lock);
|
||||
x11cb.haveRequest = false;
|
||||
XSetSelectionOwner(x11.display, x11atoms.CLIPBOARD, None, CurrentTime);
|
||||
XGrabServer(x11.display);
|
||||
if (XGetSelectionOwner(x11.display, x11atoms.CLIPBOARD) == x11.window)
|
||||
XSetSelectionOwner(
|
||||
x11.display, x11atoms.CLIPBOARD, None, CurrentTime);
|
||||
XUngrabServer(x11.display);
|
||||
XFlush(x11.display);
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
}
|
||||
|
||||
void x11CBRequest(LG_ClipboardData type)
|
||||
void x11CBRequest(LG_ClipboardRequest request, LG_ClipboardData type)
|
||||
{
|
||||
if (x11cb.aCurSelection == BadValue)
|
||||
if (request == LG_CLIPBOARD_REQUEST_INVALID ||
|
||||
type < LG_CLIPBOARD_DATA_TEXT || type >= LG_CLIPBOARD_DATA_NONE)
|
||||
return;
|
||||
|
||||
LG_LOCK(x11cb.lock);
|
||||
const LG_ClipboardRequest oldRequest = x11cb.read.window ?
|
||||
cancelReadNL() : LG_CLIPBOARD_REQUEST_INVALID;
|
||||
if (x11cb.aCurSelection == BadValue)
|
||||
{
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
if (oldRequest != LG_CLIPBOARD_REQUEST_INVALID)
|
||||
app_clipboardAbort(oldRequest);
|
||||
app_clipboardAbort(request);
|
||||
return;
|
||||
}
|
||||
|
||||
const Window window = XCreateSimpleWindow(
|
||||
x11.display, x11.window, 0, 0, 1, 1, 0, 0, 0);
|
||||
if (!window)
|
||||
{
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
if (oldRequest != LG_CLIPBOARD_REQUEST_INVALID)
|
||||
app_clipboardAbort(oldRequest);
|
||||
app_clipboardAbort(request);
|
||||
return;
|
||||
}
|
||||
|
||||
XSelectInput(x11.display, window, PropertyChangeMask);
|
||||
x11cb.read = (struct X11ClipboardRead)
|
||||
{
|
||||
.window = window,
|
||||
.request = request,
|
||||
.type = type,
|
||||
};
|
||||
XConvertSelection(
|
||||
x11.display,
|
||||
x11cb.aCurSelection,
|
||||
x11cb.aTypes[type],
|
||||
x11atoms.SEL_DATA,
|
||||
x11.window,
|
||||
window,
|
||||
CurrentTime);
|
||||
XFlush(x11.display);
|
||||
LG_UNLOCK(x11cb.lock);
|
||||
|
||||
if (oldRequest != LG_CLIPBOARD_REQUEST_INVALID)
|
||||
app_clipboardAbort(oldRequest);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,6 @@ bool x11CBEventThread(const XEvent * xe);
|
||||
bool x11CBInit(void);
|
||||
void x11CBNotice(LG_ClipboardData type);
|
||||
void x11CBRelease(void);
|
||||
void x11CBRequest(LG_ClipboardData type);
|
||||
void x11CBRequest(LG_ClipboardRequest request, LG_ClipboardData type);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user