From 6e062e2e2f11e19a9b9637ac5d24487d496bd05e Mon Sep 17 00:00:00 2001 From: hujianwei Date: Sat, 22 Oct 2022 07:40:26 +0000 Subject: [PATCH] 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. --- data_device.c | 10 +++++++++- primary_selection.c | 15 +++++++++++++++ xdata.c | 43 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/data_device.c b/data_device.c index 469bafc..544b937 100644 --- a/data_device.c +++ b/data_device.c @@ -1210,7 +1210,15 @@ XLClearForeignSelection (Timestamp time) if (current_selection_data == &foreign_selection_key) { 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 (); } diff --git a/primary_selection.c b/primary_selection.c index aee1b0d..70429c8 100644 --- a/primary_selection.c +++ b/primary_selection.c @@ -756,6 +756,8 @@ XLSetForeignPrimary (Timestamp time, CreateOfferFuncs functions) void XLClearForeignPrimary (Timestamp time) { + struct wl_resource *scratch; + if (TimestampIs (time, Earlier, foreign_selection_time)) return; @@ -764,6 +766,19 @@ XLClearForeignPrimary (Timestamp time) primary_selection = NULL; 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; } diff --git a/xdata.c b/xdata.c index 8eb7ef1..57dace6 100644 --- a/xdata.c +++ b/xdata.c @@ -1093,13 +1093,32 @@ NoticePrimaryChanged (Time time) static void NoticeClipboardCleared (Time time) { - /* Ignore outdated events. */ - if (TimeIs (time, Earlier, last_x_selection_change)) + /* Ignore outdated events. time cannot simply be compared against + 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; 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); /* Free data that is no longer used. */ @@ -1111,13 +1130,17 @@ NoticeClipboardCleared (Time time) static void NoticePrimaryCleared (Time time) { - /* Ignore outdated events. */ - if (TimeIs (time, Earlier, last_primary_time)) + if (!x_primary_targets) + { + if (!TimeIs (time, Later, last_primary_time)) + return; + } + else if (TimeIs (time, Earlier, last_primary_time)) return; DebugPrint ("PRIMARY was cleared at %lu\n", time); - last_primary_time = TimestampFromServerTime (time); + last_primary_time = TimestampFromClientTime (time); XLClearForeignPrimary (last_primary_time); /* Free data that is no longer used. */ @@ -1133,17 +1156,21 @@ HandleSelectionNotify (XFixesSelectionNotifyEvent *event) /* Ignore events sent when we get selection ownership. */ return; + /* Use the server timestamp in the fixes selection notify event + to synchronize the local time counter. */ + TimestampFromServerTime (event->timestamp); + if (event->selection == CLIPBOARD && TimeIs (event->selection_timestamp, Later, last_clipboard_change)) /* This time is used to keep track of whether or not things like disowning the selection were successful. */ last_clipboard_change - = TimestampFromServerTime (event->selection_timestamp); + = TimestampFromClientTime (event->selection_timestamp); if (event->selection == XA_PRIMARY && TimeIs (event->selection_timestamp, Later, last_primary_time)) last_primary_time - = TimestampFromServerTime (event->selection_timestamp); + = TimestampFromClientTime (event->selection_timestamp); if (event->owner != None && event->selection == CLIPBOARD)