forked from 12to11/12to11
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:
parent
08c70b8e3e
commit
5687fbca6d
10 changed files with 288 additions and 132 deletions
27
12to11.c
27
12to11.c
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
47
compositor.h
47
compositor.h
|
@ -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 *);
|
||||
|
|
|
@ -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
4
dnd.c
|
@ -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)))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
16
seat.c
|
@ -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
131
select.c
|
@ -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
109
xdata.c
|
@ -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 \
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue