Fix selection timestamp handling problems

* data_device.c (XLClearForeignSelection):
* primary_selection.c (XLClearForeignPrimary): Clear the local
selection as well upon losing selection ownership.
* xdata.c (NoticeClipboardCleared, NoticePrimaryCleared): If a
local selection is in use, don't clear it upon selection clear
events with a timestamp identical to the time at which their
ownership was acquired.  Explain why in a comment.
(HandleSelectionNotify): Always use the client time.
This commit is contained in:
hujianwei 2022-10-22 07:40:26 +00:00
parent 3fe08b5c7c
commit 6e062e2e2f
3 changed files with 59 additions and 9 deletions

View file

@ -1210,7 +1210,15 @@ XLClearForeignSelection (Timestamp time)
if (current_selection_data == &foreign_selection_key) if (current_selection_data == &foreign_selection_key)
{ {
current_selection_data = NULL; current_selection_data = NULL;
SendDataOffers ();
}
else if (current_selection_data)
{
/* The foreign selection was cleared, meaning CLIPBOARD has been
disowned after the last foreign or local selection was set,
and thus the local selection must be cleared as well. */
wl_data_source_send_cancelled (current_selection_data->resource);
current_selection_data = NULL;
SendDataOffers (); SendDataOffers ();
} }

View file

@ -756,6 +756,8 @@ XLSetForeignPrimary (Timestamp time, CreateOfferFuncs functions)
void void
XLClearForeignPrimary (Timestamp time) XLClearForeignPrimary (Timestamp time)
{ {
struct wl_resource *scratch;
if (TimestampIs (time, Earlier, foreign_selection_time)) if (TimestampIs (time, Earlier, foreign_selection_time))
return; return;
@ -764,6 +766,19 @@ XLClearForeignPrimary (Timestamp time)
primary_selection = NULL; primary_selection = NULL;
NoticeChanged (); NoticeChanged ();
} }
else if (primary_selection)
{
/* The foreign selection was cleared; that means PRIMARY has
been disowned after the last foreign or local selection was
set, which means the local selection must be cleared as
well. */
scratch = primary_selection->resource;
zwp_primary_selection_source_v1_send_cancelled (scratch);
/* Clear the primary selection and notice a change. */
primary_selection = NULL;
NoticeChanged ();
}
foreign_selection_time = time; foreign_selection_time = time;
} }

43
xdata.c
View file

@ -1093,13 +1093,32 @@ NoticePrimaryChanged (Time time)
static void static void
NoticeClipboardCleared (Time time) NoticeClipboardCleared (Time time)
{ {
/* Ignore outdated events. */ /* Ignore outdated events. time cannot simply be compared against
if (TimeIs (time, Earlier, last_x_selection_change)) the selection change time, because some clients do this:
- data_device.set_selection (data_source, N)
- data_source.delete (data_source)
- data_device.set_selection (data_source, N)
Which results in the selection being disowned at the same time as
the last selection change time, but then re-owned immediately
afterwards, leading to an out-of-date SelectionNotify event with
owner None having a timestamp equal to that time.
Thus, only timestamps after last_x_selection_change are
considered as valid here if x_selection_targets is NULL. */
if (!x_selection_targets)
{
if (!TimeIs (time, Later, last_x_selection_change))
return;
}
else if (TimeIs (time, Earlier, last_x_selection_change))
return; return;
DebugPrint ("CLIPBOARD was cleared at %lu\n", time); DebugPrint ("CLIPBOARD was cleared at %lu\n", time);
last_x_selection_change = TimestampFromServerTime (time); last_x_selection_change = TimestampFromClientTime (time);
XLClearForeignSelection (last_x_selection_change); XLClearForeignSelection (last_x_selection_change);
/* Free data that is no longer used. */ /* Free data that is no longer used. */
@ -1111,13 +1130,17 @@ NoticeClipboardCleared (Time time)
static void static void
NoticePrimaryCleared (Time time) NoticePrimaryCleared (Time time)
{ {
/* Ignore outdated events. */ if (!x_primary_targets)
if (TimeIs (time, Earlier, last_primary_time)) {
if (!TimeIs (time, Later, last_primary_time))
return;
}
else if (TimeIs (time, Earlier, last_primary_time))
return; return;
DebugPrint ("PRIMARY was cleared at %lu\n", time); DebugPrint ("PRIMARY was cleared at %lu\n", time);
last_primary_time = TimestampFromServerTime (time); last_primary_time = TimestampFromClientTime (time);
XLClearForeignPrimary (last_primary_time); XLClearForeignPrimary (last_primary_time);
/* Free data that is no longer used. */ /* Free data that is no longer used. */
@ -1133,17 +1156,21 @@ HandleSelectionNotify (XFixesSelectionNotifyEvent *event)
/* Ignore events sent when we get selection ownership. */ /* Ignore events sent when we get selection ownership. */
return; return;
/* Use the server timestamp in the fixes selection notify event
to synchronize the local time counter. */
TimestampFromServerTime (event->timestamp);
if (event->selection == CLIPBOARD if (event->selection == CLIPBOARD
&& TimeIs (event->selection_timestamp, Later, last_clipboard_change)) && TimeIs (event->selection_timestamp, Later, last_clipboard_change))
/* This time is used to keep track of whether or not things like /* This time is used to keep track of whether or not things like
disowning the selection were successful. */ disowning the selection were successful. */
last_clipboard_change last_clipboard_change
= TimestampFromServerTime (event->selection_timestamp); = TimestampFromClientTime (event->selection_timestamp);
if (event->selection == XA_PRIMARY if (event->selection == XA_PRIMARY
&& TimeIs (event->selection_timestamp, Later, last_primary_time)) && TimeIs (event->selection_timestamp, Later, last_primary_time))
last_primary_time last_primary_time
= TimestampFromServerTime (event->selection_timestamp); = TimestampFromClientTime (event->selection_timestamp);
if (event->owner != None if (event->owner != None
&& event->selection == CLIPBOARD) && event->selection == CLIPBOARD)