Implement primary selections, and minor improvements elsewhere

* 12to11.c (XLMain): Initialize primary selections.  Transfer
between X and Wayland programs is still incomplete.
* Imakefile (SRCS, OBJS): Add primary selection related objects
and sources.
(primary-selection-unstable-v1.h):
(primary-selection-unstable-v1.c): New targets.
* README: Update what is not supported.
* compositor.h: New prototypes.

* data_device.c (XLDataDeviceSendEnter): Handle resource
allocation failures.
* mime1.awk: Update generated code for changes in target entry
structures.
* seat.c (SetFocusSurface): Handle focus change for primary
selections as well.
(FindSurfaceUnder): Cut off fractional portion instead of
rounding the given coordinates, so the correct surface is found
when the cursor is moved just inside the rightmost pixel.
* surface.c (XLSurfaceRunFrameCallbacks): Handle timestamp
overflow.
* xdata.c (struct _TargetMapping): Rename atom to atom_flag, and
use it to store flags.
(MappingAtom, MappingFlag, MappingIsNextDuplicate, MappingSetFlag)
(MappingUnsetFlag, MappingIs): New macros.
(struct _TargetMappingTable): New structure.
(Duplicate): New definition.
(direct_transfer): Update duplicate types.
(mapping_table): New hash table.
(HashMimeString, SetupMappingTable): New functions.
(FindTranslationForMimeType, Receive): Use the target mapping
table to look up targets instead.
(CheckDuplicate): New function.
(SendOffers): Call CheckDuplicates.
(XLInitXData): Set up duplicate relationship between UTF8_STRING
and is conversion entry, and the targets mapping table.
This commit is contained in:
oldosfan 2022-09-13 11:41:07 +00:00
parent 0965f5b3eb
commit 6c7801f0fd
9 changed files with 219 additions and 31 deletions

View file

@ -155,6 +155,7 @@ XLMain (int argc, char **argv)
XLInitXData (); XLInitXData ();
XLInitXSettings (); XLInitXSettings ();
XLInitIconSurfaces (); XLInitIconSurfaces ();
XLInitPrimarySelection ();
/* This has to come after the rest of the initialization. */ /* This has to come after the rest of the initialization. */
DetermineServerTime (); DetermineServerTime ();
XLRunCompositor (); XLRunCompositor ();

View file

