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 ();
XLInitXSettings ();
XLInitIconSurfaces ();
XLInitPrimarySelection ();
/* This has to come after the rest of the initialization. */
DetermineServerTime ();
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 \
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 \
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 \
xdg-shell.o surface.o region.o shm.o atoms.o subcompositor.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 \
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
ANALYZE = -fanalyzer
@ -53,7 +55,7 @@ CDEBUGFLAGS := -fno-common -Wall -Warith-conversion -Wdate-time \
$(ANALYZE)
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 waste a lot of space on disk.
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
$(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::
$(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
/* 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_data_device_manager', version: 3
'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
support for multiple-provider setups (help wanted).
Primary selections and window decorations are also not supported, even
though they fit in nicely with X window management.
Window decorations are also not supported, even though they fit in
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:

View file

@ -965,6 +965,11 @@ extern void XLInitIconSurfaces (void);
extern void XLReleaseIconSurface (IconSurface *);
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. */
#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
source if it exists. */
resource = AddDataOffer (client, source);
if (!resource)
/* Allocation of the resource failed. */
goto next;
offer = wl_resource_get_user_data (resource);
offer->dnd_serial = serial;
offer->last_action = -1;
@ -1397,6 +1402,7 @@ XLDataDeviceSendEnter (Seat *seat, Surface *surface, double x, double y,
}
next:
reference = reference->next;
}
}

View file

@ -26,7 +26,7 @@ BEGIN {
match ($0, / ([a-z]+\/[-+.[:alnum:]]+) /, array)
name = array[1]
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 {

13
seat.c
View file

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

View file

@ -1167,7 +1167,13 @@ XLSurfaceRunFrameCallbacks (Surface *surface, struct timespec time)
uint32_t ms_time;
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,
ms_time);

182
xdata.c
View file

@ -42,6 +42,7 @@ typedef struct _ConversionTransferInfo ConversionTransferInfo;
typedef struct _WriteInfo WriteInfo;
typedef struct _ConversionWriteInfo ConversionWriteInfo;
typedef struct _DataConversion DataConversion;
typedef struct _TargetMappingTable TargetMappingTable;
struct _ReadTargetsData
{
@ -54,8 +55,12 @@ struct _ReadTargetsData
struct _TargetMapping
{
/* The atom of the X target. */
Atom atom;
/* The atom of the X target. The top 3 bits of an XID are
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. */
const char *mime_type;
@ -64,6 +69,13 @@ struct _TargetMapping
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
{
/* The MIME type of the Wayland offer. */
@ -179,20 +191,43 @@ struct _ConversionWriteInfo
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. */
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
and vice versa. */
static TargetMapping direct_transfer[] =
{
{ 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" },
/* These mappings are automatically generated. */
DirectTransferMappings
};
/* Lookup table for such mappings. */
static TargetMappingTable mapping_table;
/* Map of Wayland offer types to X atoms and data conversion
functions. */
static DataConversion data_conversions[] =
@ -244,6 +279,53 @@ Accept (struct wl_client *client, struct wl_resource *resource,
/* 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
HasSelectionTarget (Atom atom)
{
@ -261,13 +343,18 @@ HasSelectionTarget (Atom atom)
static TargetMapping *
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)
&& HasSelectionTarget (direct_transfer[i].atom))
return &direct_transfer[i];
if (!strcmp (direct_transfer[buckets[i]].mime_type,
mime_type)
&& HasSelectionTarget (MappingAtom (&direct_transfer[buckets[i]])))
return &direct_transfer[buckets[i]];
}
return NULL;
@ -575,11 +662,13 @@ Receive (struct wl_client *client, struct wl_resource *resource,
{
if (!translation->translation_func)
/* If a corresponding target exists, ask to receive it. */
PostReceiveDirect (time, CLIPBOARD, translation->atom, fd);
PostReceiveDirect (time, CLIPBOARD,
MappingAtom (translation), fd);
else
/* Otherwise, use the translation function. */
translation->translation_func (time, CLIPBOARD,
translation->atom, fd);
MappingAtom (translation),
fd);
}
else
close (fd);
@ -632,10 +721,55 @@ CreateOffer (struct wl_client *client, Time time)
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
SendOffers (struct wl_resource *resource, Time time)
{
int i, j;
unsigned int idx;
unsigned short *buckets;
if (time < last_x_selection_change)
/* 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)
{
/* 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 it exists, offer it to the client. TODO: handle
duplicates. */
if (MappingIs (&direct_transfer[buckets[j]],
x_selection_targets[i])
&& 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,
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
@ -1751,10 +1898,13 @@ XLInitXData (void)
SelectSelectionInput (CLIPBOARD);
/* 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;
DirectTransferInitializer (direct_transfer, 3);
/* Set up the direct transfer table. */
SetupMappingTable ();
/* And those used in the data conversions table. */
data_conversions[0].atom = UTF8_STRING;
data_conversions[0].type = UTF8_STRING;