mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
[client] clipboard: retain local Wayland offers
This commit is contained in:
@@ -194,7 +194,6 @@ static bool hasImageMimetype(char ** mimetypes)
|
|||||||
|
|
||||||
enum ClipboardInvalidateMode
|
enum ClipboardInvalidateMode
|
||||||
{
|
{
|
||||||
CLIPBOARD_INVALIDATE_SILENT,
|
|
||||||
CLIPBOARD_INVALIDATE_CONDITIONAL,
|
CLIPBOARD_INVALIDATE_CONDITIONAL,
|
||||||
CLIPBOARD_INVALIDATE_FORCE,
|
CLIPBOARD_INVALIDATE_FORCE,
|
||||||
};
|
};
|
||||||
@@ -339,8 +338,9 @@ static void dataDeviceHandleSelection(void * opaque,
|
|||||||
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||||
free(extra->mimetypes[i]);
|
free(extra->mimetypes[i]);
|
||||||
free(extra);
|
free(extra);
|
||||||
/* Publishing already retired the old offer, so a delayed self-copy echo
|
/* A self-copy offer only echoes the source that we published. It must not
|
||||||
* must not invalidate a newer local selection. */
|
* release the provider or disturb the external offer for a generation
|
||||||
|
* which the guest may still request. */
|
||||||
if (!selfCopy)
|
if (!selfCopy)
|
||||||
waylandCBInvalidateLocal(CLIPBOARD_INVALIDATE_FORCE);
|
waylandCBInvalidateLocal(CLIPBOARD_INVALIDATE_FORCE);
|
||||||
wl_data_offer_destroy(offer);
|
wl_data_offer_destroy(offer);
|
||||||
@@ -664,6 +664,12 @@ static void waylandCBInvalidateLocal(enum ClipboardInvalidateMode mode)
|
|||||||
{
|
{
|
||||||
char * mimetypes[LG_CLIPBOARD_DATA_NONE];
|
char * mimetypes[LG_CLIPBOARD_DATA_NONE];
|
||||||
LG_LOCK(wlCb.lock);
|
LG_LOCK(wlCb.lock);
|
||||||
|
const bool remoteSelection = wlCb.selectionSource != NULL;
|
||||||
|
if (mode == CLIPBOARD_INVALIDATE_CONDITIONAL && remoteSelection)
|
||||||
|
{
|
||||||
|
LG_UNLOCK(wlCb.lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
struct ClipboardRead * read = clipboardReadTakeCurrentNL();
|
struct ClipboardRead * read = clipboardReadTakeCurrentNL();
|
||||||
struct wl_data_offer * offer = wlCb.offer;
|
struct wl_data_offer * offer = wlCb.offer;
|
||||||
wlCb.offer = NULL;
|
wlCb.offer = NULL;
|
||||||
@@ -1215,11 +1221,6 @@ static void waylandCBPublish(LG_ClipboardData type)
|
|||||||
wl_data_source_offer(source, *mimetype);
|
wl_data_source_offer(source, *mimetype);
|
||||||
wl_data_source_offer(source, wlCb.lgMimetype);
|
wl_data_source_offer(source, wlCb.lgMimetype);
|
||||||
|
|
||||||
/* Publishing a remote selection supersedes the old external offer. Retire
|
|
||||||
* it before set_selection so a simultaneous focus loss cannot release the
|
|
||||||
* provider while this remote source is active. */
|
|
||||||
waylandCBInvalidateLocal(CLIPBOARD_INVALIDATE_SILENT);
|
|
||||||
|
|
||||||
LG_LOCK(wlCb.lock);
|
LG_LOCK(wlCb.lock);
|
||||||
if (wlCb.dataDevice)
|
if (wlCb.dataDevice)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -485,10 +485,6 @@ static void eventNotice(void * opaque,
|
|||||||
generation = clipboard.remoteGeneration;
|
generation = clipboard.remoteGeneration;
|
||||||
transfer = clearRemoteRequestNL();
|
transfer = clearRemoteRequestNL();
|
||||||
notice = clipboard.localAvailable && g_params.clipboardToLocal;
|
notice = clipboard.localAvailable && g_params.clipboardToLocal;
|
||||||
/* Once published, the remote notice replaces the local clipboard. Do not
|
|
||||||
* replay that stale local offer if the provider is rebound. */
|
|
||||||
if (notice)
|
|
||||||
clipboard.localTypeCount = 0;
|
|
||||||
LG_UNLOCK(clipboard.stateLock);
|
LG_UNLOCK(clipboard.stateLock);
|
||||||
LG_UNLOCK(clipboard.requestLock);
|
LG_UNLOCK(clipboard.requestLock);
|
||||||
|
|
||||||
@@ -1226,10 +1222,6 @@ void lgClipboard_setLocalAvailable(bool available)
|
|||||||
notice = available && clipboard.remoteNotice &&
|
notice = available && clipboard.remoteNotice &&
|
||||||
g_params.clipboardToLocal;
|
g_params.clipboardToLocal;
|
||||||
type = clipboard.remoteType;
|
type = clipboard.remoteType;
|
||||||
/* A pending remote notice replaces the local clipboard when it becomes
|
|
||||||
* publishable. Do not replay the superseded local offer afterwards. */
|
|
||||||
if (notice)
|
|
||||||
clipboard.localTypeCount = 0;
|
|
||||||
if (!available)
|
if (!available)
|
||||||
transfer = clearRemoteRequestNL();
|
transfer = clearRemoteRequestNL();
|
||||||
LG_UNLOCK(clipboard.stateLock);
|
LG_UNLOCK(clipboard.stateLock);
|
||||||
|
|||||||
@@ -627,7 +627,7 @@ static void testRequest(void)
|
|||||||
lgClipboard_free();
|
lgClipboard_free();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testRemoteReplacesLocal(void)
|
static void testRemoteKeepsLocalRequest(void)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
bind(&p);
|
bind(&p);
|
||||||
@@ -651,17 +651,17 @@ static void testRemoteReplacesLocal(void)
|
|||||||
notice(&q, types, 1);
|
notice(&q, types, 1);
|
||||||
CHECK(d.notice == 1);
|
CHECK(d.notice == 1);
|
||||||
|
|
||||||
/* Rebinding after a remote notice must not replay the superseded local
|
/* The two clipboard directions have independent generations. A late
|
||||||
* offer back to the provider. */
|
* request can still refer to the local offer advertised before this remote
|
||||||
lgClipboard_setTransport(&plainOps, &r);
|
* notice, so its type must remain serviceable. */
|
||||||
CHECK(r.attach == 1);
|
CHECK(q.ev->request(q.evCtx, 43, LG_CLIPBOARD_DATA_TEXT));
|
||||||
CHECK(r.notice == 0);
|
CHECK(d.request == 1);
|
||||||
CHECK(r.release == 1);
|
CHECK(d.reqType[0] == LG_CLIPBOARD_DATA_TEXT);
|
||||||
|
|
||||||
lgClipboard_free();
|
lgClipboard_free();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testPendingRemoteReplacesLocal(void)
|
static void testPendingRemoteKeepsLocalRequest(void)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
bind(&p);
|
bind(&p);
|
||||||
@@ -677,10 +677,11 @@ static void testPendingRemoteReplacesLocal(void)
|
|||||||
lgClipboard_setLocalAvailable(true);
|
lgClipboard_setLocalAvailable(true);
|
||||||
CHECK(d.notice == 1);
|
CHECK(d.notice == 1);
|
||||||
|
|
||||||
lgClipboard_setTransport(&plainOps, &q);
|
/* Publishing a notice which arrived while the local clipboard was
|
||||||
CHECK(q.attach == 1);
|
* unavailable must likewise retain the local request allow-list. */
|
||||||
CHECK(q.notice == 0);
|
CHECK(p.ev->request(p.evCtx, 44, LG_CLIPBOARD_DATA_TEXT));
|
||||||
CHECK(q.release == 1);
|
CHECK(d.request == 1);
|
||||||
|
CHECK(d.reqType[0] == LG_CLIPBOARD_DATA_TEXT);
|
||||||
|
|
||||||
lgClipboard_free();
|
lgClipboard_free();
|
||||||
}
|
}
|
||||||
@@ -1129,8 +1130,8 @@ static const struct Test tests[] =
|
|||||||
{
|
{
|
||||||
{ "preference", testPreference },
|
{ "preference", testPreference },
|
||||||
{ "request" , testRequest },
|
{ "request" , testRequest },
|
||||||
{ "remote-local", testRemoteReplacesLocal },
|
{ "remote-local", testRemoteKeepsLocalRequest },
|
||||||
{ "pending-remote", testPendingRemoteReplacesLocal },
|
{ "pending-remote", testPendingRemoteKeepsLocalRequest },
|
||||||
{ "invalid" , testInvalid },
|
{ "invalid" , testInvalid },
|
||||||
{ "cancel" , testCancel },
|
{ "cancel" , testCancel },
|
||||||
{ "generation", testGeneration },
|
{ "generation", testGeneration },
|
||||||
|
|||||||
@@ -706,12 +706,22 @@ static void testSelfCopy(void)
|
|||||||
waylandCBNotice(LG_CLIPBOARD_DATA_TEXT);
|
waylandCBNotice(LG_CLIPBOARD_DATA_TEXT);
|
||||||
struct Proxy * source = &proto.source[0];
|
struct Proxy * source = &proto.source[0];
|
||||||
CHECK(wlCb.selectionSource == (struct wl_data_source *)source);
|
CHECK(wlCb.selectionSource == (struct wl_data_source *)source);
|
||||||
|
CHECK(wlCb.offer == (struct wl_data_offer *)local);
|
||||||
|
CHECK(!local->dead);
|
||||||
|
|
||||||
|
/* A remote notice and its self echo must not erase the Wayland offer for a
|
||||||
|
* local clipboard generation that Windows can still request. */
|
||||||
|
const uint8_t localData[] = "local clipboard";
|
||||||
|
proto.receiveData = localData;
|
||||||
|
proto.receiveSize = sizeof(localData) - 1;
|
||||||
|
|
||||||
/* Losing focus before the compositor echoes our source must not release
|
/* Losing focus before the compositor echoes our source must not release
|
||||||
* the remote clipboard. */
|
* the remote clipboard. */
|
||||||
waylandCBInvalidate();
|
waylandCBInvalidate();
|
||||||
CHECK(rec.releaseN == 0);
|
CHECK(rec.releaseN == 0);
|
||||||
CHECK(wlCb.selectionSource == (struct wl_data_source *)source);
|
CHECK(wlCb.selectionSource == (struct wl_data_source *)source);
|
||||||
|
CHECK(wlCb.offer == (struct wl_data_offer *)local);
|
||||||
|
CHECK(!local->dead);
|
||||||
|
|
||||||
struct Proxy * echo = newOffer();
|
struct Proxy * echo = newOffer();
|
||||||
offerMime(echo, "text/plain");
|
offerMime(echo, "text/plain");
|
||||||
@@ -721,17 +731,28 @@ static void testSelfCopy(void)
|
|||||||
CHECK(rec.noticeN == 1);
|
CHECK(rec.noticeN == 1);
|
||||||
CHECK(rec.releaseN == 0);
|
CHECK(rec.releaseN == 0);
|
||||||
CHECK(wlCb.selectionSource == (struct wl_data_source *)source);
|
CHECK(wlCb.selectionSource == (struct wl_data_source *)source);
|
||||||
CHECK(!wlCb.offer);
|
CHECK(wlCb.offer == (struct wl_data_offer *)local);
|
||||||
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
CHECK(wlCb.mimetypes[LG_CLIPBOARD_DATA_TEXT]);
|
||||||
CHECK(!wlCb.mimetypes[i]);
|
CHECK(!local->dead);
|
||||||
CHECK(local->dead);
|
|
||||||
CHECK(echo->dead);
|
CHECK(echo->dead);
|
||||||
CHECK(proto.offerDestroyN == 2);
|
CHECK(proto.offerDestroyN == 1);
|
||||||
CHECK(!echo->user);
|
CHECK(!echo->user);
|
||||||
|
|
||||||
waylandCBInvalidate();
|
waylandCBInvalidate();
|
||||||
CHECK(rec.releaseN == 0);
|
CHECK(rec.releaseN == 0);
|
||||||
CHECK(wlCb.selectionSource == (struct wl_data_source *)source);
|
CHECK(wlCb.selectionSource == (struct wl_data_source *)source);
|
||||||
|
CHECK(wlCb.offer == (struct wl_data_offer *)local);
|
||||||
|
|
||||||
|
waylandCBRequest(102, LG_CLIPBOARD_DATA_TEXT);
|
||||||
|
CHECK(proto.receiveN == 1);
|
||||||
|
CHECK(rec.pollN == 1);
|
||||||
|
pollFire(0, EPOLLIN);
|
||||||
|
pollFire(0, EPOLLIN);
|
||||||
|
CHECK(rec.streamData.request == 102);
|
||||||
|
CHECK(rec.streamData.size == sizeof(localData) - 1);
|
||||||
|
CHECK(rec.streamData.endN == 1);
|
||||||
|
CHECK(memcmp(rec.streamData.data, localData,
|
||||||
|
sizeof(localData) - 1) == 0);
|
||||||
|
|
||||||
int fds[2];
|
int fds[2];
|
||||||
CHECK(pipe(fds) == 0);
|
CHECK(pipe(fds) == 0);
|
||||||
@@ -754,6 +775,8 @@ static void testSelfCopy(void)
|
|||||||
selectOffer(NULL);
|
selectOffer(NULL);
|
||||||
CHECK(rec.releaseN == 1);
|
CHECK(rec.releaseN == 1);
|
||||||
CHECK(!wlCb.selectionSource);
|
CHECK(!wlCb.selectionSource);
|
||||||
|
CHECK(local->dead);
|
||||||
|
CHECK(proto.offerDestroyN == 2);
|
||||||
|
|
||||||
CHECK(proto.dirtyDestroyN == 0);
|
CHECK(proto.dirtyDestroyN == 0);
|
||||||
finish();
|
finish();
|
||||||
|
|||||||
Reference in New Issue
Block a user