@ -21,14 +21,16 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \
positioner.c xdg_wm.c xdg_surface.c xdg_toplevel.c \ positioner.c xdg_wm.c xdg_surface.c xdg_toplevel.c \
frame_clock.c xerror.c ewmh.c timer.c subsurface.c seat.c \ frame_clock.c xerror.c ewmh.c timer.c subsurface.c seat.c \
data_device.c xdg_popup.c linux-dmabuf-unstable-v1.c dmabuf.c \ data_device.c xdg_popup.c linux-dmabuf-unstable-v1.c dmabuf.c \
buffer.c select.c xdata.c xsettings.c dnd.c icon_surface.c buffer.c select.c xdata.c xsettings.c dnd.c icon_surface.c \
primary-selection-unstable-v1.c primary_selection.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 \
xdg-shell.o surface.o region.o shm.o atoms.o subcompositor.o \ xdg-shell.o surface.o region.o shm.o atoms.o subcompositor.o \
positioner.o xdg_wm.o xdg_surface.o xdg_toplevel.o \ positioner.o xdg_wm.o xdg_surface.o xdg_toplevel.o \
frame_clock.o xerror.o ewmh.o timer.o subsurface.o seat.o \ frame_clock.o xerror.o ewmh.o timer.o subsurface.o seat.o \
data_device.o xdg_popup.o linux-dmabuf-unstable-v1.o dmabuf.o \ data_device.o xdg_popup.o linux-dmabuf-unstable-v1.o dmabuf.o \
buffer.o select.o xdata.o xsettings.o dnd.o icon_surface.o buffer.o select.o xdata.o xsettings.o dnd.o icon_surface.o \
primary-selection-unstable-v1.o primary_selection.o
OPTIMIZE = -O0 OPTIMIZE = -O0
ANALYZE = -fanalyzer ANALYZE = -fanalyzer
@ -53,7 +55,7 @@ CDEBUGFLAGS := -fno-common -Wall -Warith-conversion -Wdate-time \
$(ANALYZE) $(ANALYZE)
short_types.txt: media_types.txt short_types.txt: media_types.txt
XCOMM remove all data types starting with application/vnd. XCOMM Remove all data types starting with application/vnd.
XCOMM no program really uses them in clipboard data, and they XCOMM no program really uses them in clipboard data, and they
XCOMM waste a lot of space on disk. XCOMM waste a lot of space on disk.
sed '/application\/vnd/d' media_types.txt > $@ sed '/application\/vnd/d' media_types.txt > $@
@ -81,9 +83,17 @@ xdg-shell.h: xdg-shell.xml
xdg-shell.c: xdg-shell.xml xdg-shell.h xdg-shell.c: xdg-shell.xml xdg-shell.h
$(WAYLAND_SCANNER) private-code $< $@ $(WAYLAND_SCANNER) private-code $< $@
primary-selection-unstable-v1.h: primary-selection-unstable-v1.xml
$(WAYLAND_SCANNER) server-header $< $@
primary-selection-unstable-v1.c: primary-selection-unstable-v1.xml \
primary-selection-unstable-v1.h
$(WAYLAND_SCANNER) private-code $< $@
cleandir:: cleandir::
$(RM) linux-dmabuf-unstable-v1.c linux-dmabuf-unstable-v1.h \ $(RM) linux-dmabuf-unstable-v1.c linux-dmabuf-unstable-v1.h \
xdg-shell.c xdg-shell.h xdg-shell.c xdg-shell.h primary-selection-unstable-v1.c \
primary-selection-unstable-v1.h
$(RM) transfer_atoms.h short_types.txt $(RM) transfer_atoms.h short_types.txt
/* Undefine _BSD_SOURCE and _SVID_SOURCE, since both are deprecated /* Undefine _BSD_SOURCE and _SVID_SOURCE, since both are deprecated

7
README
View file

@ -42,14 +42,15 @@ complete degree:
'wl_seat', version: 7 'wl_seat', version: 7
'wl_data_device_manager', version: 3 'wl_data_device_manager', version: 3
'zwp_linux_dmabuf_v1', version: 4 'zwp_linux_dmabuf_v1', version: 4
'zwp_primary_selection_device_manager_v1', version: 1
With the main caveat being that zwp_linux_dmabuf_v1 has no real With the main caveat being that zwp_linux_dmabuf_v1 has no real
support for multiple-provider setups (help wanted). support for multiple-provider setups (help wanted).
Primary selections and window decorations are also not supported, even Window decorations are also not supported, even though they fit in
though they fit in nicely with X window management. nicely with X window management.
It would also be nice to have pinch gesture support in wl_pointer. It would also be nice to have pinch gesture support.
This directory is organized as follows: This directory is organized as follows:

View file

@ -965,6 +965,11 @@ extern void XLInitIconSurfaces (void);
extern void XLReleaseIconSurface (IconSurface *); extern void XLReleaseIconSurface (IconSurface *);
extern Bool XLIsWindowIconSurface (Window); extern Bool XLIsWindowIconSurface (Window);
/* Defined in primary_selection.c. */
extern void XLInitPrimarySelection (void);
extern void XLPrimarySelectionHandleFocusChange (Seat *);
/* Utility functions that don't belong in a specific file. */ /* Utility functions that don't belong in a specific file. */
#define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0]) #define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0])

View file

@ -1364,6 +1364,11 @@ XLDataDeviceSendEnter (Seat *seat, Surface *surface, double x, double y,
/* First, create a data offer corresponding to the data /* First, create a data offer corresponding to the data
source if it exists. */ source if it exists. */
resource = AddDataOffer (client, source); resource = AddDataOffer (client, source);
if (!resource)
/* Allocation of the resource failed. */
goto next;
offer = wl_resource_get_user_data (resource); offer = wl_resource_get_user_data (resource);
offer->dnd_serial = serial; offer->dnd_serial = serial;
offer->last_action = -1; offer->last_action = -1;
@ -1397,6 +1402,7 @@ XLDataDeviceSendEnter (Seat *seat, Surface *surface, double x, double y,
} }
next:
reference = reference->next; reference = reference->next;
} }
} }

View file

