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)
|
DetermineServerTime (void)
|
||||||
{
|
{
|
||||||
Time server_time;
|
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
|
/* Try to determine if the X server time is the same as the
|
||||||
monotonic time. If it is not, certain features such as "active"
|
monotonic time. If it is not, certain features such as "active"
|
||||||
|
@ -42,20 +43,28 @@ DetermineServerTime (void)
|
||||||
|
|
||||||
clock_gettime (CLOCK_MONOTONIC, &clock_spec);
|
clock_gettime (CLOCK_MONOTONIC, &clock_spec);
|
||||||
server_time = XLGetServerTimeRoundtrip ();
|
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
|
if (IntMultiplyWrapv (clock_spec.tv_sec, 1000, &clock_ms))
|
||||||
|| TimespecCmp (diff, MakeTimespec (0, -50000000)) <= 0)
|
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
|
/* Since the difference between the server time and the monotonic
|
||||||
time is less than 50 ms, the server time is the monotonic
|
time is less than 5 ms, the server time is most likely the
|
||||||
time. */
|
monotonic time. */
|
||||||
compositor.server_time_monotonic = True;
|
compositor.server_time_monotonic = True;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
overflow:
|
||||||
compositor.server_time_monotonic = False;
|
compositor.server_time_monotonic = False;
|
||||||
fprintf (stderr, "Warning: the X server time does not seem to"
|
fprintf (stderr, "Warning: the X server time does not seem to"
|
||||||
" be synchronized with the monotonic time. Multiple"
|
" 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 \
|
icon_surface.c primary_selection.c renderer.c \
|
||||||
picture_renderer.c explicit_synchronization.c transform.c \
|
picture_renderer.c explicit_synchronization.c transform.c \
|
||||||
wp_viewporter.c decoration.c text_input.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 \
|
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 \
|
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 \
|
icon_surface.o primary_selection.o renderer.o \
|
||||||
picture_renderer.o explicit_synchronization.o transform.o \
|
picture_renderer.o explicit_synchronization.o transform.o \
|
||||||
wp_viewporter.o decoration.o text_input.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
|
GENHEADERS = transfer_atoms.h
|
||||||
|
|
||||||
|
|
47
compositor.h
47
compositor.h
|
@ -103,6 +103,35 @@ typedef struct _ViewportExt ViewportExt;
|
||||||
|
|
||||||
extern Compositor compositor;
|
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. */
|
/* Defined in renderer.c. */
|
||||||
|
|
||||||
typedef struct _RenderFuncs RenderFuncs;
|
typedef struct _RenderFuncs RenderFuncs;
|
||||||
|
@ -1301,12 +1330,16 @@ typedef struct _DataSource DataSource;
|
||||||
typedef struct _CreateOfferFuncs CreateOfferFuncs;
|
typedef struct _CreateOfferFuncs CreateOfferFuncs;
|
||||||
typedef struct _DndOfferFuncs DndOfferFuncs;
|
typedef struct _DndOfferFuncs DndOfferFuncs;
|
||||||
|
|
||||||
typedef struct wl_resource *(*CreateOfferFunc) (struct wl_client *, Time);
|
typedef struct wl_resource *(*CreateOfferFunc) (struct wl_client *,
|
||||||
typedef void (*SendDataFunc) (struct wl_resource *, Time);
|
Timestamp);
|
||||||
|
typedef void (*SendDataFunc) (struct wl_resource *, Timestamp);
|
||||||
|
|
||||||
struct _CreateOfferFuncs
|
struct _CreateOfferFuncs
|
||||||
{
|
{
|
||||||
|
/* Function called to create data offers. */
|
||||||
CreateOfferFunc create_offer;
|
CreateOfferFunc create_offer;
|
||||||
|
|
||||||
|
/* Function called to send data offers. */
|
||||||
SendDataFunc send_offers;
|
SendDataFunc send_offers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1321,8 +1354,8 @@ extern void XLRetainDataDevice (DataDevice *);
|
||||||
extern void XLReleaseDataDevice (DataDevice *);
|
extern void XLReleaseDataDevice (DataDevice *);
|
||||||
extern void XLDataDeviceClearSeat (DataDevice *);
|
extern void XLDataDeviceClearSeat (DataDevice *);
|
||||||
extern void XLDataDeviceHandleFocusChange (DataDevice *);
|
extern void XLDataDeviceHandleFocusChange (DataDevice *);
|
||||||
extern void XLSetForeignSelection (Time, CreateOfferFuncs);
|
extern void XLSetForeignSelection (Timestamp, CreateOfferFuncs);
|
||||||
extern void XLClearForeignSelection (Time);
|
extern void XLClearForeignSelection (Timestamp);
|
||||||
extern int XLDataSourceTargetCount (DataSource *);
|
extern int XLDataSourceTargetCount (DataSource *);
|
||||||
extern void XLDataSourceGetTargets (DataSource *, Atom *);
|
extern void XLDataSourceGetTargets (DataSource *, Atom *);
|
||||||
extern struct wl_resource *XLResourceFromDataSource (DataSource *);
|
extern struct wl_resource *XLResourceFromDataSource (DataSource *);
|
||||||
|
@ -1391,7 +1424,7 @@ extern Bool XLSeatIsInert (Seat *);
|
||||||
extern Bool XLSeatIsClientFocused (Seat *, struct wl_client *);
|
extern Bool XLSeatIsClientFocused (Seat *, struct wl_client *);
|
||||||
extern Surface *XLSeatGetFocus (Seat *);
|
extern Surface *XLSeatGetFocus (Seat *);
|
||||||
extern void XLSeatShowWindowMenu (Seat *, Surface *, int, int);
|
extern void XLSeatShowWindowMenu (Seat *, Surface *, int, int);
|
||||||
extern Time XLSeatGetLastUserTime (Seat *);
|
extern Timestamp XLSeatGetLastUserTime (Seat *);
|
||||||
extern void XLSeatBeginDrag (Seat *, DataSource *, Surface *,
|
extern void XLSeatBeginDrag (Seat *, DataSource *, Surface *,
|
||||||
Surface *, uint32_t);
|
Surface *, uint32_t);
|
||||||
extern DataSource *XLSeatGetDragDataSource (Seat *);
|
extern DataSource *XLSeatGetDragDataSource (Seat *);
|
||||||
|
@ -1507,8 +1540,8 @@ extern Bool XLIsWindowIconSurface (Window);
|
||||||
/* Defined in primary_selection.c. */
|
/* Defined in primary_selection.c. */
|
||||||
|
|
||||||
extern void XLInitPrimarySelection (void);
|
extern void XLInitPrimarySelection (void);
|
||||||
extern void XLSetForeignPrimary (Time, CreateOfferFuncs);
|
extern void XLSetForeignPrimary (Timestamp, CreateOfferFuncs);
|
||||||
extern void XLClearForeignPrimary (Time);
|
extern void XLClearForeignPrimary (Timestamp);
|
||||||
extern Bool XLNoteLocalPrimary (Seat *, PDataSource *);
|
extern Bool XLNoteLocalPrimary (Seat *, PDataSource *);
|
||||||
extern void XLPrimarySelectionHandleFocusChange (Seat *);
|
extern void XLPrimarySelectionHandleFocusChange (Seat *);
|
||||||
extern struct wl_resource *XLResourceFromPDataSource (PDataSource *);
|
extern struct wl_resource *XLResourceFromPDataSource (PDataSource *);
|
||||||
|
|
|
@ -163,7 +163,7 @@ static DataSource foreign_selection_key;
|
||||||
static CreateOfferFuncs foreign_selection_functions;
|
static CreateOfferFuncs foreign_selection_functions;
|
||||||
|
|
||||||
/* Time the foreign selection was changed. */
|
/* Time the foreign selection was changed. */
|
||||||
static Time foreign_selection_time;
|
static Timestamp foreign_selection_time;
|
||||||
|
|
||||||
/* When it changed. */
|
/* When it changed. */
|
||||||
static uint32_t last_selection_change_serial;
|
static uint32_t last_selection_change_serial;
|
||||||
|
@ -759,7 +759,7 @@ UpdateSingleReferenceWithForeignOffer (struct wl_client *client,
|
||||||
DataDeviceReference *reference)
|
DataDeviceReference *reference)
|
||||||
{
|
{
|
||||||
struct wl_resource *resource;
|
struct wl_resource *resource;
|
||||||
Time time;
|
Timestamp time;
|
||||||
|
|
||||||
time = foreign_selection_time;
|
time = foreign_selection_time;
|
||||||
resource = foreign_selection_functions.create_offer (client, time);
|
resource = foreign_selection_functions.create_offer (client, time);
|
||||||
|
@ -1173,11 +1173,11 @@ XLDataDeviceHandleFocusChange (DataDevice *device)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
XLSetForeignSelection (Time time, CreateOfferFuncs functions)
|
XLSetForeignSelection (Timestamp time, CreateOfferFuncs functions)
|
||||||
{
|
{
|
||||||
uint32_t serial;
|
uint32_t serial;
|
||||||
|
|
||||||
if (time < foreign_selection_time)
|
if (TimestampIs (time, Earlier, foreign_selection_time))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
serial = wl_display_next_serial (compositor.wl_display);
|
serial = wl_display_next_serial (compositor.wl_display);
|
||||||
|
@ -1202,9 +1202,9 @@ XLSetForeignSelection (Time time, CreateOfferFuncs functions)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
XLClearForeignSelection (Time time)
|
XLClearForeignSelection (Timestamp time)
|
||||||
{
|
{
|
||||||
if (time < foreign_selection_time)
|
if (TimestampIs (time, Earlier, foreign_selection_time))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (current_selection_data == &foreign_selection_key)
|
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;
|
Window toplevel, proxy, self;
|
||||||
int version, proxy_version;
|
int version, proxy_version;
|
||||||
|
Timestamp timestamp;
|
||||||
|
|
||||||
if (finish_source || drag_state.flags & PendingDrop)
|
if (finish_source || drag_state.flags & PendingDrop)
|
||||||
/* A finish is pending. */
|
/* 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. */
|
/* Try to own XdndSelection with the last user time. */
|
||||||
if (!(drag_state.flags & SelectionSet))
|
if (!(drag_state.flags & SelectionSet))
|
||||||
{
|
{
|
||||||
drag_state.timestamp = XLSeatGetLastUserTime (seat);
|
timestamp = XLSeatGetLastUserTime (seat);
|
||||||
|
drag_state.timestamp = timestamp.milliseconds;
|
||||||
|
|
||||||
if (!XLOwnDragSelection (drag_state.timestamp,
|
if (!XLOwnDragSelection (drag_state.timestamp,
|
||||||
XLSeatGetDragDataSource (seat)))
|
XLSeatGetDragDataSource (seat)))
|
||||||
|
|
|
@ -673,8 +673,8 @@ InitRenderFuncs (void)
|
||||||
|| !XPresentQueryVersion (compositor.display,
|
|| !XPresentQueryVersion (compositor.display,
|
||||||
&major, &minor))
|
&major, &minor))
|
||||||
{
|
{
|
||||||
fprintf (stderr, "the X presentation extension is not supported"
|
fprintf (stderr, "The X presentation extension is not supported"
|
||||||
" by this X server");
|
" by this X server\n");
|
||||||
return False;
|
return False;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ static PDataSource foreign_selection_key;
|
||||||
static CreateOfferFuncs foreign_selection_functions;
|
static CreateOfferFuncs foreign_selection_functions;
|
||||||
|
|
||||||
/* The time the foreign selection was set. */
|
/* The time the foreign selection was set. */
|
||||||
static Time foreign_selection_time;
|
static Timestamp foreign_selection_time;
|
||||||
|
|
||||||
/* Forward declaration. */
|
/* Forward declaration. */
|
||||||
|
|
||||||
|
@ -354,7 +354,7 @@ UpdateSingleReferenceWithForeignOffer (struct wl_client *client,
|
||||||
PDataDevice *reference)
|
PDataDevice *reference)
|
||||||
{
|
{
|
||||||
struct wl_resource *scratch, *resource;
|
struct wl_resource *scratch, *resource;
|
||||||
Time time;
|
Timestamp time;
|
||||||
|
|
||||||
time = foreign_selection_time;
|
time = foreign_selection_time;
|
||||||
resource = foreign_selection_functions.create_offer (client, time);
|
resource = foreign_selection_functions.create_offer (client, time);
|
||||||
|
@ -721,12 +721,12 @@ HandleBind (struct wl_client *client, void *data, uint32_t version,
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
XLSetForeignPrimary (Time time, CreateOfferFuncs functions)
|
XLSetForeignPrimary (Timestamp time, CreateOfferFuncs functions)
|
||||||
{
|
{
|
||||||
uint32_t serial;
|
uint32_t serial;
|
||||||
struct wl_resource *scratch;
|
struct wl_resource *scratch;
|
||||||
|
|
||||||
if (time < foreign_selection_time)
|
if (TimestampIs (time, Earlier, foreign_selection_time))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
serial = wl_display_next_serial (compositor.wl_display);
|
serial = wl_display_next_serial (compositor.wl_display);
|
||||||
|
@ -754,9 +754,9 @@ XLSetForeignPrimary (Time time, CreateOfferFuncs functions)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
XLClearForeignPrimary (Time time)
|
XLClearForeignPrimary (Timestamp time)
|
||||||
{
|
{
|
||||||
if (time < foreign_selection_time)
|
if (TimestampIs (time, Earlier, foreign_selection_time))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (primary_selection == &foreign_selection_key)
|
if (primary_selection == &foreign_selection_key)
|
||||||
|
|
16
seat.c
16
seat.c
|
@ -337,6 +337,9 @@ struct _ModifierChangeCallback
|
||||||
|
|
||||||
struct _Seat
|
struct _Seat
|
||||||
{
|
{
|
||||||
|
/* The last user time. */
|
||||||
|
Timestamp last_user_time;
|
||||||
|
|
||||||
/* wl_global associated with this seat. */
|
/* wl_global associated with this seat. */
|
||||||
struct wl_global *global;
|
struct wl_global *global;
|
||||||
|
|
||||||
|
@ -2367,6 +2370,9 @@ HandleRawKey (XIRawEvent *event)
|
||||||
|
|
||||||
/* This is used for tracking grabs. */
|
/* This is used for tracking grabs. */
|
||||||
seat->its_depress_time = event->time;
|
seat->its_depress_time = event->time;
|
||||||
|
|
||||||
|
/* Update the last user time. */
|
||||||
|
seat->last_user_time = TimestampFromServerTime (event->time);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -3730,6 +3736,9 @@ DispatchMotion (Subcompositor *subcompositor, XIDeviceEvent *xev)
|
||||||
seat->its_root_y = xev->root_y;
|
seat->its_root_y = xev->root_y;
|
||||||
seat->its_press_time = xev->time;
|
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,
|
actual_dispatch = FindSurfaceUnder (subcompositor, xev->event_x,
|
||||||
xev->event_y);
|
xev->event_y);
|
||||||
|
|
||||||
|
@ -4375,11 +4384,10 @@ IdentifySeat (WhatEdge *edge, uint32_t serial)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Time
|
static Timestamp
|
||||||
GetLastUserTime (Seat *seat)
|
GetLastUserTime (Seat *seat)
|
||||||
{
|
{
|
||||||
return MAX (seat->its_press_time,
|
return seat->last_user_time;
|
||||||
seat->its_depress_time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Bool
|
static Bool
|
||||||
|
@ -5361,7 +5369,7 @@ XLSeatBeginDrag (Seat *seat, DataSource *data_source, Surface *start_surface,
|
||||||
seat->flags &= ~IsDropped;
|
seat->flags &= ~IsDropped;
|
||||||
}
|
}
|
||||||
|
|
||||||
Time
|
Timestamp
|
||||||
XLSeatGetLastUserTime (Seat *seat)
|
XLSeatGetLastUserTime (Seat *seat)
|
||||||
{
|
{
|
||||||
return GetLastUserTime (seat);
|
return GetLastUserTime (seat);
|
||||||
|
|
131
select.c
131
select.c
|
@ -757,19 +757,23 @@ GetTransferFunction (SelectionOwnerInfo *info,
|
||||||
}
|
}
|
||||||
|
|
||||||
static WriteTransfer *
|
static WriteTransfer *
|
||||||
FindWriteTransfer (Window requestor, Atom property)
|
FindWriteTransfer (Window requestor, Atom property,
|
||||||
|
int ignore_state)
|
||||||
{
|
{
|
||||||
WriteTransfer *transfer;
|
WriteTransfer *transfer;
|
||||||
|
|
||||||
transfer = write_transfers.next;
|
transfer = write_transfers.last;
|
||||||
|
|
||||||
while (transfer != &write_transfers)
|
while (transfer != &write_transfers)
|
||||||
{
|
{
|
||||||
if (transfer->requestor == requestor
|
if (transfer->requestor == requestor
|
||||||
&& transfer->property == property)
|
&& transfer->property == property
|
||||||
|
&& (!ignore_state
|
||||||
|
|| ((transfer->state & ignore_state)
|
||||||
|
!= ignore_state)))
|
||||||
return transfer;
|
return transfer;
|
||||||
|
|
||||||
transfer = transfer->next;
|
transfer = transfer->last;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1036,12 +1040,12 @@ TransferFinished (WriteTransfer *transfer)
|
||||||
transfer if nothing has previously been written. */
|
transfer if nothing has previously been written. */
|
||||||
|| !(transfer->state & IsFlushed))
|
|| !(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. */
|
/* There is still property data left to be written. */
|
||||||
FlushTransfer (transfer, True);
|
FlushTransfer (transfer, True);
|
||||||
transfer->state |= IsFinished;
|
transfer->state |= IsFinished;
|
||||||
|
|
||||||
DebugPrint ("Transfer finished, but there is still property data"
|
|
||||||
" unwritten\n");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
/* The transfer is really finished. */
|
/* The transfer is really finished. */
|
||||||
|
@ -1107,8 +1111,9 @@ TransferBecameReadable (WriteTransfer *transfer)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DebugPrint ("Transfer complete, bytes read as part of EOF: %td, off: %td\n",
|
DebugPrint ("Transfer complete, bytes read as part of EOF: %td, "
|
||||||
transfer->offset, transfer->size);
|
"off: %td size: %td\n", bytes_read, transfer->offset,
|
||||||
|
transfer->size);
|
||||||
|
|
||||||
transfer->offset += bytes_read;
|
transfer->offset += bytes_read;
|
||||||
TransferFinished (transfer);
|
TransferFinished (transfer);
|
||||||
|
@ -1239,11 +1244,12 @@ ConvertSelectionMultiple (SelectionOwnerInfo *info, XEvent *event,
|
||||||
DebugPrint ("Verifying MULTIPLE transfer; target = %lu, property = %lu\n",
|
DebugPrint ("Verifying MULTIPLE transfer; target = %lu, property = %lu\n",
|
||||||
atoms[i + 0], atoms[i + 1]);
|
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]))
|
|| FindQueuedTransfer (event->xselectionrequest.requestor, atoms[1]))
|
||||||
{
|
{
|
||||||
DebugPrint ("Found ongoing selection transfer with same requestor "
|
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);
|
QueueTransfer (event);
|
||||||
|
|
||||||
|
@ -1390,7 +1396,7 @@ static Bool
|
||||||
HandleSelectionRequest (XEvent *event)
|
HandleSelectionRequest (XEvent *event)
|
||||||
{
|
{
|
||||||
XEvent notify;
|
XEvent notify;
|
||||||
WriteTransfer *transfer;
|
WriteTransfer *transfer, *existing_transfer;
|
||||||
long quantum;
|
long quantum;
|
||||||
SelectionOwnerInfo *info;
|
SelectionOwnerInfo *info;
|
||||||
|
|
||||||
|
@ -1428,8 +1434,51 @@ HandleSelectionRequest (XEvent *event)
|
||||||
|
|
||||||
/* If a selection request with the same property and window already
|
/* If a selection request with the same property and window already
|
||||||
exists, delay this request for later. */
|
exists, delay this request for later. */
|
||||||
if (FindWriteTransfer (event->xselectionrequest.requestor,
|
existing_transfer
|
||||||
event->xselectionrequest.property)
|
= 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
|
/* We need to look at the queue too; otherwise, events from the
|
||||||
future might be handled out of order, if the original write
|
future might be handled out of order, if the original write
|
||||||
transfer is gone, but some events are still queued. */
|
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
|
/* Then, read from the other side of the queue, and handle
|
||||||
everything. */
|
everything. */
|
||||||
item = queued_transfers.last;
|
item = temp.last;
|
||||||
|
|
||||||
while (item != &queued_transfers)
|
while (item != &temp)
|
||||||
{
|
{
|
||||||
last = item;
|
last = item;
|
||||||
item = item->last;
|
item = item->last;
|
||||||
|
@ -1601,29 +1650,36 @@ HandleSelectionNotify (XEvent *event)
|
||||||
static Bool
|
static Bool
|
||||||
HandlePropertyDelete (XEvent *event)
|
HandlePropertyDelete (XEvent *event)
|
||||||
{
|
{
|
||||||
WriteTransfer *transfer;
|
WriteTransfer *transfer, *last;
|
||||||
|
|
||||||
transfer = FindWriteTransfer (event->xproperty.window,
|
transfer = write_transfers.last;
|
||||||
event->xproperty.atom);
|
|
||||||
if (!transfer)
|
|
||||||
return False;
|
|
||||||
|
|
||||||
DebugPrint ("Handling property deletion for %lu\n",
|
DebugPrint ("Handling property deletion for %lu; window %lu\n",
|
||||||
event->xproperty.atom);
|
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)
|
if (transfer->state & IsFinished)
|
||||||
{
|
{
|
||||||
DebugPrint ("Completing transfer\n");
|
DebugPrint ("Completing transfer\n");
|
||||||
|
|
||||||
/* The transfer is now complete; finish it by freeing its data,
|
/* The transfer is now complete; finish it by freeing its
|
||||||
and potentially writing zero-length data. */
|
data, and potentially writing zero-length data. */
|
||||||
FreeTransfer (transfer);
|
FreeTransfer (transfer);
|
||||||
}
|
}
|
||||||
else if (transfer->state & IsWaitingForIncr)
|
else if (transfer->state & IsWaitingForIncr)
|
||||||
{
|
{
|
||||||
/* If transfer is waiting for the INCR property to be deleted, mark
|
/* If transfer is waiting for the INCR property to be
|
||||||
it as started, and flush it again to write the first piece of
|
deleted, mark it as started, and flush it again to write
|
||||||
property data. */
|
the first piece of property data. */
|
||||||
|
|
||||||
DebugPrint ("Starting transfer in response to INCR property deletion\n");
|
DebugPrint ("Starting transfer in response to INCR property deletion\n");
|
||||||
|
|
||||||
|
@ -1650,6 +1706,9 @@ HandlePropertyDelete (XEvent *event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transfer = last;
|
||||||
|
}
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1658,6 +1717,22 @@ HandlePropertyNotify (XEvent *event)
|
||||||
{
|
{
|
||||||
ReadTransfer *transfer;
|
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)
|
if (event->xproperty.state != PropertyNewValue)
|
||||||
return HandlePropertyDelete (event);
|
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. */
|
/* 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
|
/* The time ownership was last asserted over CLIPBOARD, and the last
|
||||||
time any client did that. */
|
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. */
|
/* The last time ownership over PRIMARY changed. */
|
||||||
static Time last_primary_time;
|
static Timestamp last_primary_time;
|
||||||
|
|
||||||
/* The currently supported selection targets. */
|
/* The currently supported selection targets. */
|
||||||
static Atom *x_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);
|
static void PostReceiveConversion (Time, Atom, Atom, int);
|
||||||
|
|
||||||
#define ReceiveBody(selection, primary) \
|
#define ReceiveBody(selection, primary) \
|
||||||
Time time; \
|
Timestamp time; \
|
||||||
TargetMapping *translation; \
|
TargetMapping *translation; \
|
||||||
\
|
\
|
||||||
DebugPrint ("Receiving %s from X " #selection " \n", \
|
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 \
|
/* Cast to intptr_t to silence warnings when the pointer type is \
|
||||||
larger than long. */ \
|
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. */ \
|
/* Find which selection target corresponds to MIME_TYPE. */ \
|
||||||
translation = FindTranslationForMimeType (mime_type, primary); \
|
translation = FindTranslationForMimeType (mime_type, primary); \
|
||||||
|
@ -705,11 +705,11 @@ static void PostReceiveConversion (Time, Atom, Atom, int);
|
||||||
{ \
|
{ \
|
||||||
if (!translation->translation_func) \
|
if (!translation->translation_func) \
|
||||||
/* If a corresponding target exists, ask to receive it. */ \
|
/* If a corresponding target exists, ask to receive it. */ \
|
||||||
PostReceiveDirect (time, selection, \
|
PostReceiveDirect (time.milliseconds, selection, \
|
||||||
MappingAtom (translation), fd); \
|
MappingAtom (translation), fd); \
|
||||||
else \
|
else \
|
||||||
/* Otherwise, use the translation function. */ \
|
/* Otherwise, use the translation function. */ \
|
||||||
translation->translation_func (time, selection, \
|
translation->translation_func (time.milliseconds, selection, \
|
||||||
MappingAtom (translation), \
|
MappingAtom (translation), \
|
||||||
fd); \
|
fd); \
|
||||||
} \
|
} \
|
||||||
|
@ -753,8 +753,29 @@ static const struct wl_data_offer_interface wl_data_offer_impl =
|
||||||
.set_actions = SetActions,
|
.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 *
|
static struct wl_resource *
|
||||||
CreateOffer (struct wl_client *client, Time time)
|
CreateOffer (struct wl_client *client, Timestamp time)
|
||||||
{
|
{
|
||||||
struct wl_resource *resource;
|
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
|
/* Otherwise, set the user_data to the time of the selection
|
||||||
change. */
|
change. */
|
||||||
wl_resource_set_implementation (resource, &wl_data_offer_impl,
|
wl_resource_set_implementation (resource, &wl_data_offer_impl,
|
||||||
(void *) time, NULL);
|
AllocateTimestamp (time),
|
||||||
|
HandleOfferResourceDestroy);
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -786,7 +808,7 @@ static struct zwp_primary_selection_offer_v1_interface primary_offer_impl =
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct wl_resource *
|
static struct wl_resource *
|
||||||
CreatePrimaryOffer (struct wl_client *client, Time time)
|
CreatePrimaryOffer (struct wl_client *client, Timestamp time)
|
||||||
{
|
{
|
||||||
struct wl_resource *resource;
|
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
|
/* Otherwise, set the user_data to the time of the selection
|
||||||
change. */
|
change. */
|
||||||
wl_resource_set_implementation (resource, &primary_offer_impl,
|
wl_resource_set_implementation (resource, &primary_offer_impl,
|
||||||
(void *) time, NULL);
|
AllocateTimestamp (time),
|
||||||
|
HandleOfferResourceDestroy);
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -883,9 +906,9 @@ SendOffers1 (struct wl_resource *resource, int ntargets, Atom *targets,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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. */
|
/* This offer is out of date. */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -894,9 +917,9 @@ SendOffers (struct wl_resource *resource, Time time)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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. */
|
/* This offer is out of date. */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -916,7 +939,7 @@ HandleNewSelection (Time time, Atom selection, Atom *targets,
|
||||||
/* The primary selection changed, and now has the given
|
/* The primary selection changed, and now has the given
|
||||||
targets. */
|
targets. */
|
||||||
|
|
||||||
if (time < last_primary_time)
|
if (TimeIs (time, Earlier, last_primary_time))
|
||||||
{
|
{
|
||||||
XLFree (targets);
|
XLFree (targets);
|
||||||
return;
|
return;
|
||||||
|
@ -926,21 +949,21 @@ HandleNewSelection (Time time, Atom selection, Atom *targets,
|
||||||
XLFree (x_primary_targets);
|
XLFree (x_primary_targets);
|
||||||
x_primary_targets = targets;
|
x_primary_targets = targets;
|
||||||
num_x_primary_targets = ntargets;
|
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
|
/* Add the right functions and set them as the foreign primary
|
||||||
selection handler at TIME. */
|
selection handler at TIME. */
|
||||||
funcs.create_offer = CreatePrimaryOffer;
|
funcs.create_offer = CreatePrimaryOffer;
|
||||||
funcs.send_offers = SendPrimaryOffers;
|
funcs.send_offers = SendPrimaryOffers;
|
||||||
|
|
||||||
XLSetForeignPrimary (time, funcs);
|
XLSetForeignPrimary (last_primary_time, funcs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Else, the selection that changed is CLIPBOARD. */
|
/* Else, the selection that changed is CLIPBOARD. */
|
||||||
|
|
||||||
/* Ignore outdated selection changes. */
|
/* Ignore outdated selection changes. */
|
||||||
if (time < last_x_selection_change)
|
if (TimeIs (time, Earlier, last_x_selection_change))
|
||||||
{
|
{
|
||||||
/* We are responsible for deallocating targets. */
|
/* We are responsible for deallocating targets. */
|
||||||
XLFree (targets);
|
XLFree (targets);
|
||||||
|
@ -953,7 +976,7 @@ HandleNewSelection (Time time, Atom selection, Atom *targets,
|
||||||
x_selection_targets = targets;
|
x_selection_targets = targets;
|
||||||
num_x_selection_targets = ntargets;
|
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
|
/* Add the right functions and set them as the foreign selection
|
||||||
handler at TIME. */
|
handler at TIME. */
|
||||||
|
@ -961,7 +984,7 @@ HandleNewSelection (Time time, Atom selection, Atom *targets,
|
||||||
funcs.create_offer = CreateOffer;
|
funcs.create_offer = CreateOffer;
|
||||||
funcs.send_offers = SendOffers;
|
funcs.send_offers = SendOffers;
|
||||||
|
|
||||||
XLSetForeignSelection (time, funcs);
|
XLSetForeignSelection (last_x_selection_change, funcs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1071,13 +1094,13 @@ static void
|
||||||
NoticeClipboardCleared (Time time)
|
NoticeClipboardCleared (Time time)
|
||||||
{
|
{
|
||||||
/* Ignore outdated events. */
|
/* Ignore outdated events. */
|
||||||
if (time < last_x_selection_change)
|
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 = time;
|
last_x_selection_change = TimestampFromServerTime (time);
|
||||||
XLClearForeignSelection (time);
|
XLClearForeignSelection (last_x_selection_change);
|
||||||
|
|
||||||
/* Free data that is no longer used. */
|
/* Free data that is no longer used. */
|
||||||
XLFree (x_selection_targets);
|
XLFree (x_selection_targets);
|
||||||
|
@ -1089,13 +1112,13 @@ static void
|
||||||
NoticePrimaryCleared (Time time)
|
NoticePrimaryCleared (Time time)
|
||||||
{
|
{
|
||||||
/* Ignore outdated events. */
|
/* Ignore outdated events. */
|
||||||
if (time < last_primary_time)
|
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 = time;
|
last_primary_time = TimestampFromServerTime (time);
|
||||||
XLClearForeignPrimary (time);
|
XLClearForeignPrimary (last_primary_time);
|
||||||
|
|
||||||
/* Free data that is no longer used. */
|
/* Free data that is no longer used. */
|
||||||
XLFree (x_primary_targets);
|
XLFree (x_primary_targets);
|
||||||
|
@ -1111,14 +1134,16 @@ HandleSelectionNotify (XFixesSelectionNotifyEvent *event)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (event->selection == CLIPBOARD
|
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
|
/* 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 = event->selection_timestamp;
|
last_clipboard_change
|
||||||
|
= TimestampFromServerTime (event->selection_timestamp);
|
||||||
|
|
||||||
if (event->selection == XA_PRIMARY
|
if (event->selection == XA_PRIMARY
|
||||||
&& event->selection_timestamp > last_primary_time)
|
&& TimeIs (event->selection_timestamp, Later, last_primary_time))
|
||||||
last_primary_time = event->selection_timestamp;
|
last_primary_time
|
||||||
|
= TimestampFromServerTime (event->selection_timestamp);
|
||||||
|
|
||||||
if (event->owner != None
|
if (event->owner != None
|
||||||
&& event->selection == CLIPBOARD)
|
&& event->selection == CLIPBOARD)
|
||||||
|
@ -1957,9 +1982,10 @@ XLNoteSourceDestroyed (DataSource *source)
|
||||||
{
|
{
|
||||||
if (source == selection_data_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",
|
"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. */
|
/* Disown the selection. */
|
||||||
DisownSelection (CLIPBOARD);
|
DisownSelection (CLIPBOARD);
|
||||||
|
@ -2006,15 +2032,15 @@ FindTargetInArray (Atom *targets, int ntargets, Atom atom)
|
||||||
macros... */
|
macros... */
|
||||||
|
|
||||||
#define NoteLocalSelectionBody(callback, time_1, time_2, atom, field, foreign, n_foreign) \
|
#define NoteLocalSelectionBody(callback, time_1, time_2, atom, field, foreign, n_foreign) \
|
||||||
Time time; \
|
Timestamp time; \
|
||||||
Atom *targets; \
|
Atom *targets; \
|
||||||
int ntargets, i, n_data_conversions; \
|
int ntargets, i, n_data_conversions; \
|
||||||
Bool rc; \
|
Bool rc; \
|
||||||
\
|
\
|
||||||
if (source == NULL) \
|
if (source == NULL) \
|
||||||
{ \
|
{ \
|
||||||
DebugPrint ("Disowning " #atom " at %lu (vs. last change %lu)\n", \
|
DebugPrint ("Disowning " #atom " at %u (vs. last change %u)\n", \
|
||||||
time_1, time_2); \
|
time_1.milliseconds, time_2.milliseconds); \
|
||||||
\
|
\
|
||||||
/* Disown the selection. */ \
|
/* Disown the selection. */ \
|
||||||
DisownSelection (atom); \
|
DisownSelection (atom); \
|
||||||
|
@ -2022,18 +2048,19 @@ FindTargetInArray (Atom *targets, int ntargets, Atom atom)
|
||||||
\
|
\
|
||||||
/* Return whether or not the selection was actually \
|
/* Return whether or not the selection was actually \
|
||||||
disowned. */ \
|
disowned. */ \
|
||||||
return time_1 >= time_2; \
|
return TimestampIs (time_1, Later, time_2); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
time = XLSeatGetLastUserTime (seat); \
|
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. */ \
|
/* Nothing has yet happened on the seat. */ \
|
||||||
return False; \
|
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. */ \
|
/* TIME is out of date. */ \
|
||||||
return False; \
|
return False; \
|
||||||
\
|
\
|
||||||
|
@ -2081,7 +2108,7 @@ FindTargetInArray (Atom *targets, int ntargets, Atom atom)
|
||||||
/* And own the selection. */ \
|
/* And own the selection. */ \
|
||||||
field = source; \
|
field = source; \
|
||||||
\
|
\
|
||||||
rc = OwnSelection (time, atom, callback, targets, ntargets); \
|
rc = OwnSelection (time.milliseconds, atom, callback, targets, ntargets); \
|
||||||
XLFree (targets); \
|
XLFree (targets); \
|
||||||
return rc \
|
return rc \
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue