forked from 12to11/12to11
Various improvements to primary selections and resize handling
* 12to11.man: Document new environment variables. * Imakefile ($(OBJS)): Also depend on protocol headers. * README: Update implementation status. * compositor.h: Update prototypes. * data_device.c (DataOfferSetActions): Fix error sent when the offer is invalid. * dnd.c: Remove out of date comment. * primary_selection.c (struct _PDataSource): Move declaration to compositor.h. New field `atom_types'. (Offer): Record atom types as well. (HandleSourceResourceDestroy): Maybe disown the primary selection, and free offered MIME types. (XLResourceFromPDataSource, XLPDataSourceHasAtomTarget) (XLPDataSourceHasTarget, XLPDataSourceTargetCount) (XLPDataSourceGetTargets): New functions. (UpdateForSingleReference): Handle foreign selections correctly. (SetSelection): Try to own the X selection. (XLSetForeignPrimary, XLClearForeignPrimary): New functions. * seat.c (XLSeatResizeInProgress): New function. * xdata.c (struct _ReadTargetsData): New field `selection'. (struct _DataConversion): Pass send function and source resource to conversion function. (last_primary_time): New timestamp variable. (x_primary_targets, num_x_primary_targets, primary_data_source): New variables. (HasSelectionTarget, FindTranslationForMimeType): Accept arg `is_primary', meaning that the primary selection data should be used instead. (ReceiveBody): Extract generic code from Receive. (Receive): Replace with above macro. (Finish, SetActions): Correctly post errors on DND-specific actions. (ReceivePrimary): New function. (primary_offer_impl): New interface implementation. (CreatePrimaryOffer): New function. (SendOffers1): Extract generic code from SendOffers. (SendOffers, SendPrimaryOffers): Reimplement in terms of SendOffers1. (HandleNewSelection): Handle PRIMARY selection changes. (TargetsFinishCallback): Pass selection to HandleNewSelection. (NoticeClipboardChanged): Set TargetsReadData selection. (NoticePrimaryChanged): New function. (NoticeClipboardCleared): Free unused data. (NoticePrimaryCleared): New function. (HandleSelectionNotify): Handle notifications for both CLIPBOARD and PRIMARY. (MimeTypeFromTarget, TypeFromTarget): Accept argument `primary', meaning to use the primary selection when determining whether or not to call a conversion callback. (GetClipboardCallback): Pass resource and functions to the clipboard callback. (GetConversionCallback): Update to use provided functions and resource. (GetDragCallback, XLNoteSourceDestroyed): Fix typos in debug string. (XLNotePrimaryDestroyed): New function. (NoteLocalSelectionBody, NoteLocalSelectionFooter): Extract selection-generic code from XLNoteLocalSelection. (XLNoteLocalSelection): Reimplement in terms of the above two macros. (XLNoteLocalPrimary, GetPrimaryCallback): New functions. (XLInitXData): Select for selection input from XA_PRIMARY as well. * xdg_surface.c (AckConfigure): Improve debug code. (Commit): Clear that flag. (NoteBounds): If that flag is set, return. (HandleFreeze): Assume a configure event will arrive after a sync request; set flags accordingly. (XLGetXdgSurface): Add HandleFreeze. (XLXdgRoleSendConfigure): Clear some flags and add new state flag `StateWaitingForAckCommit'. Set it. It means that no commit has yet happened after ack_configure. (XLXdgRoleNoteRejectedConfigure): New function. Clear flags if the configure event following _NET_WM_SYNC_REQUEST was rejected. * xdg_toplevel.c (struct _XdgToplevel): New state flags StatePendingResize, StateConfigureSize, StatePendingConfigureStates. New field `configuration_timer'. (batch_state_changes): New flag. (DestroyBacking): Clear configuration timer. (NoteConfigureTime): New function; apply queued state changes. (HandleWmStateChange, HandleConfigureEvent): By default, queue state changes and configure events so they can be applied in one go. (Unmap): Clear delayed configuration timer. (Commit, CommitInsideFrame, PostResize): Improve movement delta adjustment for min_width and min_height, and queue pending motion for after ack_configure. (XLInitXdgToplevels): Set `batch_state_changes' depending on the DIRECT_STATE_CHANGES' environment variable.
This commit is contained in:
parent
40d19a4595
commit
112c4d4fbe
11 changed files with 1065 additions and 177 deletions
77
12to11.man
77
12to11.man
|
@ -9,6 +9,83 @@ starts a Wayland compositor on the next available socket;
|
||||||
Wayland programs will then be displayed through the X server.
|
Wayland programs will then be displayed through the X server.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
None.
|
None.
|
||||||
|
.SH ENVIRONMENT
|
||||||
|
Several environment variables exist that modify the behavior of the
|
||||||
|
protocol translator in one way or another. Most of these are used for
|
||||||
|
debugging, but some may be relevant to users as well.
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B USE_BUILTIN_RESIZE
|
||||||
|
environment variable, if set, forces the use of the built-in
|
||||||
|
drag-to-resize support provided by the protocol translator, even if
|
||||||
|
the X window manager provides its own.
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B DEBUG_REFRESH_PREDICTION
|
||||||
|
environment variable, if set, forces the frame clock to predict the
|
||||||
|
presentation deadline of the X compositing manager. This is used to
|
||||||
|
debug code that is otherwise not run without subsurfaces being
|
||||||
|
present.
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B DISABLE_FRAME_SYNCHRONIZATION
|
||||||
|
environment variable, if set, disables frame synchronization with the
|
||||||
|
X compositing manager. Setting this variable is probably not a good
|
||||||
|
idea.
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B SYNCHRONIZE
|
||||||
|
environment variable, if set, causes the X library to check for errors
|
||||||
|
immediately after issuing a request. The resulting backtraces from
|
||||||
|
the error handler then reflect the protocol request that actually
|
||||||
|
caused the error, instead of some unpredictable request in the future.
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B APPLY_STATE_WORKAROUND
|
||||||
|
environment variable, if set, causes the protocol translator to handle
|
||||||
|
toplevel window state changes differently. If Wayland programs do not
|
||||||
|
make the transition between fullscreen and maximized states correctly,
|
||||||
|
try setting this variable.
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B GLOBAL_SCALE
|
||||||
|
environment variable, if set to an integer, overrides the global
|
||||||
|
program scale factor normally provided by the
|
||||||
|
.I Gdk/WindowScalingFactor
|
||||||
|
X setting.
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B OUTPUT_SCALE
|
||||||
|
environment variable, if set to an integer, overrides the output scale
|
||||||
|
factor that is otherwise set to the global program scale factor.
|
||||||
|
Setting this option is only useful to debug Wayland program
|
||||||
|
downscaling.
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B DIRECT_STATE_CHANGES
|
||||||
|
variable, if set, forces ConfigureNotify events from the window
|
||||||
|
manager to be handled directly, without waiting some time for a
|
||||||
|
corresponding _NET_WM_STATE event to arrive. This is only
|
||||||
|
useful for debugging the window resizing logic.
|
||||||
|
.SH BUGS
|
||||||
|
There is a hard to catch bug where Wayland programs leaving the
|
||||||
|
fullscreen or maximized state may abruptly return to their maximized
|
||||||
|
size. Setting the
|
||||||
|
.B APPLY_STATE_WORKAROUND
|
||||||
|
environment variable may help.
|
||||||
|
.PP
|
||||||
|
Mozilla Firefox also does not work correctly. Resizing the Firefox
|
||||||
|
window leads to screen tearing, and Firefox often resizes its toplevel
|
||||||
|
windows outside their normal boundaries, causing invalid screen
|
||||||
|
contents to be displayed over other applications.
|
||||||
|
.PP
|
||||||
|
Using this protocol translator under a window manager that does not at
|
||||||
|
least support the
|
||||||
|
.B _NET_WM_SYNC_REQUEST
|
||||||
|
and
|
||||||
|
.B _NET_WM_STATE
|
||||||
|
window manager hints will result in Wayland programs running
|
||||||
|
incorrectly.
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
X(1), Xorg(1)
|
X(1), Xorg(1)
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
|
|
|
@ -68,7 +68,8 @@ transfer_atoms.h: short_types.txt mime0.awk mime1.awk mime2.awk mime3.awk \
|
||||||
awk -f mime3.awk short_types.txt >> $@
|
awk -f mime3.awk short_types.txt >> $@
|
||||||
awk -f mime4.awk short_types.txt >> $@
|
awk -f mime4.awk short_types.txt >> $@
|
||||||
|
|
||||||
$(OBJS): transfer_atoms.h
|
$(OBJS): transfer_atoms.h primary-selection-unstable-v1.h \
|
||||||
|
linux-dmabuf-unstable-v1.h xdg-shell.h
|
||||||
|
|
||||||
linux-dmabuf-unstable-v1.h: linux-dmabuf-unstable-v1.xml
|
linux-dmabuf-unstable-v1.h: linux-dmabuf-unstable-v1.xml
|
||||||
$(WAYLAND_SCANNER) server-header $< $@
|
$(WAYLAND_SCANNER) server-header $< $@
|
||||||
|
|
4
README
4
README
|
@ -2,8 +2,8 @@ This is a tool for running Wayland applications on an X server,
|
||||||
preferably with a compositing manager running.
|
preferably with a compositing manager running.
|
||||||
|
|
||||||
It is not yet complete. What is not yet implemented includes support
|
It is not yet complete. What is not yet implemented includes support
|
||||||
for the primary selection, touchscreens, input methods, device
|
for touchscreens, input methods, device switching in dmabuf feedback,
|
||||||
switching in dmabuf feedback, and the viewporter protocol extension.
|
and the viewporter protocol extension.
|
||||||
|
|
||||||
There are also problems with output reporting in subsurfaces.
|
There are also problems with output reporting in subsurfaces.
|
||||||
|
|
||||||
|
|
15
compositor.h
15
compositor.h
|
@ -86,6 +86,10 @@ struct _Compositor
|
||||||
|
|
||||||
typedef struct _Seat Seat;
|
typedef struct _Seat Seat;
|
||||||
|
|
||||||
|
/* Forward declarations from primary_selection.c. */
|
||||||
|
|
||||||
|
typedef struct _PDataSource PDataSource;
|
||||||
|
|
||||||
/* Defined in 12to11.c. */
|
/* Defined in 12to11.c. */
|
||||||
|
|
||||||
extern Compositor compositor;
|
extern Compositor compositor;
|
||||||
|
@ -703,6 +707,7 @@ extern void *XLXdgRoleRunOnReconstrain (Role *, void (*) (void *, XEvent *),
|
||||||
extern void XLXdgRoleCancelReconstrainCallback (void *);
|
extern void XLXdgRoleCancelReconstrainCallback (void *);
|
||||||
extern void XLXdgRoleReconstrain (Role *, XEvent *);
|
extern void XLXdgRoleReconstrain (Role *, XEvent *);
|
||||||
extern void XLXdgRoleMoveBy (Role *, int, int);
|
extern void XLXdgRoleMoveBy (Role *, int, int);
|
||||||
|
extern void XLXdgRoleNoteRejectedConfigure (Role *);
|
||||||
|
|
||||||
extern Window XLWindowFromXdgRole (Role *);
|
extern Window XLWindowFromXdgRole (Role *);
|
||||||
extern Subcompositor *XLSubcompositorFromXdgRole (Role *);
|
extern Subcompositor *XLSubcompositorFromXdgRole (Role *);
|
||||||
|
@ -871,6 +876,7 @@ extern void *XLSeatAddModifierCallback (Seat *, void (*) (unsigned int, void *),
|
||||||
void *);
|
void *);
|
||||||
extern void XLSeatRemoveModifierCallback (void *);
|
extern void XLSeatRemoveModifierCallback (void *);
|
||||||
extern unsigned int XLSeatGetEffectiveModifiers (Seat *);
|
extern unsigned int XLSeatGetEffectiveModifiers (Seat *);
|
||||||
|
extern Bool XLSeatResizeInProgress (Seat *);
|
||||||
|
|
||||||
extern Cursor InitDefaultCursor (void);
|
extern Cursor InitDefaultCursor (void);
|
||||||
|
|
||||||
|
@ -933,6 +939,7 @@ extern void XLNoteSourceDestroyed (DataSource *);
|
||||||
extern Bool XLNoteLocalSelection (Seat *, DataSource *);
|
extern Bool XLNoteLocalSelection (Seat *, DataSource *);
|
||||||
extern void XLReceiveDataFromSelection (Time, Atom, Atom, int);
|
extern void XLReceiveDataFromSelection (Time, Atom, Atom, int);
|
||||||
extern Bool XLOwnDragSelection (Time, DataSource *);
|
extern Bool XLOwnDragSelection (Time, DataSource *);
|
||||||
|
extern void XLNotePrimaryDestroyed (PDataSource *);
|
||||||
|
|
||||||
extern void XLInitXData (void);
|
extern void XLInitXData (void);
|
||||||
|
|
||||||
|
@ -968,7 +975,15 @@ 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 XLClearForeignPrimary (Time);
|
||||||
|
extern Bool XLNoteLocalPrimary (Seat *, PDataSource *);
|
||||||
extern void XLPrimarySelectionHandleFocusChange (Seat *);
|
extern void XLPrimarySelectionHandleFocusChange (Seat *);
|
||||||
|
extern struct wl_resource *XLResourceFromPDataSource (PDataSource *);
|
||||||
|
extern Bool XLPDataSourceHasAtomTarget (PDataSource *, Atom);
|
||||||
|
extern Bool XLPDataSourceHasTarget (PDataSource *, const char *);
|
||||||
|
extern int XLPDataSourceTargetCount (PDataSource *);
|
||||||
|
extern void XLPDataSourceGetTargets (PDataSource *, Atom *);
|
||||||
|
|
||||||
/* Utility functions that don't belong in a specific file. */
|
/* Utility functions that don't belong in a specific file. */
|
||||||
|
|
||||||
|
|
|
@ -452,7 +452,7 @@ DataOfferSetActions (struct wl_client *client, struct wl_resource *resource,
|
||||||
|
|
||||||
if (!(offer->state & IsDragAndDrop))
|
if (!(offer->state & IsDragAndDrop))
|
||||||
{
|
{
|
||||||
wl_resource_post_error (resource, WL_DATA_OFFER_ERROR_INVALID_FINISH,
|
wl_resource_post_error (resource, WL_DATA_OFFER_ERROR_INVALID_OFFER,
|
||||||
"trying to finish non-drag-and-drop data offer");
|
"trying to finish non-drag-and-drop data offer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
3
dnd.c
3
dnd.c
|
@ -30,9 +30,6 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
Drags between Wayland clients are implemented in seat.c and
|
Drags between Wayland clients are implemented in seat.c and
|
||||||
data_device.c instead. */
|
data_device.c instead. */
|
||||||
|
|
||||||
/* TODO: Handle XdndActionAsk and allow using keyboard modifiers to
|
|
||||||
change the selected action. Then, update README. */
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
XdndProtocolVersion = 5,
|
XdndProtocolVersion = 5,
|
||||||
|
|
|
@ -24,7 +24,6 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
#include "primary-selection-unstable-v1.h"
|
#include "primary-selection-unstable-v1.h"
|
||||||
|
|
||||||
typedef struct _PDataDevice PDataDevice;
|
typedef struct _PDataDevice PDataDevice;
|
||||||
typedef struct _PDataSource PDataSource;
|
|
||||||
typedef struct _PDataOffer PDataOffer;
|
typedef struct _PDataOffer PDataOffer;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -74,6 +73,9 @@ struct _PDataSource
|
||||||
/* List of all MIME types provided by this source. */
|
/* List of all MIME types provided by this source. */
|
||||||
XLList *mime_types;
|
XLList *mime_types;
|
||||||
|
|
||||||
|
/* List of atoms provided by this source. */
|
||||||
|
XIDList *atom_types;
|
||||||
|
|
||||||
/* Number of MIME types provided by this source. */
|
/* Number of MIME types provided by this source. */
|
||||||
int n_mime_types;
|
int n_mime_types;
|
||||||
};
|
};
|
||||||
|
@ -90,6 +92,17 @@ static uint32_t last_change_serial;
|
||||||
/* All data devices. */
|
/* All data devices. */
|
||||||
static PDataDevice all_devices;
|
static PDataDevice all_devices;
|
||||||
|
|
||||||
|
/* A special PDataSource meaning that the primary selection is a
|
||||||
|
foreign selection. */
|
||||||
|
static PDataSource foreign_selection_key;
|
||||||
|
|
||||||
|
/* Functions to call to obtain zwp_primary_selection_v1_offer
|
||||||
|
resources and send associated data for foreign selections. */
|
||||||
|
static CreateOfferFuncs foreign_selection_functions;
|
||||||
|
|
||||||
|
/* The time the foreign selection was set. */
|
||||||
|
static Time foreign_selection_time;
|
||||||
|
|
||||||
/* Forward declaration. */
|
/* Forward declaration. */
|
||||||
|
|
||||||
static void NoticeChanged (void);
|
static void NoticeChanged (void);
|
||||||
|
@ -224,6 +237,8 @@ Offer (struct wl_client *client, struct wl_resource *resource,
|
||||||
this source. */
|
this source. */
|
||||||
source->mime_types = XLListPrepend (source->mime_types,
|
source->mime_types = XLListPrepend (source->mime_types,
|
||||||
XLStrdup (mime_type));
|
XLStrdup (mime_type));
|
||||||
|
source->atom_types = XIDListPrepend (source->atom_types,
|
||||||
|
InternAtom (mime_type));
|
||||||
source->n_mime_types++;
|
source->n_mime_types++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,6 +274,10 @@ HandleSourceResourceDestroy (struct wl_resource *resource)
|
||||||
|
|
||||||
/* And free the MIME types offered by the source. */
|
/* And free the MIME types offered by the source. */
|
||||||
XLListFree (source->mime_types, XLFree);
|
XLListFree (source->mime_types, XLFree);
|
||||||
|
XIDListFree (source->atom_types, NULL);
|
||||||
|
|
||||||
|
/* Maybe disown the primary selection. */
|
||||||
|
XLNotePrimaryDestroyed (source);
|
||||||
|
|
||||||
/* If source is the primary selection, clear it and send the change
|
/* If source is the primary selection, clear it and send the change
|
||||||
to all clients. */
|
to all clients. */
|
||||||
|
@ -272,10 +291,92 @@ HandleSourceResourceDestroy (struct wl_resource *resource)
|
||||||
XLFree (source);
|
XLFree (source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wl_resource *
|
||||||
|
XLResourceFromPDataSource (PDataSource *source)
|
||||||
|
{
|
||||||
|
return source->resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool
|
||||||
|
XLPDataSourceHasAtomTarget (PDataSource *source, Atom target)
|
||||||
|
{
|
||||||
|
XIDList *list;
|
||||||
|
|
||||||
|
for (list = source->atom_types; list; list = list->next)
|
||||||
|
{
|
||||||
|
if (list->data == target)
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool
|
||||||
|
XLPDataSourceHasTarget (PDataSource *source, const char *mime_type)
|
||||||
|
{
|
||||||
|
XLList *list;
|
||||||
|
|
||||||
|
for (list = source->mime_types; list; list = list->next)
|
||||||
|
{
|
||||||
|
if (!strcmp (list->data, mime_type))
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
XLPDataSourceTargetCount (PDataSource *source)
|
||||||
|
{
|
||||||
|
return source->n_mime_types;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
XLPDataSourceGetTargets (PDataSource *source, Atom *targets)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
XIDList *list;
|
||||||
|
|
||||||
|
list = source->atom_types;
|
||||||
|
|
||||||
|
for (i = 0; i < source->n_mime_types; ++i)
|
||||||
|
{
|
||||||
|
targets[i] = list->data;
|
||||||
|
list = list->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Device implementation. */
|
/* Device implementation. */
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
UpdateSingleReferenceWithForeignOffer (struct wl_client *client,
|
||||||
|
PDataDevice *reference)
|
||||||
|
{
|
||||||
|
struct wl_resource *scratch, *resource;
|
||||||
|
Time time;
|
||||||
|
|
||||||
|
time = foreign_selection_time;
|
||||||
|
resource = foreign_selection_functions.create_offer (client, time);
|
||||||
|
scratch = reference->resource;
|
||||||
|
|
||||||
|
if (!resource)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Make the data offer known to the client. */
|
||||||
|
zwp_primary_selection_device_v1_send_data_offer (scratch, resource);
|
||||||
|
|
||||||
|
/* Tell the foreign selection provider to send supported resources
|
||||||
|
to the client. */
|
||||||
|
foreign_selection_functions.send_offers (resource, time);
|
||||||
|
|
||||||
|
/* Finally, tell the client that the offer is a selection. */
|
||||||
|
zwp_primary_selection_device_v1_send_selection (scratch, resource);
|
||||||
|
|
||||||
|
/* Set WasSentOffer since an offer was sent. */
|
||||||
|
reference->flags |= WasSentOffer;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
UpdateForSingleReference (PDataDevice *reference)
|
UpdateForSingleReference (PDataDevice *reference)
|
||||||
{
|
{
|
||||||
|
@ -302,6 +403,15 @@ UpdateForSingleReference (PDataDevice *reference)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If the primary selection is foreign, call the foreign selection
|
||||||
|
functions. */
|
||||||
|
if (primary_selection == &foreign_selection_key)
|
||||||
|
{
|
||||||
|
UpdateSingleReferenceWithForeignOffer (client, reference);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Otherwise, create a data offer for that client. */
|
/* Otherwise, create a data offer for that client. */
|
||||||
offer = AddDataOffer (client, primary_selection);
|
offer = AddDataOffer (client, primary_selection);
|
||||||
|
|
||||||
|
@ -392,8 +502,16 @@ static void
|
||||||
SetSelection (struct wl_client *client, struct wl_resource *resource,
|
SetSelection (struct wl_client *client, struct wl_resource *resource,
|
||||||
struct wl_resource *source_resource, uint32_t serial)
|
struct wl_resource *source_resource, uint32_t serial)
|
||||||
{
|
{
|
||||||
|
PDataDevice *device;
|
||||||
|
PDataSource *source;
|
||||||
struct wl_resource *scratch;
|
struct wl_resource *scratch;
|
||||||
|
|
||||||
|
device = wl_resource_get_user_data (resource);
|
||||||
|
|
||||||
|
if (!device->seat)
|
||||||
|
/* This device is inert, since the seat has been deleted. */
|
||||||
|
return;
|
||||||
|
|
||||||
if (serial < last_change_serial)
|
if (serial < last_change_serial)
|
||||||
/* This change is out of date. Do nothing. */
|
/* This change is out of date. Do nothing. */
|
||||||
return;
|
return;
|
||||||
|
@ -401,19 +519,34 @@ SetSelection (struct wl_client *client, struct wl_resource *resource,
|
||||||
/* Otherwise, set the primary selection. */
|
/* Otherwise, set the primary selection. */
|
||||||
last_change_serial = serial;
|
last_change_serial = serial;
|
||||||
|
|
||||||
|
if (source_resource)
|
||||||
|
source = wl_resource_get_user_data (source_resource);
|
||||||
|
else
|
||||||
|
source = NULL;
|
||||||
|
|
||||||
|
/* Try to own the X primary selection. If it fails, refrain from
|
||||||
|
changing the current selection data. */
|
||||||
|
if (!XLNoteLocalPrimary (device->seat, source))
|
||||||
|
return;
|
||||||
|
|
||||||
if (primary_selection)
|
if (primary_selection)
|
||||||
{
|
{
|
||||||
/* The primary selection already exists. Send the cancelled
|
/* The primary selection already exists. Send the cancelled
|
||||||
event and clear the primary selection variable. */
|
event and clear the primary selection variable. */
|
||||||
scratch = primary_selection->resource;
|
scratch = primary_selection->resource;
|
||||||
primary_selection = NULL;
|
|
||||||
|
|
||||||
|
if (primary_selection != &foreign_selection_key)
|
||||||
|
/* The special foreign selection has no associated
|
||||||
|
wl_resource. */
|
||||||
zwp_primary_selection_source_v1_send_cancelled (scratch);
|
zwp_primary_selection_source_v1_send_cancelled (scratch);
|
||||||
|
|
||||||
|
/* Clear primary_selection. */
|
||||||
|
primary_selection = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source_resource)
|
if (source_resource)
|
||||||
/* Make source_resource the new primary selection. */
|
/* Make source_resource the new primary selection. */
|
||||||
primary_selection = wl_resource_get_user_data (source_resource);
|
primary_selection = source;
|
||||||
|
|
||||||
/* Now, send the updated primary selection to clients. */
|
/* Now, send the updated primary selection to clients. */
|
||||||
NoticeChanged ();
|
NoticeChanged ();
|
||||||
|
@ -585,6 +718,56 @@ HandleBind (struct wl_client *client, void *data, uint32_t version,
|
||||||
wl_resource_set_implementation (resource, &manager_impl, NULL, NULL);
|
wl_resource_set_implementation (resource, &manager_impl, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
XLSetForeignPrimary (Time time, CreateOfferFuncs functions)
|
||||||
|
{
|
||||||
|
uint32_t serial;
|
||||||
|
struct wl_resource *scratch;
|
||||||
|
|
||||||
|
if (time < foreign_selection_time)
|
||||||
|
return;
|
||||||
|
|
||||||
|
serial = wl_display_next_serial (compositor.wl_display);
|
||||||
|
|
||||||
|
/* Use this serial to prevent clients from changing the selection
|
||||||
|
again until the next event is sent. */
|
||||||
|
last_change_serial = serial;
|
||||||
|
|
||||||
|
foreign_selection_time = time;
|
||||||
|
foreign_selection_functions = functions;
|
||||||
|
|
||||||
|
if (primary_selection
|
||||||
|
&& primary_selection != &foreign_selection_key)
|
||||||
|
{
|
||||||
|
scratch = primary_selection->resource;
|
||||||
|
zwp_primary_selection_source_v1_send_cancelled (scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use a special value of primary_selection to mean that foreign
|
||||||
|
selections are in use. */
|
||||||
|
primary_selection = &foreign_selection_key;
|
||||||
|
|
||||||
|
/* Send new data offers to current clients. */
|
||||||
|
NoticeChanged ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
XLClearForeignPrimary (Time time)
|
||||||
|
{
|
||||||
|
if (time < foreign_selection_time)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (primary_selection == &foreign_selection_key)
|
||||||
|
{
|
||||||
|
primary_selection = NULL;
|
||||||
|
NoticeChanged ();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign_selection_time = time;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
XLInitPrimarySelection (void)
|
XLInitPrimarySelection (void)
|
||||||
{
|
{
|
||||||
|
|
6
seat.c
6
seat.c
|
@ -5129,3 +5129,9 @@ XLSeatGetEffectiveModifiers (Seat *seat)
|
||||||
{
|
{
|
||||||
return seat->base | seat->locked | seat->latched;
|
return seat->base | seat->locked | seat->latched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool
|
||||||
|
XLSeatResizeInProgress (Seat *seat)
|
||||||
|
{
|
||||||
|
return seat->resize_in_progress;
|
||||||
|
}
|
||||||
|
|
604
xdata.c
604
xdata.c
|
@ -30,10 +30,11 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
#include <iconv.h>
|
#include <iconv.h>
|
||||||
|
|
||||||
#include "compositor.h"
|
#include "compositor.h"
|
||||||
|
#include "primary-selection-unstable-v1.h"
|
||||||
|
|
||||||
#include <X11/extensions/Xfixes.h>
|
#include <X11/extensions/Xfixes.h>
|
||||||
|
|
||||||
/* X11 data source for Wayland clients. */
|
/* X11 data transfer to and from Wayland clients. */
|
||||||
|
|
||||||
typedef struct _ReadTargetsData ReadTargetsData;
|
typedef struct _ReadTargetsData ReadTargetsData;
|
||||||
typedef struct _TargetMapping TargetMapping;
|
typedef struct _TargetMapping TargetMapping;
|
||||||
|
@ -51,6 +52,9 @@ struct _ReadTargetsData
|
||||||
|
|
||||||
/* Number of atoms read. */
|
/* Number of atoms read. */
|
||||||
int n_atoms;
|
int n_atoms;
|
||||||
|
|
||||||
|
/* What selection is being read from. */
|
||||||
|
Atom selection;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _TargetMapping
|
struct _TargetMapping
|
||||||
|
@ -88,7 +92,11 @@ struct _DataConversion
|
||||||
Atom atom;
|
Atom atom;
|
||||||
|
|
||||||
/* An alternative GetClipboardCallback, if any. */
|
/* An alternative GetClipboardCallback, if any. */
|
||||||
GetDataFunc (*clipboard_callback) (WriteTransfer *, Atom, Atom *);
|
GetDataFunc (*clipboard_callback) (WriteTransfer *, Atom, Atom *,
|
||||||
|
struct wl_resource *,
|
||||||
|
void (*) (struct wl_resource *,
|
||||||
|
const char *, int),
|
||||||
|
Bool);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -243,12 +251,21 @@ static Time last_x_selection_change;
|
||||||
time any client did that. */
|
time any client did that. */
|
||||||
static Time last_clipboard_time, last_clipboard_change;
|
static Time last_clipboard_time, last_clipboard_change;
|
||||||
|
|
||||||
|
/* The last time ownership over PRIMARY changed. */
|
||||||
|
static Time last_primary_time;
|
||||||
|
|
||||||
/* The currently supported selection targets. */
|
/* The currently supported selection targets. */
|
||||||
static Atom *x_selection_targets;
|
static Atom *x_selection_targets;
|
||||||
|
|
||||||
/* The number of targets in that array. */
|
/* The number of targets in that array. */
|
||||||
static int num_x_selection_targets;
|
static int num_x_selection_targets;
|
||||||
|
|
||||||
|
/* The currently supported primary selection targets. */
|
||||||
|
static Atom *x_primary_targets;
|
||||||
|
|
||||||
|
/* The number of targets in that array. */
|
||||||
|
static int num_x_primary_targets;
|
||||||
|
|
||||||
/* Data source currently being used to provide the X clipboard. */
|
/* Data source currently being used to provide the X clipboard. */
|
||||||
static DataSource *selection_data_source;
|
static DataSource *selection_data_source;
|
||||||
|
|
||||||
|
@ -256,6 +273,10 @@ static DataSource *selection_data_source;
|
||||||
selection. */
|
selection. */
|
||||||
static DataSource *drag_data_source;
|
static DataSource *drag_data_source;
|
||||||
|
|
||||||
|
/* Data source currently being used to provide the primary
|
||||||
|
selection. */
|
||||||
|
static PDataSource *primary_data_source;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
||||||
static void __attribute__ ((__format__ (gnu_printf, 1, 2)))
|
static void __attribute__ ((__format__ (gnu_printf, 1, 2)))
|
||||||
|
@ -327,10 +348,23 @@ SetupMappingTable (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static Bool
|
static Bool
|
||||||
HasSelectionTarget (Atom atom)
|
HasSelectionTarget (Atom atom, Bool is_primary)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (is_primary)
|
||||||
|
{
|
||||||
|
/* Do this for the primary selection instead. */
|
||||||
|
|
||||||
|
for (i = 0; i < num_x_primary_targets; ++i)
|
||||||
|
{
|
||||||
|
if (x_primary_targets[i] == atom)
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < num_x_selection_targets; ++i)
|
for (i = 0; i < num_x_selection_targets; ++i)
|
||||||
{
|
{
|
||||||
if (x_selection_targets[i] == atom)
|
if (x_selection_targets[i] == atom)
|
||||||
|
@ -341,7 +375,7 @@ HasSelectionTarget (Atom atom)
|
||||||
}
|
}
|
||||||
|
|
||||||
static TargetMapping *
|
static TargetMapping *
|
||||||
FindTranslationForMimeType (const char *mime_type)
|
FindTranslationForMimeType (const char *mime_type, Bool is_primary)
|
||||||
{
|
{
|
||||||
unsigned short *buckets, i;
|
unsigned short *buckets, i;
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
|
@ -349,13 +383,22 @@ FindTranslationForMimeType (const char *mime_type)
|
||||||
idx = HashMimeString (mime_type) % 32;
|
idx = HashMimeString (mime_type) % 32;
|
||||||
buckets = mapping_table.buckets[idx];
|
buckets = mapping_table.buckets[idx];
|
||||||
|
|
||||||
|
DebugPrint ("Looking for translation of MIME type %s\n",
|
||||||
|
mime_type);
|
||||||
|
|
||||||
for (i = 0; i < mapping_table.n_elements[idx]; ++i)
|
for (i = 0; i < mapping_table.n_elements[idx]; ++i)
|
||||||
{
|
{
|
||||||
if (!strcmp (direct_transfer[buckets[i]].mime_type,
|
if (!strcmp (direct_transfer[buckets[i]].mime_type,
|
||||||
mime_type)
|
mime_type)
|
||||||
&& HasSelectionTarget (MappingAtom (&direct_transfer[buckets[i]])))
|
&& HasSelectionTarget (MappingAtom (&direct_transfer[buckets[i]]),
|
||||||
|
is_primary))
|
||||||
|
{
|
||||||
|
DebugPrint ("Found translation for MIME type %s\n",
|
||||||
|
mime_type);
|
||||||
|
|
||||||
return &direct_transfer[buckets[i]];
|
return &direct_transfer[buckets[i]];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -644,34 +687,40 @@ 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) \
|
||||||
|
Time time; \
|
||||||
|
TargetMapping *translation; \
|
||||||
|
\
|
||||||
|
DebugPrint ("Receiving %s from X " #selection " \n", \
|
||||||
|
mime_type); \
|
||||||
|
\
|
||||||
|
/* 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); \
|
||||||
|
\
|
||||||
|
/* Find which selection target corresponds to MIME_TYPE. */ \
|
||||||
|
translation = FindTranslationForMimeType (mime_type, primary); \
|
||||||
|
\
|
||||||
|
if (translation) \
|
||||||
|
{ \
|
||||||
|
if (!translation->translation_func) \
|
||||||
|
/* If a corresponding target exists, ask to receive it. */ \
|
||||||
|
PostReceiveDirect (time, selection, \
|
||||||
|
MappingAtom (translation), fd); \
|
||||||
|
else \
|
||||||
|
/* Otherwise, use the translation function. */ \
|
||||||
|
translation->translation_func (time, selection, \
|
||||||
|
MappingAtom (translation), \
|
||||||
|
fd); \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
close (fd)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
Receive (struct wl_client *client, struct wl_resource *resource,
|
Receive (struct wl_client *client, struct wl_resource *resource,
|
||||||
const char *mime_type, int fd)
|
const char *mime_type, int fd)
|
||||||
{
|
{
|
||||||
Time time;
|
ReceiveBody (CLIPBOARD, False);
|
||||||
TargetMapping *translation;
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Find which selection target corresponds to MIME_TYPE. */
|
|
||||||
translation = FindTranslationForMimeType (mime_type);
|
|
||||||
|
|
||||||
if (translation)
|
|
||||||
{
|
|
||||||
if (!translation->translation_func)
|
|
||||||
/* If a corresponding target exists, ask to receive it. */
|
|
||||||
PostReceiveDirect (time, CLIPBOARD,
|
|
||||||
MappingAtom (translation), fd);
|
|
||||||
else
|
|
||||||
/* Otherwise, use the translation function. */
|
|
||||||
translation->translation_func (time, CLIPBOARD,
|
|
||||||
MappingAtom (translation),
|
|
||||||
fd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
close (fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -683,14 +732,16 @@ Destroy (struct wl_client *client, struct wl_resource *resource)
|
||||||
static void
|
static void
|
||||||
Finish (struct wl_client *client, struct wl_resource *resource)
|
Finish (struct wl_client *client, struct wl_resource *resource)
|
||||||
{
|
{
|
||||||
/* TODO... */
|
wl_resource_post_error (resource, WL_DATA_OFFER_ERROR_INVALID_FINISH,
|
||||||
|
"trying to finish foreign data offer");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
SetActions (struct wl_client *client, struct wl_resource *resource,
|
SetActions (struct wl_client *client, struct wl_resource *resource,
|
||||||
uint32_t dnd_actions, uint32_t preferred_action)
|
uint32_t dnd_actions, uint32_t preferred_action)
|
||||||
{
|
{
|
||||||
/* TODO... */
|
wl_resource_post_error (resource, WL_DATA_OFFER_ERROR_INVALID_OFFER,
|
||||||
|
"trying to finish non-drag-and-drop data offer");
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_data_offer_interface wl_data_offer_impl =
|
static const struct wl_data_offer_interface wl_data_offer_impl =
|
||||||
|
@ -721,6 +772,38 @@ CreateOffer (struct wl_client *client, Time time)
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ReceivePrimary (struct wl_client *client, struct wl_resource *resource,
|
||||||
|
const char *mime_type, int fd)
|
||||||
|
{
|
||||||
|
ReceiveBody (XA_PRIMARY, True);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct zwp_primary_selection_offer_v1_interface primary_offer_impl =
|
||||||
|
{
|
||||||
|
.receive = ReceivePrimary,
|
||||||
|
.destroy = Destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wl_resource *
|
||||||
|
CreatePrimaryOffer (struct wl_client *client, Time time)
|
||||||
|
{
|
||||||
|
struct wl_resource *resource;
|
||||||
|
|
||||||
|
resource = wl_resource_create (client,
|
||||||
|
&zwp_primary_selection_offer_v1_interface,
|
||||||
|
1, 0);
|
||||||
|
if (!resource)
|
||||||
|
/* If allocating the resource failed, return NULL. */
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Otherwise, set the user_data to the time of the selection
|
||||||
|
change. */
|
||||||
|
wl_resource_set_implementation (resource, &primary_offer_impl,
|
||||||
|
(void *) time, NULL);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
static Bool
|
static Bool
|
||||||
CheckDuplicate (unsigned short index, Atom a)
|
CheckDuplicate (unsigned short index, Atom a)
|
||||||
{
|
{
|
||||||
|
@ -765,20 +848,17 @@ CheckDuplicate (unsigned short index, Atom a)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
SendOffers (struct wl_resource *resource, Time time)
|
SendOffers1 (struct wl_resource *resource, int ntargets, Atom *targets,
|
||||||
|
void (*send_offer_func) (struct wl_resource *, const char *))
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
unsigned short *buckets;
|
unsigned short *buckets;
|
||||||
|
|
||||||
if (time < last_x_selection_change)
|
for (i = 0; i < ntargets; ++i)
|
||||||
/* This offer is out of date. */
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (i = 0; i < num_x_selection_targets; ++i)
|
|
||||||
{
|
{
|
||||||
/* Offer each type corresponding to this target. */
|
/* Offer each type corresponding to this target. */
|
||||||
idx = x_selection_targets[i] % 16;
|
idx = targets[i] % 16;
|
||||||
|
|
||||||
/* N.B. that duplicates do appear in the atom buckets, which
|
/* N.B. that duplicates do appear in the atom buckets, which
|
||||||
is intentional. */
|
is intentional. */
|
||||||
|
@ -787,12 +867,11 @@ SendOffers (struct wl_resource *resource, Time time)
|
||||||
for (j = 0; j < mapping_table.n_atom_elements[idx]; ++j)
|
for (j = 0; j < mapping_table.n_atom_elements[idx]; ++j)
|
||||||
{
|
{
|
||||||
if (MappingIs (&direct_transfer[buckets[j]],
|
if (MappingIs (&direct_transfer[buckets[j]],
|
||||||
x_selection_targets[i])
|
targets[i])
|
||||||
&& CheckDuplicate (buckets[j],
|
&& CheckDuplicate (buckets[j], targets[i]))
|
||||||
x_selection_targets[i]))
|
|
||||||
/* If it exists and was not previously offered, offer it
|
/* If it exists and was not previously offered, offer it
|
||||||
to the client. */
|
to the client. */
|
||||||
wl_data_offer_send_offer (resource,
|
send_offer_func (resource,
|
||||||
direct_transfer[buckets[j]].mime_type);
|
direct_transfer[buckets[j]].mime_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -804,10 +883,62 @@ SendOffers (struct wl_resource *resource, Time time)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
HandleNewSelection (Time time, Atom *targets, int ntargets)
|
SendOffers (struct wl_resource *resource, Time time)
|
||||||
|
{
|
||||||
|
if (time < last_x_selection_change)
|
||||||
|
/* This offer is out of date. */
|
||||||
|
return;
|
||||||
|
|
||||||
|
SendOffers1 (resource, num_x_selection_targets,
|
||||||
|
x_selection_targets, wl_data_offer_send_offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
SendPrimaryOffers (struct wl_resource *resource, Time time)
|
||||||
|
{
|
||||||
|
if (time < last_primary_time)
|
||||||
|
/* This offer is out of date. */
|
||||||
|
return;
|
||||||
|
|
||||||
|
SendOffers1 (resource, num_x_primary_targets,
|
||||||
|
x_primary_targets,
|
||||||
|
zwp_primary_selection_offer_v1_send_offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
HandleNewSelection (Time time, Atom selection, Atom *targets,
|
||||||
|
int ntargets)
|
||||||
{
|
{
|
||||||
CreateOfferFuncs funcs;
|
CreateOfferFuncs funcs;
|
||||||
|
|
||||||
|
if (selection == XA_PRIMARY)
|
||||||
|
{
|
||||||
|
/* The primary selection changed, and now has the given
|
||||||
|
targets. */
|
||||||
|
|
||||||
|
if (time < last_primary_time)
|
||||||
|
{
|
||||||
|
XLFree (targets);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up the new primary targets. */
|
||||||
|
XLFree (x_primary_targets);
|
||||||
|
x_primary_targets = targets;
|
||||||
|
num_x_primary_targets = ntargets;
|
||||||
|
last_primary_time = 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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Else, the selection that changed is CLIPBOARD. */
|
||||||
|
|
||||||
/* Ignore outdated selection changes. */
|
/* Ignore outdated selection changes. */
|
||||||
if (time < last_x_selection_change)
|
if (time < last_x_selection_change)
|
||||||
{
|
{
|
||||||
|
@ -888,13 +1019,14 @@ TargetsFinishCallback (ReadTransfer *transfer, Bool success)
|
||||||
data = GetTransferData (transfer);
|
data = GetTransferData (transfer);
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
DebugPrint ("Received targets from CLIPBOARD\n");
|
DebugPrint ("Received targets from %lu\n", data->selection);
|
||||||
else
|
else
|
||||||
DebugPrint ("Failed to obtain targets from CLIPBOARD\n");
|
DebugPrint ("Failed to obtain targets\n");
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
HandleNewSelection (GetTransferTime (transfer),
|
HandleNewSelection (GetTransferTime (transfer),
|
||||||
data->atoms, data->n_atoms);
|
data->selection, data->atoms,
|
||||||
|
data->n_atoms);
|
||||||
else
|
else
|
||||||
/* HandleNewSelection keeps data->atoms around for a while. */
|
/* HandleNewSelection keeps data->atoms around for a while. */
|
||||||
XLFree (data->atoms);
|
XLFree (data->atoms);
|
||||||
|
@ -913,12 +1045,28 @@ NoticeClipboardChanged (Time time)
|
||||||
ReadTargetsData *data;
|
ReadTargetsData *data;
|
||||||
|
|
||||||
data = XLCalloc (1, sizeof *data);
|
data = XLCalloc (1, sizeof *data);
|
||||||
|
data->selection = CLIPBOARD;
|
||||||
|
|
||||||
ConvertSelectionFuncs (CLIPBOARD, TARGETS, time, data,
|
ConvertSelectionFuncs (CLIPBOARD, TARGETS, time, data,
|
||||||
NULL, TargetsReadCallback,
|
NULL, TargetsReadCallback,
|
||||||
TargetsFinishCallback);
|
TargetsFinishCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The same, but for the primary selection. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
NoticePrimaryChanged (Time time)
|
||||||
|
{
|
||||||
|
ReadTargetsData *data;
|
||||||
|
|
||||||
|
data = XLCalloc (1, sizeof *data);
|
||||||
|
data->selection = XA_PRIMARY;
|
||||||
|
|
||||||
|
ConvertSelectionFuncs (XA_PRIMARY, TARGETS, time, data,
|
||||||
|
NULL, TargetsReadCallback,
|
||||||
|
TargetsFinishCallback);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
NoticeClipboardCleared (Time time)
|
NoticeClipboardCleared (Time time)
|
||||||
{
|
{
|
||||||
|
@ -926,8 +1074,33 @@ NoticeClipboardCleared (Time time)
|
||||||
if (time < last_x_selection_change)
|
if (time < last_x_selection_change)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
DebugPrint ("CLIPBOARD was cleared at %lu\n", time);
|
||||||
|
|
||||||
last_x_selection_change = time;
|
last_x_selection_change = time;
|
||||||
XLClearForeignSelection (time);
|
XLClearForeignSelection (time);
|
||||||
|
|
||||||
|
/* Free data that is no longer used. */
|
||||||
|
XLFree (x_selection_targets);
|
||||||
|
x_selection_targets = NULL;
|
||||||
|
num_x_selection_targets = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
NoticePrimaryCleared (Time time)
|
||||||
|
{
|
||||||
|
/* Ignore outdated events. */
|
||||||
|
if (time < last_primary_time)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DebugPrint ("PRIMARY was cleared at %lu\n", time);
|
||||||
|
|
||||||
|
last_primary_time = time;
|
||||||
|
XLClearForeignPrimary (time);
|
||||||
|
|
||||||
|
/* Free data that is no longer used. */
|
||||||
|
XLFree (x_primary_targets);
|
||||||
|
x_primary_targets = NULL;
|
||||||
|
num_x_primary_targets = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -943,11 +1116,20 @@ HandleSelectionNotify (XFixesSelectionNotifyEvent *event)
|
||||||
disowning the selection were successful. */
|
disowning the selection were successful. */
|
||||||
last_clipboard_change = event->selection_timestamp;
|
last_clipboard_change = event->selection_timestamp;
|
||||||
|
|
||||||
|
if (event->selection == XA_PRIMARY
|
||||||
|
&& event->selection_timestamp > last_primary_time)
|
||||||
|
last_primary_time = event->selection_timestamp;
|
||||||
|
|
||||||
if (event->owner != None
|
if (event->owner != None
|
||||||
&& event->selection == CLIPBOARD)
|
&& event->selection == CLIPBOARD)
|
||||||
NoticeClipboardChanged (event->timestamp);
|
NoticeClipboardChanged (event->timestamp);
|
||||||
else
|
else if (event->selection == CLIPBOARD)
|
||||||
NoticeClipboardCleared (event->timestamp);
|
NoticeClipboardCleared (event->timestamp);
|
||||||
|
else if (event->owner != None
|
||||||
|
&& event->selection == XA_PRIMARY)
|
||||||
|
NoticePrimaryChanged (event->timestamp);
|
||||||
|
else if (event->selection == XA_PRIMARY)
|
||||||
|
NoticePrimaryCleared (event->timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool
|
Bool
|
||||||
|
@ -1001,17 +1183,25 @@ GetDataConversion (Atom target)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
MimeTypeFromTarget (Atom target)
|
MimeTypeFromTarget (Atom target, Bool primary)
|
||||||
{
|
{
|
||||||
DataConversion *conversion;
|
DataConversion *conversion;
|
||||||
static char *string;
|
static char *string;
|
||||||
|
Bool missing_type;
|
||||||
|
|
||||||
/* TODO: replace XGetAtomName with something more efficient. */
|
/* TODO: replace XGetAtomName with something more efficient. */
|
||||||
if (string)
|
if (string)
|
||||||
XFree (string);
|
XFree (string);
|
||||||
string = NULL;
|
string = NULL;
|
||||||
|
|
||||||
if (!XLDataSourceHasAtomTarget (selection_data_source, target))
|
if (!primary)
|
||||||
|
missing_type = !XLDataSourceHasAtomTarget (selection_data_source,
|
||||||
|
target);
|
||||||
|
else
|
||||||
|
missing_type = !XLPDataSourceHasAtomTarget (primary_data_source,
|
||||||
|
target);
|
||||||
|
|
||||||
|
if (missing_type)
|
||||||
{
|
{
|
||||||
/* If the data source does not itself provide the specified
|
/* If the data source does not itself provide the specified
|
||||||
target, then a conversion function is in use. */
|
target, then a conversion function is in use. */
|
||||||
|
@ -1032,11 +1222,19 @@ MimeTypeFromTarget (Atom target)
|
||||||
}
|
}
|
||||||
|
|
||||||
static Atom
|
static Atom
|
||||||
TypeFromTarget (Atom target)
|
TypeFromTarget (Atom target, Bool primary)
|
||||||
{
|
{
|
||||||
DataConversion *conversion;
|
DataConversion *conversion;
|
||||||
|
Bool missing_type;
|
||||||
|
|
||||||
if (!XLDataSourceHasAtomTarget (selection_data_source, target))
|
if (!primary)
|
||||||
|
missing_type = !XLDataSourceHasAtomTarget (selection_data_source,
|
||||||
|
target);
|
||||||
|
else
|
||||||
|
missing_type = !XLPDataSourceHasAtomTarget (primary_data_source,
|
||||||
|
target);
|
||||||
|
|
||||||
|
if (missing_type)
|
||||||
{
|
{
|
||||||
/* If the data source does not itself provide the specified
|
/* If the data source does not itself provide the specified
|
||||||
target, then a conversion function is in use. Use the type
|
target, then a conversion function is in use. Use the type
|
||||||
|
@ -1183,11 +1381,14 @@ GetClipboardCallback (WriteTransfer *transfer, Atom target,
|
||||||
WriteInfo *info;
|
WriteInfo *info;
|
||||||
int pipe[2], rc;
|
int pipe[2], rc;
|
||||||
DataConversion *conversion;
|
DataConversion *conversion;
|
||||||
|
struct wl_resource *resource;
|
||||||
|
|
||||||
/* If the selection data source is destroyed, the selection will be
|
/* If the selection data source is destroyed, the selection will be
|
||||||
disowned. */
|
disowned. */
|
||||||
XLAssert (selection_data_source != NULL);
|
XLAssert (selection_data_source != NULL);
|
||||||
|
|
||||||
|
resource = XLResourceFromDataSource (selection_data_source);
|
||||||
|
|
||||||
if (!XLDataSourceHasAtomTarget (selection_data_source, target))
|
if (!XLDataSourceHasAtomTarget (selection_data_source, target))
|
||||||
{
|
{
|
||||||
/* If the data source does not itself provide the specified
|
/* If the data source does not itself provide the specified
|
||||||
|
@ -1202,7 +1403,9 @@ GetClipboardCallback (WriteTransfer *transfer, Atom target,
|
||||||
|
|
||||||
if (conversion->clipboard_callback)
|
if (conversion->clipboard_callback)
|
||||||
return conversion->clipboard_callback (transfer, target,
|
return conversion->clipboard_callback (transfer, target,
|
||||||
type);
|
type, resource,
|
||||||
|
wl_data_source_send_send,
|
||||||
|
False);
|
||||||
|
|
||||||
/* Otherwise, use the default callback. */
|
/* Otherwise, use the default callback. */
|
||||||
DebugPrint ("Conversion to type %lu specified with default callback\n",
|
DebugPrint ("Conversion to type %lu specified with default callback\n",
|
||||||
|
@ -1223,12 +1426,13 @@ GetClipboardCallback (WriteTransfer *transfer, Atom target,
|
||||||
DebugPrint ("Created pipe (%d, %d)\n", pipe[0], pipe[1]);
|
DebugPrint ("Created pipe (%d, %d)\n", pipe[0], pipe[1]);
|
||||||
|
|
||||||
/* Send the write end of the pipe to the source. */
|
/* Send the write end of the pipe to the source. */
|
||||||
wl_data_source_send_send (XLResourceFromDataSource (selection_data_source),
|
wl_data_source_send_send (resource,
|
||||||
MimeTypeFromTarget (target), pipe[1]);
|
MimeTypeFromTarget (target, False),
|
||||||
|
pipe[1]);
|
||||||
close (pipe[1]);
|
close (pipe[1]);
|
||||||
|
|
||||||
/* And set the prop type appropriately. */
|
/* And set the prop type appropriately. */
|
||||||
*type = TypeFromTarget (target);
|
*type = TypeFromTarget (target, False);
|
||||||
|
|
||||||
/* Create the transfer info structure. */
|
/* Create the transfer info structure. */
|
||||||
info = XLMalloc (sizeof *info);
|
info = XLMalloc (sizeof *info);
|
||||||
|
@ -1416,8 +1620,11 @@ ConversionReadFunc (WriteTransfer *transfer, unsigned char *buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
static GetDataFunc
|
static GetDataFunc
|
||||||
GetConversionCallback (WriteTransfer *transfer, Atom target,
|
GetConversionCallback (WriteTransfer *transfer, Atom target, Atom *type,
|
||||||
Atom *type)
|
struct wl_resource *source,
|
||||||
|
void (*send_send) (struct wl_resource *, const char *,
|
||||||
|
int),
|
||||||
|
Bool is_primary)
|
||||||
{
|
{
|
||||||
ConversionWriteInfo *info;
|
ConversionWriteInfo *info;
|
||||||
int pipe[2], rc;
|
int pipe[2], rc;
|
||||||
|
@ -1441,8 +1648,7 @@ GetConversionCallback (WriteTransfer *transfer, Atom target,
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Then, send the write end of the pipe to the data source. */
|
/* Then, send the write end of the pipe to the data source. */
|
||||||
wl_data_source_send_send (XLResourceFromDataSource (selection_data_source),
|
send_send (source, "text/plain;charset=utf-8", pipe[1]);
|
||||||
"text/plain;charset=utf-8", pipe[1]);
|
|
||||||
close (pipe[1]);
|
close (pipe[1]);
|
||||||
|
|
||||||
/* Following that, set the property type correctly. */
|
/* Following that, set the property type correctly. */
|
||||||
|
@ -1677,7 +1883,7 @@ GetDragCallback (WriteTransfer *transfer, Atom target,
|
||||||
be disowned. */
|
be disowned. */
|
||||||
XLAssert (drag_data_source != NULL);
|
XLAssert (drag_data_source != NULL);
|
||||||
|
|
||||||
DebugPrint ("Entered GetClipboardCallback; target is %lu\n",
|
DebugPrint ("Entered GetDragCallback; target is %lu\n",
|
||||||
target);
|
target);
|
||||||
|
|
||||||
/* First, create a non-blocking pipe. We will give the write end to
|
/* First, create a non-blocking pipe. We will give the write end to
|
||||||
|
@ -1764,6 +1970,19 @@ XLNoteSourceDestroyed (DataSource *source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
XLNotePrimaryDestroyed (PDataSource *source)
|
||||||
|
{
|
||||||
|
if (source == primary_data_source)
|
||||||
|
{
|
||||||
|
DebugPrint ("Disowning PRIMARY\nThis is being done in response"
|
||||||
|
" to the data source being destroyed.\n");
|
||||||
|
|
||||||
|
/* Disown the selection. */
|
||||||
|
DisownSelection (XA_PRIMARY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Bool
|
static Bool
|
||||||
FindTargetInArray (Atom *targets, int ntargets, Atom atom)
|
FindTargetInArray (Atom *targets, int ntargets, Atom atom)
|
||||||
{
|
{
|
||||||
|
@ -1778,52 +1997,96 @@ FindTargetInArray (Atom *targets, int ntargets, Atom atom)
|
||||||
return False;
|
return False;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME: this should really be something other than two giant
|
||||||
|
macros... */
|
||||||
|
|
||||||
|
#define NoteLocalSelectionBody(callback, time_1, time_2, atom, field, foreign, n_foreign) \
|
||||||
|
Time 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); \
|
||||||
|
\
|
||||||
|
/* Disown the selection. */ \
|
||||||
|
DisownSelection (atom); \
|
||||||
|
field = NULL; \
|
||||||
|
\
|
||||||
|
/* Return whether or not the selection was actually \
|
||||||
|
disowned. */ \
|
||||||
|
return time_1 >= time_2; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
time = XLSeatGetLastUserTime (seat); \
|
||||||
|
\
|
||||||
|
DebugPrint ("Acquiring ownership of " #atom " at %lu\n", time); \
|
||||||
|
\
|
||||||
|
if (!time) \
|
||||||
|
/* Nothing has yet happened on the seat. */ \
|
||||||
|
return False; \
|
||||||
|
\
|
||||||
|
if (time < time_1 || time < time_2) \
|
||||||
|
/* TIME is out of date. */ \
|
||||||
|
return False; \
|
||||||
|
\
|
||||||
|
DebugPrint ("Setting callback function for " #atom "\n"); \
|
||||||
|
\
|
||||||
|
/* Since the local selection is now set, free foreign selection \
|
||||||
|
data. */ \
|
||||||
|
XLFree (foreign); \
|
||||||
|
n_foreign = 0; \
|
||||||
|
foreign = NULL; \
|
||||||
|
\
|
||||||
|
/* Otherwise, set the clipboard change time to TIME. */ \
|
||||||
|
time_1 = time; \
|
||||||
|
time_2 = time; \
|
||||||
|
\
|
||||||
|
|
||||||
|
#define NoteLocalSelectionFooter(callback, atom, field, has_target) \
|
||||||
|
/* Now, look to see what standard X targets the client doesn't \
|
||||||
|
offer, and add them to the targets array. \
|
||||||
|
\
|
||||||
|
Most functioning Wayland clients will also offer the typical X \
|
||||||
|
selection targets such as STRING and UTF8_STRING in addition to \
|
||||||
|
MIME types; when they do, they perform the data conversion \
|
||||||
|
conversion better than us. */ \
|
||||||
|
\
|
||||||
|
for (i = 0; i < ArrayElements (data_conversions); ++i) \
|
||||||
|
{ \
|
||||||
|
/* If the source provides MIME typed-data for a format we know \
|
||||||
|
how to convert, announce the associated selection conversion \
|
||||||
|
targets. */ \
|
||||||
|
\
|
||||||
|
if (has_target (source, \
|
||||||
|
data_conversions[i].mime_type) \
|
||||||
|
&& !FindTargetInArray (targets, ntargets, \
|
||||||
|
data_conversions[i].type)) \
|
||||||
|
{ \
|
||||||
|
DebugPrint ("Client doesn't provide standard X conversion" \
|
||||||
|
" target for %s; adding it\n", \
|
||||||
|
data_conversions[i].mime_type); \
|
||||||
|
\
|
||||||
|
targets[ntargets++] = data_conversions[i].type; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* And own the selection. */ \
|
||||||
|
field = source; \
|
||||||
|
\
|
||||||
|
rc = OwnSelection (time, atom, callback, targets, ntargets); \
|
||||||
|
XLFree (targets); \
|
||||||
|
return rc \
|
||||||
|
|
||||||
Bool
|
Bool
|
||||||
XLNoteLocalSelection (Seat *seat, DataSource *source)
|
XLNoteLocalSelection (Seat *seat, DataSource *source)
|
||||||
{
|
{
|
||||||
Time time;
|
NoteLocalSelectionBody (GetClipboardCallback, last_clipboard_time,
|
||||||
Atom *targets;
|
last_x_selection_change, CLIPBOARD,
|
||||||
int ntargets, i, n_data_conversions;
|
selection_data_source, x_selection_targets,
|
||||||
Bool rc;
|
num_x_selection_targets);
|
||||||
|
|
||||||
if (source == NULL)
|
|
||||||
{
|
|
||||||
DebugPrint ("Disowning CLIPBOARD at %lu (vs. last change %lu)\n",
|
|
||||||
last_clipboard_time, last_x_selection_change);
|
|
||||||
|
|
||||||
/* Disown the selection. */
|
|
||||||
DisownSelection (CLIPBOARD);
|
|
||||||
selection_data_source = NULL;
|
|
||||||
|
|
||||||
/* Return whether or not the selection was actually
|
|
||||||
disowned. */
|
|
||||||
return last_clipboard_time >= last_x_selection_change;
|
|
||||||
}
|
|
||||||
|
|
||||||
time = XLSeatGetLastUserTime (seat);
|
|
||||||
|
|
||||||
DebugPrint ("Acquiring ownership of CLIPBOARD at %lu\n", time);
|
|
||||||
|
|
||||||
if (!time)
|
|
||||||
/* Nothing has yet happened on the seat. */
|
|
||||||
return False;
|
|
||||||
|
|
||||||
if (time < last_clipboard_time
|
|
||||||
|| time < last_clipboard_change)
|
|
||||||
/* TIME is out of date. */
|
|
||||||
return False;
|
|
||||||
|
|
||||||
DebugPrint ("Setting callback function for CLIPBOARD\n");
|
|
||||||
|
|
||||||
/* Since the local selection is now set, free foreign selection
|
|
||||||
data. */
|
|
||||||
XLFree (x_selection_targets);
|
|
||||||
num_x_selection_targets = 0;
|
|
||||||
x_selection_targets = NULL;
|
|
||||||
|
|
||||||
/* Otherwise, set the clipboard change time to TIME. */
|
|
||||||
last_clipboard_time = time;
|
|
||||||
last_clipboard_change = time;
|
|
||||||
|
|
||||||
/* And copy the targets from the data source. */
|
/* And copy the targets from the data source. */
|
||||||
ntargets = XLDataSourceTargetCount (source);
|
ntargets = XLDataSourceTargetCount (source);
|
||||||
|
@ -1831,41 +2094,113 @@ XLNoteLocalSelection (Seat *seat, DataSource *source)
|
||||||
targets = XLMalloc (sizeof *targets * (ntargets + n_data_conversions));
|
targets = XLMalloc (sizeof *targets * (ntargets + n_data_conversions));
|
||||||
XLDataSourceGetTargets (source, targets);
|
XLDataSourceGetTargets (source, targets);
|
||||||
|
|
||||||
/* Now, look to see what standard X targets the client doesn't
|
NoteLocalSelectionFooter (GetClipboardCallback, CLIPBOARD,
|
||||||
offer, and add them to the targets array.
|
selection_data_source,
|
||||||
|
XLDataSourceHasTarget);
|
||||||
Most functioning Wayland clients will also offer the typical X
|
|
||||||
selection targets such as STRING and UTF8_STRING in addition to
|
|
||||||
MIME types; when they do, they perform the data conversion
|
|
||||||
conversion better than us. */
|
|
||||||
|
|
||||||
for (i = 0; i < ArrayElements (data_conversions); ++i)
|
|
||||||
{
|
|
||||||
/* If the source provides MIME typed-data for a format we know
|
|
||||||
how to convert, announce the associated selection conversion
|
|
||||||
targets. */
|
|
||||||
|
|
||||||
if (XLDataSourceHasTarget (source,
|
|
||||||
data_conversions[i].mime_type)
|
|
||||||
&& !FindTargetInArray (targets, ntargets,
|
|
||||||
data_conversions[i].type))
|
|
||||||
{
|
|
||||||
DebugPrint ("Client doesn't provide standard X conversion"
|
|
||||||
" target for %s; adding it\n",
|
|
||||||
data_conversions[i].mime_type);
|
|
||||||
|
|
||||||
targets[ntargets++] = data_conversions[i].type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* And own the selection. */
|
static GetDataFunc
|
||||||
selection_data_source = source;
|
GetPrimaryCallback (WriteTransfer *transfer, Atom target,
|
||||||
|
Atom *type)
|
||||||
|
{
|
||||||
|
WriteInfo *info;
|
||||||
|
int pipe[2], rc;
|
||||||
|
DataConversion *conversion;
|
||||||
|
struct wl_resource *resource;
|
||||||
|
|
||||||
rc = OwnSelection (time, CLIPBOARD, GetClipboardCallback,
|
/* If the selection data source is destroyed, the selection will be
|
||||||
targets, ntargets);
|
disowned. */
|
||||||
XLFree (targets);
|
XLAssert (primary_data_source != NULL);
|
||||||
|
|
||||||
return rc;
|
resource = XLResourceFromPDataSource (primary_data_source);
|
||||||
|
|
||||||
|
if (!XLPDataSourceHasAtomTarget (primary_data_source, target))
|
||||||
|
{
|
||||||
|
/* If the data source does not itself provide the specified
|
||||||
|
target, then a conversion function is in use. Call the
|
||||||
|
clipboard callback specified in the conversion table entry,
|
||||||
|
if it exists. */
|
||||||
|
conversion = GetDataConversion (target);
|
||||||
|
|
||||||
|
if (!conversion)
|
||||||
|
/* There must be a data conversion here. */
|
||||||
|
abort ();
|
||||||
|
|
||||||
|
/* This is ugly but the only reasonable way to fit the following
|
||||||
|
function in 80 columns. */
|
||||||
|
|
||||||
|
#define Function zwp_primary_selection_source_v1_send_send
|
||||||
|
|
||||||
|
if (conversion->clipboard_callback)
|
||||||
|
return conversion->clipboard_callback (transfer, target,
|
||||||
|
type, resource,
|
||||||
|
Function, True);
|
||||||
|
|
||||||
|
#undef Function
|
||||||
|
|
||||||
|
/* Otherwise, use the default callback. */
|
||||||
|
DebugPrint ("Conversion to type %lu specified with default callback\n",
|
||||||
|
target);
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugPrint ("Entered GetPrimaryCallback; target is %lu\n",
|
||||||
|
target);
|
||||||
|
|
||||||
|
/* First, create a non-blocking pipe. We will give the write end to
|
||||||
|
the client. */
|
||||||
|
rc = pipe2 (pipe, O_NONBLOCK);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
/* Creating the pipe failed. */
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
DebugPrint ("Created pipe (%d, %d)\n", pipe[0], pipe[1]);
|
||||||
|
|
||||||
|
/* Send the write end of the pipe to the source. */
|
||||||
|
zwp_primary_selection_source_v1_send_send (resource,
|
||||||
|
MimeTypeFromTarget (target, True),
|
||||||
|
pipe[1]);
|
||||||
|
close (pipe[1]);
|
||||||
|
|
||||||
|
/* And set the prop type appropriately. */
|
||||||
|
*type = TypeFromTarget (target, True);
|
||||||
|
|
||||||
|
/* Create the transfer info structure. */
|
||||||
|
info = XLMalloc (sizeof *info);
|
||||||
|
info->fd = pipe[0];
|
||||||
|
info->flags = 0;
|
||||||
|
|
||||||
|
DebugPrint ("Adding the read callback\n");
|
||||||
|
|
||||||
|
/* Wait for info to become readable. */
|
||||||
|
info->read_callback = XLAddReadFd (info->fd, transfer,
|
||||||
|
NoticeTransferReadable);
|
||||||
|
|
||||||
|
/* Set info as the transfer data. */
|
||||||
|
SetWriteTransferData (transfer, info);
|
||||||
|
|
||||||
|
return ClipboardReadFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool
|
||||||
|
XLNoteLocalPrimary (Seat *seat, PDataSource *source)
|
||||||
|
{
|
||||||
|
/* I forgot why there are two variables for tracking the clipboard
|
||||||
|
time, so just use one here. */
|
||||||
|
NoteLocalSelectionBody (GetPrimaryCallback, last_primary_time,
|
||||||
|
last_primary_time, XA_PRIMARY,
|
||||||
|
primary_data_source, x_primary_targets,
|
||||||
|
num_x_primary_targets);
|
||||||
|
|
||||||
|
/* And copy the targets from the data source. */
|
||||||
|
ntargets = XLPDataSourceTargetCount (source);
|
||||||
|
n_data_conversions = ArrayElements (data_conversions);
|
||||||
|
targets = XLMalloc (sizeof *targets * (ntargets + n_data_conversions));
|
||||||
|
XLPDataSourceGetTargets (source, targets);
|
||||||
|
|
||||||
|
NoteLocalSelectionFooter (GetPrimaryCallback, XA_PRIMARY,
|
||||||
|
primary_data_source,
|
||||||
|
XLPDataSourceHasTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1896,6 +2231,7 @@ XLInitXData (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectSelectionInput (CLIPBOARD);
|
SelectSelectionInput (CLIPBOARD);
|
||||||
|
SelectSelectionInput (XA_PRIMARY);
|
||||||
|
|
||||||
/* Initialize atoms used in the direct transfer table. */
|
/* Initialize atoms used in the direct transfer table. */
|
||||||
direct_transfer[1].atom_flag = UTF8_STRING | Duplicate;
|
direct_transfer[1].atom_flag = UTF8_STRING | Duplicate;
|
||||||
|
|
|
@ -35,7 +35,9 @@ enum
|
||||||
StateLateFrame = (1 << 1),
|
StateLateFrame = (1 << 1),
|
||||||
StatePendingWindowGeometry = (1 << 2),
|
StatePendingWindowGeometry = (1 << 2),
|
||||||
StateWaitingForAckConfigure = (1 << 3),
|
StateWaitingForAckConfigure = (1 << 3),
|
||||||
StateLateFrameAcked = (1 << 4),
|
StateWaitingForAckCommit = (1 << 4),
|
||||||
|
StateLateFrameAcked = (1 << 5),
|
||||||
|
StateMaybeConfigure = (1 << 6),
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _XdgRole XdgRole;
|
typedef struct _XdgRole XdgRole;
|
||||||
|
@ -599,6 +601,10 @@ AckConfigure (struct wl_client *client, struct wl_resource *resource,
|
||||||
if (XLFrameClockIsFrozen (xdg_role->clock)
|
if (XLFrameClockIsFrozen (xdg_role->clock)
|
||||||
&& xdg_role->role.surface)
|
&& xdg_role->role.surface)
|
||||||
RunFrameCallbacksConditionally (xdg_role);
|
RunFrameCallbacksConditionally (xdg_role);
|
||||||
|
|
||||||
|
#ifdef DEBUG_GEOMETRY_CALCULATION
|
||||||
|
fprintf (stderr, "Client acknowledged configuration\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xdg_role->impl)
|
if (xdg_role->impl)
|
||||||
|
@ -656,6 +662,10 @@ Commit (Surface *surface, Role *role)
|
||||||
xdg_role->state &= ~StatePendingWindowGeometry;
|
xdg_role->state &= ~StatePendingWindowGeometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This flag means no commit has happened after an
|
||||||
|
ack_configure. */
|
||||||
|
xdg_role->state &= ~StateWaitingForAckCommit;
|
||||||
|
|
||||||
/* A frame is already in progress, so instead say that an urgent
|
/* A frame is already in progress, so instead say that an urgent
|
||||||
update is needed immediately after the frame completes. In any
|
update is needed immediately after the frame completes. In any
|
||||||
case, don't run frame callbacks upon buffer release anymore. */
|
case, don't run frame callbacks upon buffer release anymore. */
|
||||||
|
@ -1062,6 +1072,13 @@ NoteBounds (void *data, int min_x, int min_y,
|
||||||
Don't resize the window until it's acknowledged. */
|
Don't resize the window until it's acknowledged. */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (role->state & StateWaitingForAckCommit)
|
||||||
|
/* Don't resize the window until all configure events are
|
||||||
|
acknowledged. We wait for a commit on the xdg_toplevel to do
|
||||||
|
this, because Firefox updates subsurfaces while the old size is
|
||||||
|
still in effect. */
|
||||||
|
return;
|
||||||
|
|
||||||
/* Avoid resizing the window should its actual size not have
|
/* Avoid resizing the window should its actual size not have
|
||||||
changed. */
|
changed. */
|
||||||
|
|
||||||
|
@ -1071,6 +1088,10 @@ NoteBounds (void *data, int min_x, int min_y,
|
||||||
if (role->bounds_width != bounds_width
|
if (role->bounds_width != bounds_width
|
||||||
|| role->bounds_height != bounds_height)
|
|| role->bounds_height != bounds_height)
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG_GEOMETRY_CALCULATION
|
||||||
|
fprintf (stderr, "Resizing to: %d %d\n", bounds_width, bounds_height);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (role->impl->funcs.note_window_pre_resize)
|
if (role->impl->funcs.note_window_pre_resize)
|
||||||
role->impl->funcs.note_window_pre_resize (&role->role,
|
role->impl->funcs.note_window_pre_resize (&role->role,
|
||||||
role->impl,
|
role->impl,
|
||||||
|
@ -1225,6 +1246,24 @@ WriteRedirectProperty (XdgRole *role)
|
||||||
(unsigned char *) &bypass_compositor, 1);
|
(unsigned char *) &bypass_compositor, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
HandleFreeze (void *data)
|
||||||
|
{
|
||||||
|
XdgRole *role;
|
||||||
|
|
||||||
|
role = data;
|
||||||
|
|
||||||
|
/* _NET_WM_SYNC_REQUEST events should be succeeded by a
|
||||||
|
ConfigureNotify event. */
|
||||||
|
role->state |= StateWaitingForAckConfigure;
|
||||||
|
role->state |= StateWaitingForAckCommit;
|
||||||
|
|
||||||
|
/* This flag means the WaitingForAckConfigure was caused by a
|
||||||
|
_NET_WM_SYNC_REQUEST, and the following ConfigureNotify event
|
||||||
|
might not lead to a configure event being sent. */
|
||||||
|
role->state |= StateMaybeConfigure;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
|
XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
|
||||||
uint32_t id, struct wl_resource *surface_resource)
|
uint32_t id, struct wl_resource *surface_resource)
|
||||||
|
@ -1331,6 +1370,8 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
|
||||||
role->subcompositor = MakeSubcompositor ();
|
role->subcompositor = MakeSubcompositor ();
|
||||||
role->clock = XLMakeFrameClockForWindow (role->window);
|
role->clock = XLMakeFrameClockForWindow (role->window);
|
||||||
|
|
||||||
|
XLFrameClockSetFreezeCallback (role->clock, HandleFreeze, role);
|
||||||
|
|
||||||
SubcompositorSetTarget (role->subcompositor, role->picture);
|
SubcompositorSetTarget (role->subcompositor, role->picture);
|
||||||
SubcompositorSetInputCallback (role->subcompositor,
|
SubcompositorSetInputCallback (role->subcompositor,
|
||||||
InputRegionChanged, role);
|
InputRegionChanged, role);
|
||||||
|
@ -1412,6 +1453,16 @@ XLXdgRoleSendConfigure (Role *role, uint32_t serial)
|
||||||
xdg_role = XdgRoleFromRole (role);
|
xdg_role = XdgRoleFromRole (role);
|
||||||
xdg_role->conf_serial = serial;
|
xdg_role->conf_serial = serial;
|
||||||
xdg_role->state |= StateWaitingForAckConfigure;
|
xdg_role->state |= StateWaitingForAckConfigure;
|
||||||
|
xdg_role->state |= StateWaitingForAckCommit;
|
||||||
|
|
||||||
|
/* We know know that the ConfigureNotify event following any
|
||||||
|
_NET_WM_SYNC_REQUEST event was accepted, so clear the maybe
|
||||||
|
configure flag. */
|
||||||
|
xdg_role->state &= ~StateMaybeConfigure;
|
||||||
|
|
||||||
|
#ifdef DEBUG_GEOMETRY_CALCULATION
|
||||||
|
fprintf (stderr, "Waiting for ack_configure...\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
xdg_surface_send_configure (role->resource, serial);
|
xdg_surface_send_configure (role->resource, serial);
|
||||||
}
|
}
|
||||||
|
@ -1767,3 +1818,24 @@ XLLookUpXdgPopup (Window window)
|
||||||
|
|
||||||
return role->impl;
|
return role->impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
XLXdgRoleNoteRejectedConfigure (Role *role)
|
||||||
|
{
|
||||||
|
XdgRole *xdg_role;
|
||||||
|
|
||||||
|
xdg_role = XdgRoleFromRole (role);
|
||||||
|
|
||||||
|
if (xdg_role->state & StateMaybeConfigure)
|
||||||
|
{
|
||||||
|
/* A configure event immediately following _NET_WM_SYNC_REQUEST
|
||||||
|
was rejected, meaning that we do not have to change anything
|
||||||
|
before unfreezing the frame clock. */
|
||||||
|
xdg_role->state &= ~StateWaitingForAckConfigure;
|
||||||
|
xdg_role->state &= ~StateWaitingForAckCommit;
|
||||||
|
xdg_role->state &= ~StateMaybeConfigure;
|
||||||
|
|
||||||
|
/* Unfreeze the frame clock now. */
|
||||||
|
XLFrameClockUnfreeze (xdg_role->clock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
245
xdg_toplevel.c
245
xdg_toplevel.c
|
@ -42,6 +42,9 @@ enum
|
||||||
StatePendingMaxSize = (1 << 2),
|
StatePendingMaxSize = (1 << 2),
|
||||||
StatePendingMinSize = (1 << 3),
|
StatePendingMinSize = (1 << 3),
|
||||||
StatePendingAckMovement = (1 << 4),
|
StatePendingAckMovement = (1 << 4),
|
||||||
|
StatePendingResize = (1 << 5),
|
||||||
|
StatePendingConfigureSize = (1 << 6),
|
||||||
|
StatePendingConfigureStates = (1 << 7),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -195,10 +198,26 @@ struct _XdgToplevel
|
||||||
int width01, height01, width10, height10;
|
int width01, height01, width10, height10;
|
||||||
int width00, height00, width11, height11;
|
int width00, height00, width11, height11;
|
||||||
|
|
||||||
|
/* The width, height, west and north motion of the next resize. */
|
||||||
|
int resize_width, resize_height, resize_west, resize_north;
|
||||||
|
|
||||||
/* Mask of what this toplevel is allowed to do. It is first set
|
/* Mask of what this toplevel is allowed to do. It is first set
|
||||||
based on _NET_SUPPORTED upon toplevel creation, and then
|
based on _NET_SUPPORTED upon toplevel creation, and then
|
||||||
_NET_WM_ALLOWED_ACTIONS. */
|
_NET_WM_ALLOWED_ACTIONS. */
|
||||||
int supported;
|
int supported;
|
||||||
|
|
||||||
|
/* Timer for completing window state changes. The order of
|
||||||
|
_NET_WM_STATE changes and ConfigureNotify events is not
|
||||||
|
predictable, so we batch up both kinds of events with a 0.01
|
||||||
|
second delay by default, before sending the resulting
|
||||||
|
ConfigureNotify event. However, if drag-to-resize is in
|
||||||
|
progress, no such delay is effected. */
|
||||||
|
#define DefaultStateDelayNanoseconds 10000000
|
||||||
|
Timer *configuration_timer;
|
||||||
|
|
||||||
|
/* The width and height used by that timer if
|
||||||
|
StatePendingConfigureSize is set. */
|
||||||
|
int configure_width, configure_height;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* iconv context used to convert between UTF-8 and Latin-1. */
|
/* iconv context used to convert between UTF-8 and Latin-1. */
|
||||||
|
@ -208,6 +227,10 @@ static iconv_t latin_1_cd;
|
||||||
with configure events. */
|
with configure events. */
|
||||||
static Bool apply_state_workaround;
|
static Bool apply_state_workaround;
|
||||||
|
|
||||||
|
/* Whether or not to batch together state and size changes that arrive
|
||||||
|
at almost the same time. */
|
||||||
|
static Bool batch_state_changes;
|
||||||
|
|
||||||
static XdgUnmapCallback *
|
static XdgUnmapCallback *
|
||||||
RunOnUnmap (XdgToplevel *toplevel, void (*unmap) (void *),
|
RunOnUnmap (XdgToplevel *toplevel, void (*unmap) (void *),
|
||||||
void *data)
|
void *data)
|
||||||
|
@ -290,6 +313,10 @@ DestroyBacking (XdgToplevel *toplevel)
|
||||||
if (--toplevel->refcount)
|
if (--toplevel->refcount)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* If there is a pending configuration timer, remove it. */
|
||||||
|
if (toplevel->configuration_timer)
|
||||||
|
RemoveTimer (toplevel->configuration_timer);
|
||||||
|
|
||||||
if (toplevel->parent_callback)
|
if (toplevel->parent_callback)
|
||||||
CancelUnmapCallback (toplevel->parent_callback);
|
CancelUnmapCallback (toplevel->parent_callback);
|
||||||
|
|
||||||
|
@ -325,6 +352,109 @@ SendConfigure (XdgToplevel *toplevel, unsigned int width,
|
||||||
toplevel->conf_serial = serial;
|
toplevel->conf_serial = serial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Forward declaration. */
|
||||||
|
|
||||||
|
static void SendStates (XdgToplevel *);
|
||||||
|
static void WriteStates (XdgToplevel *);
|
||||||
|
|
||||||
|
static void
|
||||||
|
NoteConfigureTime (Timer *timer, void *data, struct timespec time)
|
||||||
|
{
|
||||||
|
XdgToplevel *toplevel;
|
||||||
|
int width, height, effective_width, effective_height;
|
||||||
|
|
||||||
|
toplevel = data;
|
||||||
|
|
||||||
|
/* If only the window state changed, call SendStates. */
|
||||||
|
if (!(toplevel->state & StatePendingConfigureSize))
|
||||||
|
SendStates (toplevel);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* If the states changed, write them. */
|
||||||
|
if (toplevel->state & StatePendingConfigureStates)
|
||||||
|
WriteStates (toplevel);
|
||||||
|
|
||||||
|
effective_width = toplevel->configure_width / global_scale_factor;
|
||||||
|
effective_height = toplevel->configure_height / global_scale_factor;
|
||||||
|
|
||||||
|
/* Compute the geometry for the configure event based on the
|
||||||
|
current size of the toplevel. */
|
||||||
|
XLXdgRoleCalcNewWindowSize (toplevel->role,
|
||||||
|
effective_width,
|
||||||
|
effective_height,
|
||||||
|
&width, &height);
|
||||||
|
|
||||||
|
/* Send the configure event. */
|
||||||
|
SendConfigure (toplevel, width, height);
|
||||||
|
|
||||||
|
/* Mark the state has having been calculated if some state
|
||||||
|
transition has occured. */
|
||||||
|
if (toplevel->toplevel_state.fullscreen
|
||||||
|
|| toplevel->toplevel_state.maximized)
|
||||||
|
toplevel->state &= ~StateMissingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the pending size and state flags. */
|
||||||
|
toplevel->state &= ~StatePendingConfigureSize;
|
||||||
|
toplevel->state &= ~StatePendingConfigureStates;
|
||||||
|
|
||||||
|
/* Cancel and clear the timer. */
|
||||||
|
RemoveTimer (timer);
|
||||||
|
toplevel->configuration_timer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
FlushConfigurationTimer (XdgToplevel *toplevel)
|
||||||
|
{
|
||||||
|
if (!toplevel->configuration_timer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Cancel the configuration timer and flush pending state to the
|
||||||
|
state array. It is assumed that a configure event will be sent
|
||||||
|
immediately afterwards. */
|
||||||
|
|
||||||
|
if (toplevel->state & StatePendingConfigureStates)
|
||||||
|
WriteStates (toplevel);
|
||||||
|
|
||||||
|
/* Clear the pending size and state flags. */
|
||||||
|
toplevel->state &= ~StatePendingConfigureSize;
|
||||||
|
toplevel->state &= ~StatePendingConfigureStates;
|
||||||
|
|
||||||
|
/* Cancel and clear the timer. */
|
||||||
|
RemoveTimer (toplevel->configuration_timer);
|
||||||
|
toplevel->configuration_timer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool
|
||||||
|
MaybePostDelayedConfigure (XdgToplevel *toplevel, int flag)
|
||||||
|
{
|
||||||
|
XLList *tem;
|
||||||
|
|
||||||
|
if (!batch_state_changes)
|
||||||
|
return False;
|
||||||
|
|
||||||
|
toplevel->state |= flag;
|
||||||
|
|
||||||
|
if (toplevel->configuration_timer)
|
||||||
|
{
|
||||||
|
/* The timer is already ticking... */
|
||||||
|
RetimeTimer (toplevel->configuration_timer);
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If some seat is being resized, return False. */
|
||||||
|
for (tem = live_seats; tem; tem = tem->next)
|
||||||
|
{
|
||||||
|
if (XLSeatResizeInProgress (tem->data))
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
toplevel->configuration_timer
|
||||||
|
= AddTimer (NoteConfigureTime, toplevel,
|
||||||
|
MakeTimespec (0, DefaultStateDelayNanoseconds));
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
WriteStates (XdgToplevel *toplevel)
|
WriteStates (XdgToplevel *toplevel)
|
||||||
{
|
{
|
||||||
|
@ -477,7 +607,9 @@ HandleWmStateChange (XdgToplevel *toplevel)
|
||||||
state->maximized = True;
|
state->maximized = True;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp (&old, &state, sizeof *state))
|
if (memcmp (&old, &state, sizeof *state)
|
||||||
|
&& !MaybePostDelayedConfigure (toplevel,
|
||||||
|
StatePendingConfigureStates))
|
||||||
/* Finally, send states if they changed. */
|
/* Finally, send states if they changed. */
|
||||||
SendStates (toplevel);
|
SendStates (toplevel);
|
||||||
|
|
||||||
|
@ -826,6 +958,11 @@ Unmap (XdgToplevel *toplevel)
|
||||||
|
|
||||||
memset (&toplevel->state, 0, sizeof toplevel->states);
|
memset (&toplevel->state, 0, sizeof toplevel->states);
|
||||||
|
|
||||||
|
/* If there is a pending configure timer, remove it. */
|
||||||
|
if (toplevel->configuration_timer)
|
||||||
|
RemoveTimer (toplevel->configuration_timer);
|
||||||
|
toplevel->configuration_timer = NULL;
|
||||||
|
|
||||||
XLListFree (toplevel->resize_callbacks,
|
XLListFree (toplevel->resize_callbacks,
|
||||||
XLSeatCancelResizeCallback);
|
XLSeatCancelResizeCallback);
|
||||||
toplevel->resize_callbacks = NULL;
|
toplevel->resize_callbacks = NULL;
|
||||||
|
@ -909,6 +1046,7 @@ Commit (Role *role, Surface *surface, XdgRoleImplementation *impl)
|
||||||
if (toplevel->state & StateIsMapped)
|
if (toplevel->state & StateIsMapped)
|
||||||
Unmap (toplevel);
|
Unmap (toplevel);
|
||||||
|
|
||||||
|
FlushConfigurationTimer (toplevel);
|
||||||
SendConfigure (toplevel, 0, 0);
|
SendConfigure (toplevel, 0, 0);
|
||||||
}
|
}
|
||||||
else if (!toplevel->conf_reply)
|
else if (!toplevel->conf_reply)
|
||||||
|
@ -919,6 +1057,67 @@ Commit (Role *role, Surface *surface, XdgRoleImplementation *impl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
PostResize1 (XdgToplevel *toplevel, int west_motion, int north_motion,
|
||||||
|
int new_width, int new_height)
|
||||||
|
{
|
||||||
|
/* FIXME: the two computations below are still not completely
|
||||||
|
right. */
|
||||||
|
|
||||||
|
if (new_width < toplevel->min_width)
|
||||||
|
{
|
||||||
|
west_motion += toplevel->min_width - new_width;
|
||||||
|
|
||||||
|
/* Don't move too far west. */
|
||||||
|
if (west_motion > 0)
|
||||||
|
west_motion = 0;
|
||||||
|
|
||||||
|
new_width = toplevel->min_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_height < toplevel->min_height)
|
||||||
|
{
|
||||||
|
north_motion += toplevel->min_height - new_height;
|
||||||
|
|
||||||
|
/* Don't move too far north. */
|
||||||
|
if (north_motion > 0)
|
||||||
|
north_motion = 0;
|
||||||
|
|
||||||
|
new_height = toplevel->min_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(toplevel->state & StatePendingAckMovement))
|
||||||
|
{
|
||||||
|
FlushConfigurationTimer (toplevel);
|
||||||
|
SendConfigure (toplevel, new_width, new_height);
|
||||||
|
|
||||||
|
toplevel->ack_west += west_motion;
|
||||||
|
toplevel->ack_north += north_motion;
|
||||||
|
toplevel->state |= StatePendingAckMovement;
|
||||||
|
|
||||||
|
/* Clear extra state flags that are no longer useful. */
|
||||||
|
toplevel->state &= ~StatePendingResize;
|
||||||
|
toplevel->resize_west = 0;
|
||||||
|
toplevel->resize_north = 0;
|
||||||
|
toplevel->resize_width = 0;
|
||||||
|
toplevel->resize_height = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* A configure event has been posted but has not yet been fully
|
||||||
|
processed. Accumulate the new width, height, west and north
|
||||||
|
values, and send another configure event once it really does
|
||||||
|
arrive, and the previous changes have been committed. */
|
||||||
|
|
||||||
|
toplevel->state |= StatePendingResize;
|
||||||
|
|
||||||
|
toplevel->resize_west += west_motion;
|
||||||
|
toplevel->resize_north += north_motion;
|
||||||
|
toplevel->resize_width = new_width;
|
||||||
|
toplevel->resize_height = new_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
CommitInsideFrame (Role *role, XdgRoleImplementation *impl)
|
CommitInsideFrame (Role *role, XdgRoleImplementation *impl)
|
||||||
{
|
{
|
||||||
|
@ -935,6 +1134,14 @@ CommitInsideFrame (Role *role, XdgRoleImplementation *impl)
|
||||||
toplevel->ack_west = 0;
|
toplevel->ack_west = 0;
|
||||||
toplevel->ack_north = 0;
|
toplevel->ack_north = 0;
|
||||||
toplevel->state &= ~StatePendingAckMovement;
|
toplevel->state &= ~StatePendingAckMovement;
|
||||||
|
|
||||||
|
/* This pending movement has completed. Apply postponed state,
|
||||||
|
if there is any. */
|
||||||
|
if (toplevel->state & StatePendingResize)
|
||||||
|
PostResize1 (toplevel, toplevel->resize_west,
|
||||||
|
toplevel->resize_north,
|
||||||
|
toplevel->resize_width,
|
||||||
|
toplevel->resize_height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -994,7 +1201,14 @@ HandleConfigureEvent (XdgToplevel *toplevel, XEvent *event)
|
||||||
|
|
||||||
if (event->xconfigure.width == toplevel->width
|
if (event->xconfigure.width == toplevel->width
|
||||||
&& event->xconfigure.height == toplevel->height)
|
&& event->xconfigure.height == toplevel->height)
|
||||||
|
{
|
||||||
|
if (!toplevel->configuration_timer)
|
||||||
|
/* No configure event will be generated in the future.
|
||||||
|
Unfreeze the frame clock. */
|
||||||
|
XLXdgRoleNoteRejectedConfigure (toplevel->role);
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
/* Try to guess if the window state was restored to some earlier
|
/* Try to guess if the window state was restored to some earlier
|
||||||
value, and set it now, to avoid race conditions when some clients
|
value, and set it now, to avoid race conditions when some clients
|
||||||
|
@ -1005,16 +1219,21 @@ HandleConfigureEvent (XdgToplevel *toplevel, XEvent *event)
|
||||||
event->xconfigure.height))
|
event->xconfigure.height))
|
||||||
WriteStates (toplevel);
|
WriteStates (toplevel);
|
||||||
|
|
||||||
|
if (!MaybePostDelayedConfigure (toplevel, StatePendingConfigureSize))
|
||||||
|
{
|
||||||
XLXdgRoleCalcNewWindowSize (toplevel->role,
|
XLXdgRoleCalcNewWindowSize (toplevel->role,
|
||||||
ConfigureWidth (event),
|
ConfigureWidth (event),
|
||||||
ConfigureHeight (event),
|
ConfigureHeight (event),
|
||||||
&width, &height);
|
&width, &height);
|
||||||
|
|
||||||
SendConfigure (toplevel, width, height);
|
SendConfigure (toplevel, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
/* Set toplevel->width and toplevel->height correctly. */
|
/* Set toplevel->width and toplevel->height correctly. */
|
||||||
toplevel->width = event->xconfigure.width;
|
toplevel->width = event->xconfigure.width;
|
||||||
toplevel->height = event->xconfigure.height;
|
toplevel->height = event->xconfigure.height;
|
||||||
|
toplevel->configure_width = toplevel->width;
|
||||||
|
toplevel->configure_height = toplevel->height;
|
||||||
|
|
||||||
/* Also set the bounds width and height to avoid resizing
|
/* Also set the bounds width and height to avoid resizing
|
||||||
the window. */
|
the window. */
|
||||||
|
@ -1177,27 +1396,8 @@ PostResize (Role *role, XdgRoleImplementation *impl, int west_motion,
|
||||||
XdgToplevel *toplevel;
|
XdgToplevel *toplevel;
|
||||||
|
|
||||||
toplevel = ToplevelFromRoleImpl (impl);
|
toplevel = ToplevelFromRoleImpl (impl);
|
||||||
|
PostResize1 (toplevel, west_motion, north_motion,
|
||||||
if (new_width < toplevel->min_width)
|
new_width, new_height);
|
||||||
{
|
|
||||||
new_width = toplevel->min_width;
|
|
||||||
|
|
||||||
/* FIXME: this computation is not correct, just "good
|
|
||||||
enough". */
|
|
||||||
west_motion = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new_height < toplevel->min_height)
|
|
||||||
{
|
|
||||||
new_height = toplevel->min_height;
|
|
||||||
north_motion = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SendConfigure (toplevel, new_width, new_height);
|
|
||||||
|
|
||||||
toplevel->ack_west += west_motion;
|
|
||||||
toplevel->ack_north += north_motion;
|
|
||||||
toplevel->state |= StatePendingAckMovement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1858,6 +2058,7 @@ XLInitXdgToplevels (void)
|
||||||
{
|
{
|
||||||
latin_1_cd = iconv_open ("ISO-8859-1", "UTF-8");
|
latin_1_cd = iconv_open ("ISO-8859-1", "UTF-8");
|
||||||
apply_state_workaround = (getenv ("APPLY_STATE_WORKAROUND") != NULL);
|
apply_state_workaround = (getenv ("APPLY_STATE_WORKAROUND") != NULL);
|
||||||
|
batch_state_changes = !getenv ("DIRECT_STATE_CHANGES");
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool
|
Bool
|
||||||
|
|
Loading…
Add table
Reference in a new issue