Improve reliability of timestamp handling

* 12to11.c (DetermineServerTime): Correctly handle server time
truncation.
* Imakefile (SRCS): Add time.c
(OBJS): Add time.o.
* compositor.h (struct _Timestamp, enum _TimestampDifference):
New structure and enum.
(TimestampIs, TimeIs): New macros.
(struct _CreateOfferFuncs): Accept Timestamp, not Time.
* data_device.c (UpdateSingleReferenceWithForeignOffer)
(XLSetForeignSelection, XLClearForeignSelection): Accept
Timestamp, not Time.
* dnd.c (XLDoDragMotion):
* picture_renderer.c (InitRenderFuncs):
* primary_selection.c (UpdateSingleReferenceWithForeignOffer)
(XLSetForeignPrimary, XLClearForeignPrimary): Adjust for changed
timestamp handling.
* seat.c (struct _Seat): New field `last_user_time'.
(HandleRawKey, DispatchMotion): Record last user time as
timestamp.
(GetLastUserTime, XLSeatGetLastUserTime): Return Timestamp, not
Time.
* select.c (FindWriteTransfer, TransferFinished)
(TransferBecameReadable, ConvertSelectionMultiple)
(HandleSelectionRequest, DrainQueuedTransfers)
(HandlePropertyDelete, HandlePropertyNotify): Allow duplicate
write transfers if the other is only waiting for property
deletion.
* xdata.c (ReceiveBody, HandleOfferResourceDestroy)
(AllocateTimestamp, CreateOffer, CreatePrimaryOffer, SendOffers)
(SendPrimaryOffers, HandleNewSelection, NoticeClipboardCleared)
(NoticePrimaryCleared, HandleSelectionNotify)
(XLNoteSourceDestroyed, NoteLocalSelectionBody)
(NoteLocalSelectionFooter): Adjust timestamp handling to use
wraparound-safe Timestamp and not Time.
This commit is contained in:
oldosfan 2022-10-16 03:31:52 +00:00
parent 08c70b8e3e
commit 5687fbca6d
10 changed files with 288 additions and 132 deletions

View file

@ -34,7 +34,8 @@ static void
DetermineServerTime (void)
{
Time server_time;
struct timespec clock_spec, server_spec, diff;
struct timespec clock_spec;
uint64_t clock_ms, truncated;
/* Try to determine if the X server time is the same as the
monotonic time. If it is not, certain features such as "active"
@ -42,20 +43,28 @@ DetermineServerTime (void)
clock_gettime (CLOCK_MONOTONIC, &clock_spec);
server_time = XLGetServerTimeRoundtrip ();
server_spec.tv_sec = server_time / 1000;
server_spec.tv_nsec = ((server_time - server_time / 1000 * 1000)
* 1000000);
diff = TimespecSub (server_spec, clock_spec);
/* Convert the monotonic time to milliseconds. */
if (TimespecCmp (diff, MakeTimespec (0, 50000000)) <= 0
|| TimespecCmp (diff, MakeTimespec (0, -50000000)) <= 0)
if (IntMultiplyWrapv (clock_spec.tv_sec, 1000, &clock_ms))
goto overflow;
if (IntAddWrapv (clock_ms, clock_spec.tv_nsec / 1000000,
&clock_ms))
goto overflow;
/* Truncate the time to 32 bits. */
truncated = clock_ms;
/* Compare the clock time with the server time. */
if (llabs ((long long) server_time - (long long) truncated) <= 5)
/* Since the difference between the server time and the monotonic
time is less than 50 ms, the server time is the monotonic
time. */
time is less than 5 ms, the server time is most likely the
monotonic time. */
compositor.server_time_monotonic = True;
else
{
overflow:
compositor.server_time_monotonic = False;
fprintf (stderr, "Warning: the X server time does not seem to"
" be synchronized with the monotonic time. Multiple"

View file

@ -23,7 +23,8 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \
icon_surface.c primary_selection.c renderer.c \
picture_renderer.c explicit_synchronization.c transform.c \
wp_viewporter.c decoration.c text_input.c \
single_pixel_buffer.c drm_lease.c pointer_constraints.c
single_pixel_buffer.c drm_lease.c pointer_constraints.c \
time.c
OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
surface.o region.o shm.o atoms.o subcompositor.o positioner.o \
@ -33,7 +34,8 @@ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
icon_surface.o primary_selection.o renderer.o \
picture_renderer.o explicit_synchronization.o transform.o \
wp_viewporter.o decoration.o text_input.o \
single_pixel_buffer.o drm_lease.o pointer_constraints.o
single_pixel_buffer.o drm_lease.o pointer_constraints.o \
time.o
GENHEADERS = transfer_atoms.h

View file

@ -103,6 +103,35 @@ typedef struct _ViewportExt ViewportExt;
extern Compositor compositor;
/* Defined in time.c. */
typedef struct _Timestamp Timestamp;
typedef enum _TimestampDifference TimestampDifference;
struct _Timestamp
{
/* Number of server months passed. */
unsigned int months;
/* Millisecond time into those months. */
unsigned int milliseconds;
};
enum _TimestampDifference
{
Earlier,
Same,
Later,
};
extern Timestamp TimestampFromServerTime (Time);
extern Timestamp TimestampFromClientTime (Time);
extern TimestampDifference CompareTimestamps (Timestamp, Timestamp);
extern TimestampDifference CompareTimeWith (Time, Timestamp);
#define TimestampIs(a, op, b) (CompareTimestamps ((a), (b)) == (op))
#define TimeIs(a, op, b) (CompareTimeWith ((a), (b)) == (op))
/* Defined in renderer.c. */
typedef struct _RenderFuncs RenderFuncs;
@ -1301,12 +1330,16 @@ typedef struct _DataSource DataSource;
typedef struct _CreateOfferFuncs CreateOfferFuncs;
typedef struct _DndOfferFuncs DndOfferFuncs;
typedef struct wl_resource *(*CreateOfferFunc) (struct wl_client *, Time);
typedef void (*SendDataFunc) (struct wl_resource *, Time);
typedef struct wl_resource *(*CreateOfferFunc) (struct wl_client *,
Timestamp);
typedef void (*SendDataFunc) (struct wl_resource *, Timestamp);
struct _CreateOfferFuncs
{
/* Function called to create data offers. */
CreateOfferFunc create_offer;
/* Function called to send data offers. */
SendDataFunc send_offers;
};
@ -1321,8 +1354,8 @@ extern void XLRetainDataDevice (DataDevice *);
extern void XLReleaseDataDevice (DataDevice *);
extern void XLDataDeviceClearSeat (DataDevice *);
extern void XLDataDeviceHandleFocusChange (DataDevice *);
extern void XLSetForeignSelection (Time, CreateOfferFuncs);
extern void XLClearForeignSelection (Time);
extern void XLSetForeignSelection (Timestamp, CreateOfferFuncs);
extern void XLClearForeignSelection (Timestamp);
extern int XLDataSourceTargetCount (DataSource *);
extern void XLDataSourceGetTargets (DataSource *, Atom *);
extern struct wl_resource *XLResourceFromDataSource (DataSource *);
@ -1391,7 +1424,7 @@ extern Bool XLSeatIsInert (Seat *);
extern Bool XLSeatIsClientFocused (Seat *, struct wl_client *);
extern Surface *XLSeatGetFocus (Seat *);
extern void XLSeatShowWindowMenu (Seat *, Surface *, int, int);
extern Time XLSeatGetLastUserTime (Seat *);
extern Timestamp XLSeatGetLastUserTime (Seat *);
extern void XLSeatBeginDrag (Seat *, DataSource *, Surface *,
Surface *, uint32_t);
extern DataSource *XLSeatGetDragDataSource (Seat *);
@ -1507,8 +1540,8 @@ extern Bool XLIsWindowIconSurface (Window);
/* Defined in primary_selection.c. */
extern void XLInitPrimarySelection (void);
extern void XLSetForeignPrimary (Time, CreateOfferFuncs);
extern void XLClearForeignPrimary (Time);
extern void XLSetForeignPrimary (Timestamp, CreateOfferFuncs);
extern void XLClearForeignPrimary (Timestamp);
extern Bool XLNoteLocalPrimary (Seat *, PDataSource *);
extern void XLPrimarySelectionHandleFocusChange (Seat *);
extern struct wl_resource *XLResourceFromPDataSource (PDataSource *);

View file

@ -163,7 +163,7 @@ static DataSource foreign_selection_key;
static CreateOfferFuncs foreign_selection_functions;
/* Time the foreign selection was changed. */
static Time foreign_selection_time;
static Timestamp foreign_selection_time;
/* When it changed. */
static uint32_t last_selection_change_serial;
@ -759,7 +759,7 @@ UpdateSingleReferenceWithForeignOffer (struct wl_client *client,
DataDeviceReference *reference)
{
struct wl_resource *resource;
Time time;
Timestamp time;
time = foreign_selection_time;
resource = foreign_selection_functions.create_offer (client, time);
@ -1173,11 +1173,11 @@ XLDataDeviceHandleFocusChange (DataDevice *device)
}
void
XLSetForeignSelection (Time time, CreateOfferFuncs functions)
XLSetForeignSelection (Timestamp time, CreateOfferFuncs functions)
{
uint32_t serial;
if (time < foreign_selection_time)
if (TimestampIs (time, Earlier, foreign_selection_time))
return;
serial = wl_display_next_serial (compositor.wl_display);
@ -1202,9 +1202,9 @@ XLSetForeignSelection (Time time, CreateOfferFuncs functions)
}
void
XLClearForeignSelection (Time time)
XLClearForeignSelection (Timestamp time)
{
if (time < foreign_selection_time)
if (TimestampIs (time, Earlier, foreign_selection_time))
return;
if (current_selection_data == &foreign_selection_key)

4
dnd.c
View file

@ -2920,6 +2920,7 @@ XLDoDragMotion (Seat *seat, double root_x, double root_y)
{
Window toplevel, proxy, self;
int version, proxy_version;
Timestamp timestamp;
if (finish_source || drag_state.flags & PendingDrop)
/* A finish is pending. */
@ -2960,7 +2961,8 @@ XLDoDragMotion (Seat *seat, double root_x, double root_y)
/* Try to own XdndSelection with the last user time. */
if (!(drag_state.flags & SelectionSet))
{
drag_state.timestamp = XLSeatGetLastUserTime (seat);
timestamp = XLSeatGetLastUserTime (seat);
drag_state.timestamp = timestamp.milliseconds;
if (!XLOwnDragSelection (drag_state.timestamp,
XLSeatGetDragDataSource (seat)))

View file

@ -673,8 +673,8 @@ InitRenderFuncs (void)
|| !XPresentQueryVersion (compositor.display,
&major, &minor))
{
fprintf (stderr, "the X presentation extension is not supported"
" by this X server");
fprintf (stderr, "The X presentation extension is not supported"
" by this X server\n");
return False;
}

View file

@ -101,7 +101,7 @@ static PDataSource foreign_selection_key;
static CreateOfferFuncs foreign_selection_functions;
/* The time the foreign selection was set. */
static Time foreign_selection_time;
static Timestamp foreign_selection_time;
/* Forward declaration. */
@ -354,7 +354,7 @@ UpdateSingleReferenceWithForeignOffer (struct wl_client *client,
PDataDevice *reference)
{
struct wl_resource *scratch, *resource;
Time time;
Timestamp time;
time = foreign_selection_time;
resource = foreign_selection_functions.create_offer (client, time);
@ -721,12 +721,12 @@ HandleBind (struct wl_client *client, void *data, uint32_t version,
void
XLSetForeignPrimary (Time time, CreateOfferFuncs functions)
XLSetForeignPrimary (Timestamp time, CreateOfferFuncs functions)
{
uint32_t serial;
struct wl_resource *scratch;
if (time < foreign_selection_time)
if (TimestampIs (time, Earlier, foreign_selection_time))
return;
serial = wl_display_next_serial (compositor.wl_display);
@ -754,9 +754,9 @@ XLSetForeignPrimary (Time time, CreateOfferFuncs functions)
}
void
XLClearForeignPrimary (Time time)
XLClearForeignPrimary (Timestamp time)
{
if (time < foreign_selection_time)
if (TimestampIs (time, Earlier, foreign_selection_time))
return;
if (primary_selection == &foreign_selection_key)

16
seat.c
View file

@ -337,6 +337,9 @@ struct _ModifierChangeCallback
struct _Seat
{
/* The last user time. */
Timestamp last_user_time;
/* wl_global associated with this seat. */
struct wl_global *global;
@ -2367,6 +2370,9 @@ HandleRawKey (XIRawEvent *event)
/* This is used for tracking grabs. */
seat->its_depress_time = event->time;
/* Update the last user time. */
seat->last_user_time = TimestampFromServerTime (event->time);
}
static void
@ -3730,6 +3736,9 @@ DispatchMotion (Subcompositor *subcompositor, XIDeviceEvent *xev)
seat->its_root_y = xev->root_y;
seat->its_press_time = xev->time;
/* Update the last user time. */
seat->last_user_time = TimestampFromServerTime (xev->time);
actual_dispatch = FindSurfaceUnder (subcompositor, xev->event_x,
xev->event_y);
@ -4375,11 +4384,10 @@ IdentifySeat (WhatEdge *edge, uint32_t serial)
return NULL;
}
static Time
static Timestamp
GetLastUserTime (Seat *seat)
{
return MAX (seat->its_press_time,
seat->its_depress_time);
return seat->last_user_time;
}
static Bool
@ -5361,7 +5369,7 @@ XLSeatBeginDrag (Seat *seat, DataSource *data_source, Surface *start_surface,
seat->flags &= ~IsDropped;
}
Time
Timestamp
XLSeatGetLastUserTime (Seat *seat)
{
return GetLastUserTime (seat);

131
select.c
View file

@ -757,19 +757,23 @@ GetTransferFunction (SelectionOwnerInfo *info,
}
static WriteTransfer *
FindWriteTransfer (Window requestor, Atom property)
FindWriteTransfer (Window requestor, Atom property,
int ignore_state)
{
WriteTransfer *transfer;
transfer = write_transfers.next;
transfer = write_transfers.last;
while (transfer != &write_transfers)
{
if (transfer->requestor == requestor
&& transfer->property == property)
&& transfer->property == property
&& (!ignore_state
|| ((transfer->state & ignore_state)
!= ignore_state)))
return transfer;
transfer = transfer->next;
transfer = transfer->last;
}
return NULL;
@ -1036,12 +1040,12 @@ TransferFinished (WriteTransfer *transfer)
transfer if nothing has previously been written. */
|| !(transfer->state & IsFlushed))
{
DebugPrint ("Transfer finished, but there is still property data"
" unwritten (offset %td)\n", transfer->offset);
/* There is still property data left to be written. */
FlushTransfer (transfer, True);
transfer->state |= IsFinished;
DebugPrint ("Transfer finished, but there is still property data"
" unwritten\n");
}
else
/* The transfer is really finished. */
@ -1107,8 +1111,9 @@ TransferBecameReadable (WriteTransfer *transfer)
}
else
{
DebugPrint ("Transfer complete, bytes read as part of EOF: %td, off: %td\n",
transfer->offset, transfer->size);
DebugPrint ("Transfer complete, bytes read as part of EOF: %td, "
"off: %td size: %td\n", bytes_read, transfer->offset,
transfer->size);
transfer->offset += bytes_read;
TransferFinished (transfer);
@ -1239,11 +1244,12 @@ ConvertSelectionMultiple (SelectionOwnerInfo *info, XEvent *event,
DebugPrint ("Verifying MULTIPLE transfer; target = %lu, property = %lu\n",
atoms[i + 0], atoms[i + 1]);
if (FindWriteTransfer (event->xselectionrequest.requestor, atoms[1])
if (FindWriteTransfer (event->xselectionrequest.requestor, atoms[1], 0)
|| FindQueuedTransfer (event->xselectionrequest.requestor, atoms[1]))
{
DebugPrint ("Found ongoing selection transfer with same requestor "
"and property; this MULTIPLE request will have to be queued.\n");
"and property; this MULTIPLE request will have to be "
"queued.\n");
QueueTransfer (event);
@ -1390,7 +1396,7 @@ static Bool
HandleSelectionRequest (XEvent *event)
{
XEvent notify;
WriteTransfer *transfer;
WriteTransfer *transfer, *existing_transfer;
long quantum;
SelectionOwnerInfo *info;
@ -1428,8 +1434,51 @@ HandleSelectionRequest (XEvent *event)
/* If a selection request with the same property and window already
exists, delay this request for later. */
if (FindWriteTransfer (event->xselectionrequest.requestor,
event->xselectionrequest.property)
existing_transfer
= FindWriteTransfer (event->xselectionrequest.requestor,
event->xselectionrequest.property,
/* Ignore write transfers that are finished
but pending property deletion. If the
existing transfer is finished, but we are
still waiting for property deletion, allow
the new transfer to take place. This is
because some very popular programs ask for
TARGETS, and then ask for STRING with the
same property, but only delete the
property for the first request after the
data for the second request arrives. This
is against the ICCCM, as it says:
The requestor should set the property
argument to the name of a property that
the owner can use to report the value of
the selection. Requestors should ensure
that the named property does not exist
on the window before issuing the
ConvertSelection request.
and:
Once all the data in the selection has
been retrieved (which may require
getting the values of several properties
-- see the section called "Use of
Selection Properties". ), the requestor
should delete the property in the
SelectionNotify request by using a
GetProperty request with the delete
argument set to True. As previously
discussed, the owner has no way of
knowing when the data has been
transferred to the requestor unless the
property is removed.
Both paragraphs mean that the property
should have been deleted by the time the
second request is made! */
IsFinished | IsWaitingForDelete);
if (existing_transfer
/* We need to look at the queue too; otherwise, events from the
future might be handled out of order, if the original write
transfer is gone, but some events are still queued. */
@ -1549,9 +1598,9 @@ DrainQueuedTransfers (void)
/* Then, read from the other side of the queue, and handle
everything. */
item = queued_transfers.last;
item = temp.last;
while (item != &queued_transfers)
while (item != &temp)
{
last = item;
item = item->last;
@ -1601,29 +1650,36 @@ HandleSelectionNotify (XEvent *event)
static Bool
HandlePropertyDelete (XEvent *event)
{
WriteTransfer *transfer;
WriteTransfer *transfer, *last;
transfer = FindWriteTransfer (event->xproperty.window,
event->xproperty.atom);
if (!transfer)
return False;
transfer = write_transfers.last;
DebugPrint ("Handling property deletion for %lu\n",
event->xproperty.atom);
DebugPrint ("Handling property deletion for %lu; window %lu\n",
event->xproperty.atom, event->xproperty.window);
while (transfer != &write_transfers)
{
last = transfer->last;
/* There can be multiple finished transfers with the same
property pending deletion if a client not compliant with the
ICCCM is in use. See the large comment in
HandleSelectionRequest. */
DebugPrint ("Handling transfer %p\n", event);
if (transfer->state & IsFinished)
{
DebugPrint ("Completing transfer\n");
/* The transfer is now complete; finish it by freeing its data,
and potentially writing zero-length data. */
/* The transfer is now complete; finish it by freeing its
data, and potentially writing zero-length data. */
FreeTransfer (transfer);
}
else if (transfer->state & IsWaitingForIncr)
{
/* If transfer is waiting for the INCR property to be deleted, mark
it as started, and flush it again to write the first piece of
property data. */
/* If transfer is waiting for the INCR property to be
deleted, mark it as started, and flush it again to write
the first piece of property data. */
DebugPrint ("Starting transfer in response to INCR property deletion\n");
@ -1650,6 +1706,9 @@ HandlePropertyDelete (XEvent *event)
}
}
transfer = last;
}
return True;
}
@ -1658,6 +1717,22 @@ HandlePropertyNotify (XEvent *event)
{
ReadTransfer *transfer;
if (event->xproperty.window != DefaultRootWindow (compositor.display))
/* Xlib selects for PropertyNotifyMask on the root window, which
results in a lot of noise here. */
DebugPrint ("PropertyNotify event:\n"
"serial:\t%lu\n"
"window:\t%lu\n"
"atom:\t%lu\n"
"time:\t%lu\n"
"state:\t%s\n",
event->xproperty.serial,
event->xproperty.window,
event->xproperty.atom,
event->xproperty.time,
(event->xproperty.state == PropertyNewValue
? "PropertyNewValue" : "PropertyDelete"));
if (event->xproperty.state != PropertyNewValue)
return HandlePropertyDelete (event);

109
xdata.c
View file

@ -245,14 +245,14 @@ static DataConversion data_conversions[] =
};
/* The time of the last X selection change. */
static Time last_x_selection_change;
static Timestamp last_x_selection_change;
/* The time ownership was last asserted over CLIPBOARD, and the last
time any client did that. */
static Time last_clipboard_time, last_clipboard_change;
static Timestamp last_clipboard_time, last_clipboard_change;
/* The last time ownership over PRIMARY changed. */
static Time last_primary_time;
static Timestamp last_primary_time;
/* The currently supported selection targets. */
static Atom *x_selection_targets;
@ -688,7 +688,7 @@ PostReceiveDirect (Time time, Atom selection, Atom target, int fd)
static void PostReceiveConversion (Time, Atom, Atom, int);
#define ReceiveBody(selection, primary) \
Time time; \
Timestamp time; \
TargetMapping *translation; \
\
DebugPrint ("Receiving %s from X " #selection " \n", \
@ -696,7 +696,7 @@ static void PostReceiveConversion (Time, Atom, Atom, int);
\
/* Cast to intptr_t to silence warnings when the pointer type is \
larger than long. */ \
time = (Time) (intptr_t) wl_resource_get_user_data (resource); \
time = *(Timestamp *) wl_resource_get_user_data (resource); \
\
/* Find which selection target corresponds to MIME_TYPE. */ \
translation = FindTranslationForMimeType (mime_type, primary); \
@ -705,11 +705,11 @@ static void PostReceiveConversion (Time, Atom, Atom, int);
{ \
if (!translation->translation_func) \
/* If a corresponding target exists, ask to receive it. */ \
PostReceiveDirect (time, selection, \
PostReceiveDirect (time.milliseconds, selection, \
MappingAtom (translation), fd); \
else \
/* Otherwise, use the translation function. */ \
translation->translation_func (time, selection, \
translation->translation_func (time.milliseconds, selection, \
MappingAtom (translation), \
fd); \
} \
@ -753,8 +753,29 @@ static const struct wl_data_offer_interface wl_data_offer_impl =
.set_actions = SetActions,
};
static void
HandleOfferResourceDestroy (struct wl_resource *resource)
{
Timestamp *timestamp;
timestamp = wl_resource_get_user_data (resource);
XLFree (timestamp);
}
static Timestamp *
AllocateTimestamp (Timestamp timestamp)
{
Timestamp *data;
data = XLMalloc (sizeof *data);
*data = timestamp;
return data;
}
static struct wl_resource *
CreateOffer (struct wl_client *client, Time time)
CreateOffer (struct wl_client *client, Timestamp time)
{
struct wl_resource *resource;
@ -768,7 +789,8 @@ CreateOffer (struct wl_client *client, Time time)
/* Otherwise, set the user_data to the time of the selection
change. */
wl_resource_set_implementation (resource, &wl_data_offer_impl,
(void *) time, NULL);
AllocateTimestamp (time),
HandleOfferResourceDestroy);
return resource;
}
@ -786,7 +808,7 @@ static struct zwp_primary_selection_offer_v1_interface primary_offer_impl =
};
static struct wl_resource *
CreatePrimaryOffer (struct wl_client *client, Time time)
CreatePrimaryOffer (struct wl_client *client, Timestamp time)
{
struct wl_resource *resource;
@ -800,7 +822,8 @@ CreatePrimaryOffer (struct wl_client *client, Time time)
/* Otherwise, set the user_data to the time of the selection
change. */
wl_resource_set_implementation (resource, &primary_offer_impl,
(void *) time, NULL);
AllocateTimestamp (time),
HandleOfferResourceDestroy);
return resource;
}
@ -883,9 +906,9 @@ SendOffers1 (struct wl_resource *resource, int ntargets, Atom *targets,
}
static void
SendOffers (struct wl_resource *resource, Time time)
SendOffers (struct wl_resource *resource, Timestamp time)
{
if (time < last_x_selection_change)
if (TimestampIs (time, Earlier, last_x_selection_change))
/* This offer is out of date. */
return;
@ -894,9 +917,9 @@ SendOffers (struct wl_resource *resource, Time time)
}
static void
SendPrimaryOffers (struct wl_resource *resource, Time time)
SendPrimaryOffers (struct wl_resource *resource, Timestamp time)
{
if (time < last_primary_time)
if (TimestampIs (time, Earlier, last_primary_time))
/* This offer is out of date. */
return;
@ -916,7 +939,7 @@ HandleNewSelection (Time time, Atom selection, Atom *targets,
/* The primary selection changed, and now has the given
targets. */
if (time < last_primary_time)
if (TimeIs (time, Earlier, last_primary_time))
{
XLFree (targets);
return;
@ -926,21 +949,21 @@ HandleNewSelection (Time time, Atom selection, Atom *targets,
XLFree (x_primary_targets);
x_primary_targets = targets;
num_x_primary_targets = ntargets;
last_primary_time = time;
last_primary_time = TimestampFromClientTime (time);
/* Add the right functions and set them as the foreign primary
selection handler at TIME. */
funcs.create_offer = CreatePrimaryOffer;
funcs.send_offers = SendPrimaryOffers;
XLSetForeignPrimary (time, funcs);
XLSetForeignPrimary (last_primary_time, funcs);
return;
}
/* Else, the selection that changed is CLIPBOARD. */
/* Ignore outdated selection changes. */
if (time < last_x_selection_change)
if (TimeIs (time, Earlier, last_x_selection_change))
{
/* We are responsible for deallocating targets. */
XLFree (targets);
@ -953,7 +976,7 @@ HandleNewSelection (Time time, Atom selection, Atom *targets,
x_selection_targets = targets;
num_x_selection_targets = ntargets;
last_x_selection_change = time;
last_x_selection_change = TimestampFromClientTime (time);
/* Add the right functions and set them as the foreign selection
handler at TIME. */
@ -961,7 +984,7 @@ HandleNewSelection (Time time, Atom selection, Atom *targets,
funcs.create_offer = CreateOffer;
funcs.send_offers = SendOffers;
XLSetForeignSelection (time, funcs);
XLSetForeignSelection (last_x_selection_change, funcs);
}
static void
@ -1071,13 +1094,13 @@ static void
NoticeClipboardCleared (Time time)
{
/* Ignore outdated events. */
if (time < last_x_selection_change)
if (TimeIs (time, Earlier, last_x_selection_change))
return;
DebugPrint ("CLIPBOARD was cleared at %lu\n", time);
last_x_selection_change = time;
XLClearForeignSelection (time);
last_x_selection_change = TimestampFromServerTime (time);
XLClearForeignSelection (last_x_selection_change);
/* Free data that is no longer used. */
XLFree (x_selection_targets);
@ -1089,13 +1112,13 @@ static void
NoticePrimaryCleared (Time time)
{
/* Ignore outdated events. */
if (time < last_primary_time)
if (TimeIs (time, Earlier, last_primary_time))
return;
DebugPrint ("PRIMARY was cleared at %lu\n", time);
last_primary_time = time;
XLClearForeignPrimary (time);
last_primary_time = TimestampFromServerTime (time);
XLClearForeignPrimary (last_primary_time);
/* Free data that is no longer used. */
XLFree (x_primary_targets);
@ -1111,14 +1134,16 @@ HandleSelectionNotify (XFixesSelectionNotifyEvent *event)
return;
if (event->selection == CLIPBOARD
&& event->selection_timestamp > last_clipboard_change)
&& 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 = event->selection_timestamp;
last_clipboard_change
= TimestampFromServerTime (event->selection_timestamp);
if (event->selection == XA_PRIMARY
&& event->selection_timestamp > last_primary_time)
last_primary_time = event->selection_timestamp;
&& TimeIs (event->selection_timestamp, Later, last_primary_time))
last_primary_time
= TimestampFromServerTime (event->selection_timestamp);
if (event->owner != None
&& event->selection == CLIPBOARD)
@ -1957,9 +1982,10 @@ XLNoteSourceDestroyed (DataSource *source)
{
if (source == selection_data_source)
{
DebugPrint ("Disowning CLIPBOARD at %lu (vs. last change %lu)\n"
DebugPrint ("Disowning CLIPBOARD at %u (vs. last change %u)\n"
"This is being done in response to the source being destroyed.\n",
last_clipboard_time, last_x_selection_change);
last_clipboard_time.milliseconds,
last_x_selection_change.milliseconds);
/* Disown the selection. */
DisownSelection (CLIPBOARD);
@ -2006,15 +2032,15 @@ FindTargetInArray (Atom *targets, int ntargets, Atom atom)
macros... */
#define NoteLocalSelectionBody(callback, time_1, time_2, atom, field, foreign, n_foreign) \
Time time; \
Timestamp time; \
Atom *targets; \
int ntargets, i, n_data_conversions; \
Bool rc; \
\
if (source == NULL) \
{ \
DebugPrint ("Disowning " #atom " at %lu (vs. last change %lu)\n", \
time_1, time_2); \
DebugPrint ("Disowning " #atom " at %u (vs. last change %u)\n", \
time_1.milliseconds, time_2.milliseconds); \
\
/* Disown the selection. */ \
DisownSelection (atom); \
@ -2022,18 +2048,19 @@ FindTargetInArray (Atom *targets, int ntargets, Atom atom)
\
/* Return whether or not the selection was actually \
disowned. */ \
return time_1 >= time_2; \
return TimestampIs (time_1, Later, time_2); \
} \
\
time = XLSeatGetLastUserTime (seat); \
\
DebugPrint ("Acquiring ownership of " #atom " at %lu\n", time); \
DebugPrint ("Acquiring ownership of " #atom " at %u\n", time.milliseconds); \
\
if (!time) \
if (!time.months && !time.milliseconds) \
/* Nothing has yet happened on the seat. */ \
return False; \
\
if (time < time_1 || time < time_2) \
if (TimestampIs (time, Earlier, time_1) \
|| TimestampIs (time, Earlier, time_2)) \
/* TIME is out of date. */ \
return False; \
\
@ -2081,7 +2108,7 @@ FindTargetInArray (Atom *targets, int ntargets, Atom atom)
/* And own the selection. */ \
field = source; \
\
rc = OwnSelection (time, atom, callback, targets, ntargets); \
rc = OwnSelection (time.milliseconds, atom, callback, targets, ntargets); \
XLFree (targets); \
return rc \