@ -26,7 +26,7 @@ BEGIN {
match ($0, / ([a-z]+\/[-+.[:alnum:]]+) /, array) match ($0, / ([a-z]+\/[-+.[:alnum:]]+) /, array)
name = array[1] name = array[1]
gsub (/[[:punct:]]/, "_", name) # Convert to a valid atom name gsub (/[[:punct:]]/, "_", name) # Convert to a valid atom name
printf " table[%d + start].atom = %s;\\\n", i++, name printf " table[%d + start].atom_flag = %s;\\\n", i++, name
} }
END { END {

15
seat.c
View file

@ -2625,7 +2625,12 @@ SetFocusSurface (Seat *seat, Surface *focus)
} }
if (!focus) if (!focus)
return; {
/* These changes must be handled even if there is no more focus
surface. */
XLPrimarySelectionHandleFocusChange (seat);
return;
}
seat->focus_surface = focus; seat->focus_surface = focus;
seat->focus_destroy_callback seat->focus_destroy_callback
@ -2633,6 +2638,8 @@ SetFocusSurface (Seat *seat, Surface *focus)
SendKeyboardEnter (seat, focus); SendKeyboardEnter (seat, focus);
XLPrimarySelectionHandleFocusChange (seat);
if (seat->data_device) if (seat->data_device)
XLDataDeviceHandleFocusChange (seat->data_device); XLDataDeviceHandleFocusChange (seat->data_device);
} }
@ -2670,8 +2677,10 @@ FindSurfaceUnder (Subcompositor *subcompositor, double x, double y)
int x_off, y_off; int x_off, y_off;
View *view; View *view;
view = SubcompositorLookupView (subcompositor, lrint (x), /* Do not round these figures. Instead, cut off the fractional,
lrint (y), &x_off, &y_off); like the X server does when deciding when to set the cursor. */
view = SubcompositorLookupView (subcompositor, x, y,
&x_off, &y_off);
if (view) if (view)
return ViewGetData (view); return ViewGetData (view);

View file

@ -1167,7 +1167,13 @@ XLSurfaceRunFrameCallbacks (Surface *surface, struct timespec time)
uint32_t ms_time; uint32_t ms_time;
XLList *list; XLList *list;
ms_time = time.tv_sec * 1000 + time.tv_nsec / 1000000; /* I don't know what else is reasonable in case of overflow. */
if (IntMultiplyWrapv (time.tv_sec, 1000, &ms_time))
ms_time = UINT32_MAX;
else if (IntAddWrapv (ms_time, time.tv_nsec / 1000000,
&ms_time))
ms_time = UINT32_MAX;
RunFrameCallbacks (&surface->current_state.frame_callbacks, RunFrameCallbacks (&surface->current_state.frame_callbacks,
ms_time); ms_time);

186
xdata.c
View file

@ -42,6 +42,7 @@ typedef struct _ConversionTransferInfo ConversionTransferInfo;
typedef struct _WriteInfo WriteInfo; typedef struct _WriteInfo WriteInfo;
typedef struct _ConversionWriteInfo ConversionWriteInfo; typedef struct _ConversionWriteInfo ConversionWriteInfo;
typedef struct _DataConversion DataConversion; typedef struct _DataConversion DataConversion;
typedef struct _TargetMappingTable TargetMappingTable;
struct _ReadTargetsData struct _ReadTargetsData
{ {
@ -54,8 +55,12 @@ struct _ReadTargetsData
struct _TargetMapping struct _TargetMapping
{ {
/* The atom of the X target. */ /* The atom of the X target. The top 3 bits of an XID are
Atom atom; guaranteed to be 0, so the 31st bit is used to store a flag
meaning that the next entry is a duplicate of this one, and the
30th bit is used to store a flag containing state used by
SendOffers. */
Atom atom_flag;
/* The name of the Wayland MIME type. */ /* The name of the Wayland MIME type. */
const char *mime_type; const char *mime_type;
@ -64,6 +69,13 @@ struct _TargetMapping
void (*translation_func) (Time, Atom, Atom, int); void (*translation_func) (Time, Atom, Atom, int);
}; };
#define MappingAtom(mapping) ((mapping)->atom_flag & 0x1fffffff)
#define MappingFlag(mapping) ((mapping)->atom_flag & (1 << 29))
#define MappingIsNextDuplicate(mapping) ((mapping)->atom_flag & (1 << 30))
#define MappingSetFlag(mapping) ((mapping)->atom_flag |= (1 << 29))
#define MappingUnsetFlag(mapping) ((mapping)->atom_flag &= ~(1 << 29))
#define MappingIs(mapping, atom) (MappingAtom (mapping) == (atom))
struct _DataConversion struct _DataConversion
{ {
/* The MIME type of the Wayland offer. */ /* The MIME type of the Wayland offer. */
@ -179,20 +191,43 @@ struct _ConversionWriteInfo
iconv_t cd; iconv_t cd;
}; };
struct _TargetMappingTable
{
/* Array of indices into direct_transfer. */
unsigned short *buckets[32];
/* Number of elements in each array. */
unsigned short n_elements[32];
/* Array of indices into direct_transfer. */
unsigned short *atom_buckets[16];
/* Number of elements in each array. */
unsigned short n_atom_elements[16];
};
/* Base event code of the Xfixes extension. */ /* Base event code of the Xfixes extension. */
static int fixes_event_base; static int fixes_event_base;
/* This means the next item in the targets mapping table has the same
MIME type as this one. */
#define Duplicate (1U << 30)
/* Map of targets that can be transferred from X to Wayland clients /* Map of targets that can be transferred from X to Wayland clients
and vice versa. */ and vice versa. */
static TargetMapping direct_transfer[] = static TargetMapping direct_transfer[] =
{ {
{ XA_STRING, "text/plain;charset=iso-9889-1" }, { XA_STRING, "text/plain;charset=iso-9889-1" },
{ 0, "text/plain;charset=utf-8" }, { Duplicate, "text/plain;charset=utf-8" },
{ XA_STRING, "text/plain;charset=utf-8" }, { XA_STRING, "text/plain;charset=utf-8" },
/* These mappings are automatically generated. */ /* These mappings are automatically generated. */
DirectTransferMappings DirectTransferMappings
}; };
/* Lookup table for such mappings. */
static TargetMappingTable mapping_table;
/* Map of Wayland offer types to X atoms and data conversion /* Map of Wayland offer types to X atoms and data conversion
functions. */ functions. */
static DataConversion data_conversions[] = static DataConversion data_conversions[] =
@ -244,6 +279,53 @@ Accept (struct wl_client *client, struct wl_resource *resource,
/* Nothing has to be done here yet. */ /* Nothing has to be done here yet. */
} }
static unsigned int
HashMimeString (const char *string)
{
unsigned int i;
i = 3323198485ul;
for (; *string; ++string)
{
i ^= *string;
i *= 0x5bd1e995;
i ^= i >> 15;
}
return i;
}
static void
SetupMappingTable (void)
{
unsigned int idx, i, nelements;
/* This is needed for the atoms table, since the atom indices are
determined by the X server. */
XLAssert (ArrayElements (direct_transfer) <= USHRT_MAX);
for (i = 0; i < ArrayElements (direct_transfer); ++i)
{
idx = HashMimeString (direct_transfer[i].mime_type) % 32;
nelements = ++mapping_table.n_elements[idx];
mapping_table.buckets[idx]
= XLRealloc (mapping_table.buckets[idx],
nelements * sizeof (unsigned short));
mapping_table.buckets[idx][nelements - 1] = i;
/* Now, set up the lookup table indexed by atoms. It is faster
to compare atoms than strings, so the table is smaller. */
idx = MappingAtom (&direct_transfer[i]) % 16;
nelements = ++mapping_table.n_atom_elements[idx];
mapping_table.atom_buckets[idx]
= XLRealloc (mapping_table.atom_buckets[idx],
nelements * sizeof (unsigned short));
mapping_table.atom_buckets[idx][nelements - 1] = i;
}
}
static Bool static Bool
HasSelectionTarget (Atom atom) HasSelectionTarget (Atom atom)
{ {
@ -261,13 +343,18 @@ HasSelectionTarget (Atom atom)
static TargetMapping * static TargetMapping *
FindTranslationForMimeType (const char *mime_type) FindTranslationForMimeType (const char *mime_type)
{ {
int i; unsigned short *buckets, i;
unsigned int idx;
for (i = 0; i < ArrayElements (direct_transfer); ++i) idx = HashMimeString (mime_type) % 32;
buckets = mapping_table.buckets[idx];
for (i = 0; i < mapping_table.n_elements[idx]; ++i)
{ {
if (!strcmp (direct_transfer[i].mime_type, mime_type) if (!strcmp (direct_transfer[buckets[i]].mime_type,
&& HasSelectionTarget (direct_transfer[i].atom)) mime_type)
return &direct_transfer[i]; && HasSelectionTarget (MappingAtom (&direct_transfer[buckets[i]])))
return &direct_transfer[buckets[i]];
} }
return NULL; return NULL;
@ -575,11 +662,13 @@ Receive (struct wl_client *client, struct wl_resource *resource,
{ {
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, CLIPBOARD, translation->atom, fd); PostReceiveDirect (time, CLIPBOARD,
MappingAtom (translation), fd);
else else
/* Otherwise, use the translation function. */ /* Otherwise, use the translation function. */
translation->translation_func (time, CLIPBOARD, translation->translation_func (time, CLIPBOARD,
translation->atom, fd); MappingAtom (translation),
fd);
} }
else else
close (fd); close (fd);
@ -632,10 +721,55 @@ CreateOffer (struct wl_client *client, Time time)
return resource; return resource;
} }
static Bool
CheckDuplicate (unsigned short index, Atom a)
{
TargetMapping *start;
start = &direct_transfer[index];
/* If the flag is already set, then this type has already been
sent. */
if (MappingFlag (start))
return False;
/* Set this entry's duplicate flag. */
MappingSetFlag (start);
/* As long as the next index still refers to a duplicate of this
item, set its duplicate flag. */
while (MappingIsNextDuplicate (start))
MappingSetFlag (++start);
/* Do the same backwards. */
if (index)
{
start = &direct_transfer[index - 1];
while (MappingIsNextDuplicate (start))
{
MappingSetFlag (start);
/* If this is now the start of the target mapping table,
break. */
if (start == direct_transfer)
break;
start--;
}
}
return True;
}
static void static void
SendOffers (struct wl_resource *resource, Time time) SendOffers (struct wl_resource *resource, Time time)
{ {
int i, j; int i, j;
unsigned int idx;
unsigned short *buckets;
if (time < last_x_selection_change) if (time < last_x_selection_change)
/* This offer is out of date. */ /* This offer is out of date. */
@ -644,16 +778,29 @@ SendOffers (struct wl_resource *resource, Time time)
for (i = 0; i < num_x_selection_targets; ++i) 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;
for (j = 0; j < ArrayElements (direct_transfer); ++j) /* N.B. that duplicates do appear in the atom buckets, which
is intentional. */
buckets = mapping_table.atom_buckets[idx];
for (j = 0; j < mapping_table.n_atom_elements[idx]; ++j)
{ {
if (direct_transfer[j].atom == x_selection_targets[i]) if (MappingIs (&direct_transfer[buckets[j]],
/* If it exists, offer it to the client. TODO: handle x_selection_targets[i])
duplicates. */ && CheckDuplicate (buckets[j],
x_selection_targets[i]))
/* If it exists and was not previously offered, offer it
to the client. */
wl_data_offer_send_offer (resource, wl_data_offer_send_offer (resource,
direct_transfer[j].mime_type); direct_transfer[buckets[j]].mime_type);
} }
} }
/* Clear the duplicate flag of each item in the targets table that
was touched. */
for (i = 0; i < ArrayElements (direct_transfer); ++i)
MappingUnsetFlag (&direct_transfer[i]);
} }
static void static void
@ -1751,10 +1898,13 @@ XLInitXData (void)
SelectSelectionInput (CLIPBOARD); SelectSelectionInput (CLIPBOARD);
/* Initialize atoms used in the direct transfer table. */ /* Initialize atoms used in the direct transfer table. */
direct_transfer[1].atom = UTF8_STRING; direct_transfer[1].atom_flag = UTF8_STRING | Duplicate;
direct_transfer[2].translation_func = PostReceiveConversion; direct_transfer[2].translation_func = PostReceiveConversion;
DirectTransferInitializer (direct_transfer, 3); DirectTransferInitializer (direct_transfer, 3);
/* Set up the direct transfer table. */
SetupMappingTable ();
/* And those used in the data conversions table. */ /* And those used in the data conversions table. */
data_conversions[0].atom = UTF8_STRING; data_conversions[0].atom = UTF8_STRING;
data_conversions[0].type = UTF8_STRING; data_conversions[0].type = UTF8_STRING;