forked from 12to11/12to11
Implement support for zwp_text_input_manager_v3
* 12to11.c (HandleCmdline): New function. (XLMain): Handle locale, initialize text input and and set up the resource database. * 12to11.man: Document resources and command-line arguments. * Imakefile (SRCS, OBJS): Add text_input.c and text_input.o. (text-input-unstable-v3): New scanner target. * README: Document support for zwp_text_input_manager_v3. * alloc.c (XLMalloc, XLCalloc, XLRealloc): Allow alloc of size 0 to return NULL. * atoms.c (resource_quark, app_quark, QString): New quarks. (XLInitAtoms): Initialize some quarks. * compositor.h (struct _Compositor): Add resource and app names. (struct _RoleFuncs): Add `select_extra_events'. (struct _TextInputFuncs): New structure. * run.c (HandleOneXEvent): Filter events if necessary. * seat.c (AllKeyMask): New define. (input_funcs): New variable. (MakeSeatForDevicePair, NoticeDeviceDisabled): Always assign a single seat the role of "text input seat". (ClearFocusSurface, SetFocusSurface): Call input method hooks. (HackKeyboardModifiers): New function. (DispatchKey): Ignore repeated key events. (XLSeatGetFocus): New function. (XLSeatSetTextInputFuncs, XLSeatGetKeyboardDevice) (XLSeatGetInputMethodSeat, XLSeatDispatchCoreKeyEvent): New function. * surface.c (XLSurfaceSelectExtraEvents): New function. * xdg_surface.c (DefaultEventMask): Add core event mask here. (XLHandleXEventForXdgSurfaces): Handle core key events. (Commit, NoteBounds): Improve debug code. (SelectExtraEvents): New function. (XLGetXdgSurface, XLXdgRoleSetBoundsSize): Improve debug code. (XLInitXdgSurfaces): Reduce size of assoc table. * xdg_toplevel.c (SendConfigure, SendDecorationConfigure, Map) (HandleConfigureEvent, SetMode, UnsetMode): Avoid sending decoration configure before initial commit.
This commit is contained in:
parent
46aa84e4e5
commit
c190ead122
12 changed files with 526 additions and 49 deletions
79
12to11.c
79
12to11.c
|
@ -19,8 +19,10 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
|
@ -62,6 +64,71 @@ DetermineServerTime (void)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
HandleCmdline (int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Set the default resource and class names. */
|
||||
compositor.resource_name = "12to11";
|
||||
compositor.app_name = "12to11";
|
||||
|
||||
if (argc < 2)
|
||||
/* There are no arguments to handle. */
|
||||
return;
|
||||
|
||||
/* Determine the instance name based on the executable. First,
|
||||
remove any leading directory separator from argv[0]. */
|
||||
compositor.app_name = strrchr (argv[0], '/');
|
||||
|
||||
/* If no directory separator was present, just use it. */
|
||||
if (!compositor.app_name)
|
||||
compositor.app_name = argv[0];
|
||||
else
|
||||
/* Otherwise, strip the trailing '/'. */
|
||||
compositor.app_name = compositor.app_name + 1;
|
||||
|
||||
for (i = 1; i < argc; ++i)
|
||||
{
|
||||
if (!strcmp (argv[i], "-help"))
|
||||
{
|
||||
print_usage:
|
||||
fprintf (stderr,
|
||||
"usage: %s [-name name] [-class class]\n",
|
||||
argv[0]);
|
||||
exit (!strcmp (argv[i], "-help") ? 0 : 1);
|
||||
}
|
||||
else if (!strcmp (argv[i], "-class"))
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
fprintf (stderr, "%s: option -class requires a value\n",
|
||||
argv[0]);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
compositor.resource_name = argv[++i];
|
||||
}
|
||||
else if (!strcmp (argv[i], "-name"))
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
fprintf (stderr, "%s: option -name requires a value\n",
|
||||
argv[0]);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
compositor.app_name = argv[++i];
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%s: bad command line option \"%s\"\n",
|
||||
argv[0], argv[1]);
|
||||
goto print_usage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
XLMain (int argc, char **argv)
|
||||
{
|
||||
|
@ -69,6 +136,9 @@ XLMain (int argc, char **argv)
|
|||
struct wl_display *wl_display;
|
||||
const char *socket;
|
||||
|
||||
/* Set the locale. */
|
||||
setlocale (LC_ALL, "");
|
||||
|
||||
dpy = XOpenDisplay (NULL);
|
||||
wl_display = wl_display_create ();
|
||||
|
||||
|
@ -86,6 +156,14 @@ XLMain (int argc, char **argv)
|
|||
exit (1);
|
||||
}
|
||||
|
||||
/* Call XGetDefault with some dummy values to have the resource
|
||||
database set up. */
|
||||
XrmInitialize ();
|
||||
XGetDefault (dpy, "dummmy", "value");
|
||||
|
||||
/* Parse command-line arguments. */
|
||||
HandleCmdline (argc, argv);
|
||||
|
||||
compositor.display = dpy;
|
||||
compositor.conn = XGetXCBConnection (dpy);
|
||||
compositor.wl_display = wl_display;
|
||||
|
@ -124,6 +202,7 @@ XLMain (int argc, char **argv)
|
|||
XLInitExplicitSynchronization ();
|
||||
XLInitWpViewporter ();
|
||||
XLInitDecoration ();
|
||||
XLInitTextInput ();
|
||||
/* This has to come after the rest of the initialization. */
|
||||
DetermineServerTime ();
|
||||
XLRunCompositor ();
|
||||
|
|
94
12to11.man
94
12to11.man
|
@ -3,12 +3,102 @@
|
|||
12to11 - Wayland to X protocol translator
|
||||
.SH SYNOPSIS
|
||||
.B 12to11
|
||||
[\-\fIclass\fP class] [\-\fIname\fP name]
|
||||
.SH DESCRIPTION
|
||||
.I 12to11
|
||||
starts a Wayland compositor on the next available socket;
|
||||
Wayland programs will then be displayed through the X server.
|
||||
.SH OPTIONS
|
||||
None.
|
||||
Some X Toolkit library-style command-line arguments are also accepted,
|
||||
despite the protocol translator being implemented without using a
|
||||
toolkit. These options are:
|
||||
.TP 8
|
||||
.B \-class \fIclass\fP
|
||||
This option specifies the resource class under which resources are to
|
||||
be obtained. When not set, it defaults to the string ``12to11''. The
|
||||
resource class and name used by the protocol translator are \fInot\fP
|
||||
set as the
|
||||
.B WM_CLASS
|
||||
property on windows created by the protocol translator; instead, those
|
||||
windows get the classes and names assigned to them by their individual
|
||||
Wayland clients.
|
||||
.TP 8
|
||||
.B \-name \fIname\fP
|
||||
This option specifies the instance name under which resources are to
|
||||
be obtained. When not set, it defaults to the executable file name.
|
||||
.TP 8
|
||||
.B \-help\fP
|
||||
This option causes the protocol translator to print a message
|
||||
describing options it accepts. The protocol translator will then exit
|
||||
after printing the message.
|
||||
.SH RESOURCES
|
||||
\fI12to11\fP understands some resource names and classes that can be
|
||||
used to specify various settings that affect its behavior. Those
|
||||
resources are listed below:
|
||||
.TP 8
|
||||
.B ximFont\fP (class \fBXimFont\fP)
|
||||
Specifies the font to be used for displaying the preedit or status
|
||||
areas of an input method.
|
||||
.IP
|
||||
In many input methods, the preedit (a.k.a composition or
|
||||
preconversion) string and/or a status window is displayed on screen
|
||||
while typing by the input method server. The client is supposed to
|
||||
determine the font used by the input method server to display --
|
||||
however, the Wayland protocol does not allow Wayland clients to
|
||||
specify that themselves, so the protocol translator has to do that by
|
||||
itself.
|
||||
.TP
|
||||
.B ximStyles\fP (class \fBXimStyles\fP)
|
||||
Specifies the default input method styles used by the protocol
|
||||
translator.
|
||||
.IP
|
||||
X input methods can support different editing styles, which affect how
|
||||
preconversion and status text is displayed while typing. These styles
|
||||
are named:
|
||||
.TP
|
||||
.I overTheSpot
|
||||
In ``over the spot'', the preedit string is displayed in a window
|
||||
created by the input method at a location specified by the Wayland
|
||||
client, typically the text insertion point.
|
||||
.TP
|
||||
.I offTheSpot
|
||||
In ``off the spot'', the preedit string is displayed in a window
|
||||
created by the input method at some location away from the text
|
||||
insertion point. Often, this type of window is placed at the bottom
|
||||
of the preedit window.
|
||||
.TP
|
||||
.I rootWindow
|
||||
In the ``root window'' editing style, the preedit string is displayed
|
||||
in a popup window created by the input method.
|
||||
.TP
|
||||
.I onTheSpot
|
||||
In the ``on the spot'' editing style, the preedit string is displayed
|
||||
inside the text itself by the Wayland client.
|
||||
.PP
|
||||
Not all input methods support all editing styles. In the real world,
|
||||
most only support one or two of the styles listed above. The protocol
|
||||
translator will search for a style supported by the input method when
|
||||
initializing input method support. The \fBximStyles\fP resource is
|
||||
used to control the order in which the protocol translator searches
|
||||
for input styles, and should be a comma separated list of input names,
|
||||
which are searched in left-to-right order. For example,
|
||||
.PP
|
||||
.in +4
|
||||
.EX
|
||||
\fBoverTheSpot,rootWindow\fP
|
||||
.EE
|
||||
.in
|
||||
.PP
|
||||
will result in the protocol translator searching for the ``over the
|
||||
spot'' input style, and if that is not present, the ``root window''
|
||||
style. Whitespace must not be present inside the comma-separated
|
||||
list. When \fBximStyles\fP is not specified, it defaults to:
|
||||
.PP
|
||||
.in +4
|
||||
.EX
|
||||
\fBoverTheSpot,offTheSpot,rootWindow,onTheSpot\fP
|
||||
.EE
|
||||
.in
|
||||
.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
|
||||
|
@ -99,6 +189,6 @@ and
|
|||
window manager hints will result in Wayland programs running
|
||||
incorrectly.
|
||||
.SH "SEE ALSO"
|
||||
X(1), Xorg(1)
|
||||
X(7), Xorg(1)
|
||||
.SH AUTHOR
|
||||
Various contributors.
|
||||
|
|
|
@ -21,7 +21,7 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \
|
|||
dmabuf.c buffer.c select.c xdata.c xsettings.c dnd.c \
|
||||
icon_surface.c primary_selection.c renderer.c \
|
||||
picture_renderer.c explicit_synchronization.c transform.c \
|
||||
wp_viewporter.c decoration.c
|
||||
wp_viewporter.c decoration.c text_input.c
|
||||
|
||||
OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
|
||||
surface.o region.o shm.o atoms.o subcompositor.o positioner.o \
|
||||
|
@ -30,7 +30,7 @@ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
|
|||
dmabuf.o buffer.o select.o xdata.o xsettings.o dnd.o \
|
||||
icon_surface.o primary_selection.o renderer.o \
|
||||
picture_renderer.o explicit_synchronization.o transform.o \
|
||||
wp_viewporter.o decoration.o
|
||||
wp_viewporter.o decoration.o text_input.o
|
||||
|
||||
GENHEADERS = transfer_atoms.h
|
||||
|
||||
|
@ -111,6 +111,7 @@ ScannerTarget(primary-selection-unstable-v1)
|
|||
ScannerTarget(linux-explicit-synchronization-unstable-v1)
|
||||
ScannerTarget(viewporter)
|
||||
ScannerTarget(xdg-decoration-unstable-v1)
|
||||
ScannerTarget(text-input-unstable-v3)
|
||||
|
||||
/* Make OBJS depend on scanner headers, and depend on both them and SRCS. */
|
||||
$(OBJS): $(GENHEADERS)
|
||||
|
|
1
README
1
README
|
@ -66,6 +66,7 @@ complete degree:
|
|||
'zwp_primary_selection_device_manager_v1', version: 1
|
||||
'wp_viewporter', version: 1
|
||||
'zxdg_decoration_manager_v1', version: 1
|
||||
'zwp_text_input_manager_v3', version: 1
|
||||
|
||||
When built with EGL, the following Wayland protocol is also supported:
|
||||
|
||||
|
|
8
alloc.c
8
alloc.c
|
@ -31,7 +31,7 @@ XLMalloc (size_t size)
|
|||
|
||||
ptr = malloc (size);
|
||||
|
||||
if (!ptr)
|
||||
if (!ptr && size)
|
||||
{
|
||||
fprintf (stderr, "Allocation of %zu bytes failed\n",
|
||||
size);
|
||||
|
@ -54,7 +54,7 @@ XLCalloc (size_t nmemb, size_t size)
|
|||
|
||||
ptr = calloc (nmemb, size);
|
||||
|
||||
if (!ptr)
|
||||
if (!ptr && nmemb && size)
|
||||
{
|
||||
fprintf (stderr, "Allocation of %zu * %zu failed\n",
|
||||
nmemb, size);
|
||||
|
@ -96,11 +96,13 @@ XLRealloc (void *ptr, size_t size)
|
|||
|
||||
ptr = realloc (ptr, size);
|
||||
|
||||
if (!ptr)
|
||||
if (size && !ptr)
|
||||
{
|
||||
fprintf (stderr, "Reallocation of %zu bytes failed\n", size);
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* Allow realloc to return NULL if size is also NULL. */
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
|
7
atoms.c
7
atoms.c
|
@ -131,6 +131,8 @@ Atom _NET_WM_OPAQUE_REGION, _XL_BUFFER_RELEASE, _NET_WM_SYNC_REQUEST_COUNTER,
|
|||
_NET_WM_FRAME_TIMINGS, _NET_WM_BYPASS_COMPOSITOR, WM_STATE,
|
||||
_NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_DND;
|
||||
|
||||
XrmQuark resource_quark, app_quark, QString;
|
||||
|
||||
/* Hash table containing atoms. */
|
||||
|
||||
static AtomTable atom_table;
|
||||
|
@ -284,4 +286,9 @@ XLInitAtoms (void)
|
|||
|
||||
/* This is automatically generated. */
|
||||
DirectTransferAtomInit (atoms, 61);
|
||||
|
||||
/* Now, initialize quarks. */
|
||||
resource_quark = XrmPermStringToQuark (compositor.resource_name);
|
||||
app_quark = XrmPermStringToQuark (compositor.app_name);
|
||||
QString = XrmPermStringToQuark ("String");
|
||||
}
|
||||
|
|
30
compositor.h
30
compositor.h
|
@ -26,6 +26,7 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
|||
#include <X11/Xlib-xcb.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xresource.h>
|
||||
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <X11/extensions/Xrender.h>
|
||||
|
@ -80,6 +81,9 @@ struct _Compositor
|
|||
|
||||
/* Whether the server time is monotonic. */
|
||||
Bool server_time_monotonic;
|
||||
|
||||
/* The resource and app names. */
|
||||
const char *resource_name, *app_name;
|
||||
};
|
||||
|
||||
/* Forward declarations from seat.c. */
|
||||
|
@ -920,6 +924,7 @@ struct _RoleFuncs
|
|||
void (*parent_rescale) (Surface *, Role *);
|
||||
void (*note_desync_child) (Surface *, Role *);
|
||||
void (*note_child_synced) (Surface *, Role *);
|
||||
void (*select_extra_events) (Surface *, Role *, unsigned long);
|
||||
};
|
||||
|
||||
struct _Role
|
||||
|
@ -966,6 +971,7 @@ extern void XLSurfacePostResize (Surface *, int, int, int, int);
|
|||
extern void XLSurfaceMoveBy (Surface *, int, int);
|
||||
extern Window XLWindowFromSurface (Surface *);
|
||||
extern void XLUpdateSurfaceOutputs (Surface *, int, int, int, int);
|
||||
extern void XLSurfaceSelectExtraEvents (Surface *, unsigned long);
|
||||
|
||||
extern void SurfaceToWindow (Surface *, double, double, double *, double *);
|
||||
extern void ScaleToWindow (Surface *, double, double, double *, double *);
|
||||
|
@ -1008,6 +1014,8 @@ extern Atom _NET_WM_OPAQUE_REGION, _XL_BUFFER_RELEASE,
|
|||
XdndFinished, _NET_WM_FRAME_TIMINGS, _NET_WM_BYPASS_COMPOSITOR, WM_STATE,
|
||||
_NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_DND;
|
||||
|
||||
extern XrmQuark resource_quark, app_quark, QString;
|
||||
|
||||
/* This is automatically generated by mime4.awk. */
|
||||
extern Atom DirectTransferAtoms;
|
||||
|
||||
|
@ -1247,6 +1255,22 @@ extern void XLDataDeviceSendMotion (Seat *, Surface *, double, double, Time);
|
|||
extern void XLDataDeviceSendLeave (Seat *, Surface *, DataSource *);
|
||||
extern void XLDataDeviceSendDrop (Seat *, Surface *);
|
||||
|
||||
/* Defined in text_input.h. */
|
||||
|
||||
typedef struct _TextInputFuncs TextInputFuncs;
|
||||
|
||||
struct _TextInputFuncs
|
||||
{
|
||||
void (*focus_in) (Seat *, Surface *);
|
||||
void (*focus_out) (Seat *);
|
||||
|
||||
/* The last argument is actually an XIDeviceEvent *. */
|
||||
Bool (*filter_input) (Seat *, Surface *, void *);
|
||||
};
|
||||
|
||||
extern void XLTextInputDispatchCoreEvent (Surface *, XEvent *);
|
||||
extern void XLInitTextInput (void);
|
||||
|
||||
/* Defined in seat.c. */
|
||||
|
||||
extern int xi2_opcode;
|
||||
|
@ -1274,6 +1298,7 @@ extern DataDevice *XLSeatGetDataDevice (Seat *);
|
|||
extern void XLSeatSetDataDevice (Seat *, DataDevice *);
|
||||
extern Bool XLSeatIsInert (Seat *);
|
||||
extern Bool XLSeatIsClientFocused (Seat *, struct wl_client *);
|
||||
extern Surface *XLSeatGetFocus (Seat *);
|
||||
extern void XLSeatShowWindowMenu (Seat *, Surface *, int, int);
|
||||
extern Time XLSeatGetLastUserTime (Seat *);
|
||||
extern void XLSeatBeginDrag (Seat *, DataSource *, Surface *,
|
||||
|
@ -1284,6 +1309,11 @@ extern void *XLSeatAddModifierCallback (Seat *, void (*) (unsigned int, void *),
|
|||
extern void XLSeatRemoveModifierCallback (void *);
|
||||
extern unsigned int XLSeatGetEffectiveModifiers (Seat *);
|
||||
extern Bool XLSeatResizeInProgress (Seat *);
|
||||
extern void XLSeatSetTextInputFuncs (TextInputFuncs *);
|
||||
extern int XLSeatGetKeyboardDevice (Seat *);
|
||||
extern Seat *XLSeatGetInputMethodSeat (void);
|
||||
extern void XLSeatDispatchCoreKeyEvent (Seat *, Surface *, XEvent *,
|
||||
KeySym);
|
||||
|
||||
extern Cursor InitDefaultCursor (void);
|
||||
|
||||
|
|
6
run.c
6
run.c
|
@ -130,6 +130,12 @@ HandleOneXEvent (XEvent *event)
|
|||
{
|
||||
XLHandleOneXEventForDnd (event);
|
||||
|
||||
/* Filter all non-GenericEvents through the input method
|
||||
infrastructure. */
|
||||
if (event->type != GenericEvent
|
||||
&& XFilterEvent (event, event->xany.window))
|
||||
return;
|
||||
|
||||
if (XLHandleXEventForXdgSurfaces (event))
|
||||
return;
|
||||
|
||||
|
|
181
seat.c
181
seat.c
|
@ -75,12 +75,18 @@ static XLAssocTable *devices;
|
|||
|
||||
XLList *live_seats;
|
||||
|
||||
/* This is a mask of all keyboard state. */
|
||||
#define AllKeyMask \
|
||||
(ShiftMask | LockMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask \
|
||||
| Mod4Mask)
|
||||
|
||||
enum
|
||||
{
|
||||
IsInert = 1,
|
||||
IsWindowMenuShown = (1 << 2),
|
||||
IsDragging = (1 << 3),
|
||||
IsDropped = (1 << 4),
|
||||
IsTextInputSeat = (1 << 5),
|
||||
};
|
||||
|
||||
enum
|
||||
|
@ -533,6 +539,9 @@ struct _DeviceInfo
|
|||
#define MaskLen(event) \
|
||||
(((event) >> 3) + 1)
|
||||
|
||||
/* Text input functions. */
|
||||
static TextInputFuncs *input_funcs;
|
||||
|
||||
#define CursorFromRole(role) ((SeatCursor *) (role))
|
||||
|
||||
/* Subcompositor targets used inside cursor subframes. */
|
||||
|
@ -1786,6 +1795,10 @@ MakeSeatForDevicePair (int master_keyboard, int master_pointer,
|
|||
XLMakeAssoc (seats, master_keyboard, seat);
|
||||
XLMakeAssoc (seats, master_pointer, seat);
|
||||
|
||||
if (!live_seats)
|
||||
/* This is the first seat; make it the input seat. */
|
||||
seat->flags |= IsTextInputSeat;
|
||||
|
||||
live_seats = XLListPrepend (live_seats, seat);
|
||||
|
||||
/* Now update the seat state from the X server. */
|
||||
|
@ -2076,7 +2089,7 @@ RunDestroyListeners (Seat *seat)
|
|||
static void
|
||||
NoticeDeviceDisabled (int deviceid)
|
||||
{
|
||||
Seat *seat;
|
||||
Seat *seat, *new;
|
||||
DeviceInfo *info;
|
||||
|
||||
/* First, see if there is any deviceinfo related to the disabled
|
||||
|
@ -2124,6 +2137,18 @@ NoticeDeviceDisabled (int deviceid)
|
|||
|
||||
wl_global_destroy (seat->global);
|
||||
|
||||
/* If it was the input seat, then find a new seat to take its
|
||||
place. */
|
||||
if (seat->flags & IsTextInputSeat
|
||||
&& live_seats)
|
||||
{
|
||||
new = live_seats->data;
|
||||
|
||||
/* This results in nondeterministic selection of input
|
||||
seats, and as such can be confusing to the user. */
|
||||
new->flags |= IsTextInputSeat;
|
||||
}
|
||||
|
||||
/* And release the seat. */
|
||||
|
||||
ReleaseSeat (seat);
|
||||
|
@ -2624,6 +2649,12 @@ ClearFocusSurface (void *data)
|
|||
|
||||
seat->focus_surface = NULL;
|
||||
seat->focus_destroy_callback = NULL;
|
||||
|
||||
XLPrimarySelectionHandleFocusChange (seat);
|
||||
|
||||
/* Tell any input method about the focus change. */
|
||||
if (input_funcs)
|
||||
input_funcs->focus_out (seat);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2697,6 +2728,29 @@ SendKeyboardModifiers (Seat *seat, Surface *focus)
|
|||
UpdateSingleModifiers (seat, keyboard, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
HackKeyboardModifiers (Seat *seat, Surface *focus, int effective,
|
||||
int group)
|
||||
{
|
||||
Keyboard *keyboard;
|
||||
uint32_t serial;
|
||||
SeatClientInfo *info;
|
||||
|
||||
serial = wl_display_next_serial (compositor.wl_display);
|
||||
info = ClientInfoForResource (seat, focus->resource);
|
||||
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
keyboard = info->keyboards.next;
|
||||
|
||||
for (; keyboard != &info->keyboards; keyboard = keyboard->next)
|
||||
/* It is wrong to send the new modifiers in seat->based, but I
|
||||
don't know anything better. */
|
||||
wl_keyboard_send_modifiers (keyboard->resource, serial,
|
||||
effective, 0, 0, group);
|
||||
}
|
||||
|
||||
static void
|
||||
SendUpdatedModifiers (Seat *seat)
|
||||
{
|
||||
|
@ -2758,6 +2812,10 @@ SetFocusSurface (Seat *seat, Surface *focus)
|
|||
XLSurfaceCancelRunOnFree (seat->focus_destroy_callback);
|
||||
seat->focus_destroy_callback = NULL;
|
||||
seat->focus_surface = NULL;
|
||||
|
||||
if (input_funcs)
|
||||
/* Tell any input method about the change. */
|
||||
input_funcs->focus_out (seat);
|
||||
}
|
||||
|
||||
if (!focus)
|
||||
|
@ -2768,6 +2826,10 @@ SetFocusSurface (Seat *seat, Surface *focus)
|
|||
return;
|
||||
}
|
||||
|
||||
if (input_funcs)
|
||||
/* Tell any input method about the change. */
|
||||
input_funcs->focus_in (seat, focus);
|
||||
|
||||
seat->focus_surface = focus;
|
||||
seat->focus_destroy_callback
|
||||
= XLSurfaceRunOnFree (focus, ClearFocusSurface, seat);
|
||||
|
@ -3971,6 +4033,17 @@ DispatchKey (XIDeviceEvent *xev)
|
|||
|
||||
if (seat->focus_surface)
|
||||
{
|
||||
if (input_funcs
|
||||
&& seat->flags & IsTextInputSeat
|
||||
&& input_funcs->filter_input (seat, seat->focus_surface,
|
||||
xev))
|
||||
/* The input method decided to filter the key. */
|
||||
return;
|
||||
|
||||
/* Ignore repeated keys. */
|
||||
if (xev->flags & XIKeyRepeat)
|
||||
return;
|
||||
|
||||
if (xev->evtype == XI_KeyPress)
|
||||
SendKeyboardKey (seat, seat->focus_surface,
|
||||
xev->time, WaylandKeycode (xev->detail),
|
||||
|
@ -4799,6 +4872,12 @@ XLSeatIsClientFocused (Seat *seat, struct wl_client *client)
|
|||
return client == surface_client;
|
||||
}
|
||||
|
||||
Surface *
|
||||
XLSeatGetFocus (Seat *seat)
|
||||
{
|
||||
return seat->focus_surface;
|
||||
}
|
||||
|
||||
void
|
||||
XLSeatShowWindowMenu (Seat *seat, Surface *surface, int root_x,
|
||||
int root_y)
|
||||
|
@ -5272,3 +5351,103 @@ XLSeatResizeInProgress (Seat *seat)
|
|||
{
|
||||
return seat->resize_in_progress;
|
||||
}
|
||||
|
||||
void
|
||||
XLSeatSetTextInputFuncs (TextInputFuncs *funcs)
|
||||
{
|
||||
input_funcs = funcs;
|
||||
}
|
||||
|
||||
int
|
||||
XLSeatGetKeyboardDevice (Seat *seat)
|
||||
{
|
||||
return seat->master_keyboard;
|
||||
}
|
||||
|
||||
Seat *
|
||||
XLSeatGetInputMethodSeat (void)
|
||||
{
|
||||
XLList *list;
|
||||
Seat *seat;
|
||||
|
||||
for (list = live_seats; list; list = list->next)
|
||||
{
|
||||
seat = list->data;
|
||||
|
||||
if (seat->flags & IsTextInputSeat)
|
||||
return seat;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
XLSeatDispatchCoreKeyEvent (Seat *seat, Surface *surface, XEvent *event,
|
||||
KeySym keysym)
|
||||
{
|
||||
unsigned int effective;
|
||||
unsigned int state, group;
|
||||
unsigned int mods_return;
|
||||
KeyCode keycode;
|
||||
KeySym sym_return;
|
||||
|
||||
/* Dispatch a core event generated by an input method to SEAT. If
|
||||
SURFACE is no longer the focus surface, refrain from doing
|
||||
anything. If a keycode can be found for KEYSYM, use that
|
||||
keycode. */
|
||||
|
||||
if (surface != seat->focus_surface)
|
||||
return;
|
||||
|
||||
/* Get the group and state of the key event. */
|
||||
group = event->xkey.state >> 13;
|
||||
state = event->xkey.state & AllKeyMask;
|
||||
|
||||
/* Get the effective state of the seat. */
|
||||
effective = seat->base | seat->latched | seat->locked;
|
||||
|
||||
/* Determine what keycode to use. If a keysym was provided, try to
|
||||
find a corresponding keycode. */
|
||||
|
||||
if (keysym)
|
||||
{
|
||||
/* If looking up the event keycode also results in the keysym,
|
||||
then just use the keycode specified in the event. */
|
||||
if (XkbLookupKeySym (compositor.display, event->xkey.keycode,
|
||||
event->xkey.state, &mods_return, &sym_return)
|
||||
&& keysym == sym_return)
|
||||
{
|
||||
keycode = event->xkey.keycode;
|
||||
}
|
||||
else
|
||||
keycode = XKeysymToKeycode (compositor.display, keysym);
|
||||
|
||||
/* But if no corresponding keycode could be found, use the
|
||||
keycode provided in the event. */
|
||||
if (!keycode)
|
||||
keycode = event->xkey.keycode;
|
||||
}
|
||||
else
|
||||
keycode = event->xkey.keycode;
|
||||
|
||||
if (group != seat->effective_group || state != effective)
|
||||
/* The modifiers in the provided core event are different from
|
||||
what the focus surface was previously sent. Send a new
|
||||
modifier event with the effective state provided in the give
|
||||
core event. */
|
||||
HackKeyboardModifiers (seat, surface, effective, group);
|
||||
|
||||
/* Then send the event. */
|
||||
if (event->xkey.type == KeyPress)
|
||||
SendKeyboardKey (seat, seat->focus_surface, event->xkey.time,
|
||||
WaylandKeycode (keycode),
|
||||
WL_KEYBOARD_KEY_STATE_PRESSED);
|
||||
else
|
||||
SendKeyboardKey (seat, seat->focus_surface, event->xkey.time,
|
||||
WaylandKeycode (keycode),
|
||||
WL_KEYBOARD_KEY_STATE_RELEASED);
|
||||
|
||||
/* Restore the modifiers. */
|
||||
if (group != seat->effective_group || state != effective)
|
||||
SendKeyboardModifiers (seat, surface);
|
||||
}
|
||||
|
|
15
surface.c
15
surface.c
|
@ -1734,6 +1734,21 @@ XLSurfaceMoveBy (Surface *surface, int west, int north)
|
|||
west, north);
|
||||
}
|
||||
|
||||
void
|
||||
XLSurfaceSelectExtraEvents (Surface *surface, unsigned long event_mask)
|
||||
{
|
||||
if (!surface->role
|
||||
|| !surface->role->funcs.select_extra_events)
|
||||
return;
|
||||
|
||||
/* Note that this need only be implemented for surfaces that can get
|
||||
the input focus. */
|
||||
surface->role->funcs.select_extra_events (surface, surface->role,
|
||||
event_mask);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* The following functions convert from window to surface
|
||||
coordinates and vice versa:
|
||||
|
||||
|
|
|
@ -29,6 +29,10 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
|||
|
||||
#define XdgRoleFromRole(role) ((XdgRole *) (role))
|
||||
|
||||
/* This is the default core event mask used by our windows. */
|
||||
#define DefaultEventMask \
|
||||
(ExposureMask | StructureNotifyMask | PropertyChangeMask)
|
||||
|
||||
enum
|
||||
{
|
||||
StatePendingFrameCallback = 1,
|
||||
|
@ -472,6 +476,22 @@ XLHandleXEventForXdgSurfaces (XEvent *event)
|
|||
return False;
|
||||
}
|
||||
|
||||
if (event->type == KeyPress || event->type == KeyRelease)
|
||||
{
|
||||
/* These events are actually sent by the input method library
|
||||
upon receiving XIM_COMMIT messages. */
|
||||
|
||||
role = XLLookUpAssoc (surfaces, event->xkey.window);
|
||||
|
||||
if (role && role->role.surface)
|
||||
{
|
||||
XLTextInputDispatchCoreEvent (role->role.surface, event);
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
window = XLGetGEWindowForSeats (event);
|
||||
|
||||
if (window != None)
|
||||
|
@ -685,7 +705,8 @@ Commit (Surface *surface, Role *role)
|
|||
|
||||
/* This flag means no commit has happened after an
|
||||
ack_configure. */
|
||||
if (!(xdg_role->state & StateWaitingForAckConfigure))
|
||||
if (!(xdg_role->state & StateWaitingForAckConfigure)
|
||||
&& xdg_role->state & StateWaitingForAckCommit)
|
||||
{
|
||||
#ifdef DEBUG_GEOMETRY_CALCULATION
|
||||
fprintf (stderr, "Client aknowledged commit\n");
|
||||
|
@ -1131,15 +1152,13 @@ NoteBounds (void *data, int min_x, int min_y,
|
|||
bounds_width = max_x - min_x + 1;
|
||||
bounds_height = max_y - min_y + 1;
|
||||
|
||||
#ifdef DEBUG_GEOMETRY_CALCULATION
|
||||
fprintf (stderr, "Noticed bounds: %d %d\n", bounds_width, bounds_height);
|
||||
#endif
|
||||
|
||||
if (role->bounds_width != bounds_width
|
||||
|| role->bounds_height != bounds_height)
|
||||
{
|
||||
#ifdef DEBUG_GEOMETRY_CALCULATION
|
||||
fprintf (stderr, "Resizing to: %d %d\n", bounds_width, bounds_height);
|
||||
fprintf (stderr, "Resizing to: %d %d (from: %d %d)\n",
|
||||
bounds_width, bounds_height, role->bounds_width,
|
||||
role->bounds_height);
|
||||
#endif
|
||||
|
||||
if (role->impl->funcs.note_window_pre_resize)
|
||||
|
@ -1357,6 +1376,19 @@ HandleFreeze (void *data)
|
|||
role->state |= StateMaybeConfigure;
|
||||
}
|
||||
|
||||
static void
|
||||
SelectExtraEvents (Surface *surface, Role *role,
|
||||
unsigned long event_mask)
|
||||
{
|
||||
XdgRole *xdg_role;
|
||||
|
||||
xdg_role = XdgRoleFromRole (role);
|
||||
|
||||
/* Select extra events for the input method. */
|
||||
XSelectInput (compositor.display, xdg_role->window,
|
||||
DefaultEventMask | event_mask);
|
||||
}
|
||||
|
||||
void
|
||||
XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
|
||||
uint32_t id, struct wl_resource *surface_resource)
|
||||
|
@ -1431,11 +1463,11 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
|
|||
role->role.funcs.rescale = Rescale;
|
||||
role->role.funcs.note_desync_child = NoteDesyncChild;
|
||||
role->role.funcs.note_child_synced = NoteChildSynced;
|
||||
role->role.funcs.select_extra_events = SelectExtraEvents;
|
||||
|
||||
attrs.colormap = compositor.colormap;
|
||||
attrs.border_pixel = border_pixel;
|
||||
attrs.event_mask = (ExposureMask | StructureNotifyMask
|
||||
| PropertyChangeMask);
|
||||
attrs.event_mask = DefaultEventMask;
|
||||
attrs.cursor = InitDefaultCursor ();
|
||||
flags = (CWColormap | CWBorderPixel | CWEventMask
|
||||
| CWCursor);
|
||||
|
@ -1647,6 +1679,11 @@ XLXdgRoleSetBoundsSize (Role *role, int bounds_width, int bounds_height)
|
|||
xdg_role->bounds_width = bounds_width;
|
||||
xdg_role->bounds_height = bounds_height;
|
||||
|
||||
#ifdef DEBUG_GEOMETRY_CALCULATION
|
||||
fprintf (stderr, "Set new bounds size: %d %d\n", bounds_width,
|
||||
bounds_height);
|
||||
#endif
|
||||
|
||||
/* Now, a temporary bounds_width and bounds_height has been
|
||||
recorded. This means that if a configure event has not yet been
|
||||
delivered, then any subsequent SubcompositorUpdate will cause
|
||||
|
@ -1857,7 +1894,7 @@ XLInitXdgSurfaces (void)
|
|||
XColor alloc;
|
||||
int shape_minor, shape_major, shape_error;
|
||||
|
||||
surfaces = XLCreateAssocTable (2048);
|
||||
surfaces = XLCreateAssocTable (1024);
|
||||
|
||||
alloc.red = 0;
|
||||
alloc.green = 65535;
|
||||
|
|
|
@ -40,15 +40,17 @@ typedef enum _DecorationMode DecorationMode;
|
|||
|
||||
enum
|
||||
{
|
||||
StateIsMapped = 1,
|
||||
StateMissingState = (1 << 1),
|
||||
StatePendingMaxSize = (1 << 2),
|
||||
StatePendingMinSize = (1 << 3),
|
||||
StatePendingAckMovement = (1 << 4),
|
||||
StatePendingResize = (1 << 5),
|
||||
StatePendingConfigureSize = (1 << 6),
|
||||
StatePendingConfigureStates = (1 << 7),
|
||||
StateDecorationModeDirty = (1 << 8),
|
||||
StateIsMapped = 1,
|
||||
StateMissingState = (1 << 1),
|
||||
StatePendingMaxSize = (1 << 2),
|
||||
StatePendingMinSize = (1 << 3),
|
||||
StatePendingAckMovement = (1 << 4),
|
||||
StatePendingResize = (1 << 5),
|
||||
StatePendingConfigureSize = (1 << 6),
|
||||
StatePendingConfigureStates = (1 << 7),
|
||||
StateDecorationModeDirty = (1 << 8),
|
||||
StateEverMapped = (1 << 9),
|
||||
StateNeedDecorationConfigure = (1 << 10),
|
||||
};
|
||||
|
||||
enum
|
||||
|
@ -370,6 +372,27 @@ AddState (XdgToplevel *toplevel, uint32_t state)
|
|||
*data = state;
|
||||
}
|
||||
|
||||
static void
|
||||
SendDecorationConfigure1 (XdgToplevel *toplevel)
|
||||
{
|
||||
#define ServerSide ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
|
||||
#define ClientSide ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE
|
||||
|
||||
if (toplevel->decor == DecorationModeClient)
|
||||
zxdg_toplevel_decoration_v1_send_configure (toplevel->decoration->resource,
|
||||
ClientSide);
|
||||
else
|
||||
zxdg_toplevel_decoration_v1_send_configure (toplevel->decoration->resource,
|
||||
ServerSide);
|
||||
|
||||
#undef ServerSide
|
||||
#undef ClientSide
|
||||
|
||||
/* This means that the decoration should be reapplied upon the next
|
||||
commit. */
|
||||
toplevel->state |= StateDecorationModeDirty;
|
||||
}
|
||||
|
||||
static void
|
||||
SendConfigure (XdgToplevel *toplevel, unsigned int width,
|
||||
unsigned int height)
|
||||
|
@ -380,6 +403,15 @@ SendConfigure (XdgToplevel *toplevel, unsigned int width,
|
|||
xdg_toplevel_send_configure (toplevel->resource, width, height,
|
||||
&toplevel->states);
|
||||
|
||||
/* If a toplevel decoration resource is created and
|
||||
SetMode/UnsetMode is called before the initial toplevel commit,
|
||||
then the toplevel decoration mode must be sent here instead. */
|
||||
|
||||
if (toplevel->state & StateNeedDecorationConfigure
|
||||
&& toplevel->decoration)
|
||||
SendDecorationConfigure1 (toplevel);
|
||||
toplevel->state &= ~StateNeedDecorationConfigure;
|
||||
|
||||
XLXdgRoleSendConfigure (toplevel->role, serial);
|
||||
|
||||
toplevel->conf_reply = True;
|
||||
|
@ -396,27 +428,11 @@ SendDecorationConfigure (XdgToplevel *toplevel)
|
|||
|
||||
serial = wl_display_next_serial (compositor.wl_display);
|
||||
|
||||
#define ServerSide ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
|
||||
#define ClientSide ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE
|
||||
|
||||
if (toplevel->decor == DecorationModeClient)
|
||||
zxdg_toplevel_decoration_v1_send_configure (toplevel->decoration->resource,
|
||||
ClientSide);
|
||||
else
|
||||
zxdg_toplevel_decoration_v1_send_configure (toplevel->decoration->resource,
|
||||
ServerSide);
|
||||
|
||||
#undef ServerSide
|
||||
#undef ClientSide
|
||||
|
||||
SendDecorationConfigure1 (toplevel);
|
||||
XLXdgRoleSendConfigure (toplevel->role, serial);
|
||||
|
||||
toplevel->conf_reply = True;
|
||||
toplevel->conf_serial = serial;
|
||||
|
||||
/* This means that the decoration should be reapplied upon the next
|
||||
commit. */
|
||||
toplevel->state |= StateDecorationModeDirty;
|
||||
}
|
||||
|
||||
/* Forward declaration. */
|
||||
|
@ -1081,7 +1097,7 @@ Map (XdgToplevel *toplevel)
|
|||
at this point. */
|
||||
SubcompositorGarbage (XLSubcompositorFromXdgRole (toplevel->role));
|
||||
|
||||
toplevel->state |= StateIsMapped | StateMissingState;
|
||||
toplevel->state |= StateIsMapped | StateMissingState | StateEverMapped;
|
||||
|
||||
/* Update the width and height from the xdg_surface bounds. */
|
||||
toplevel->width = XLXdgRoleGetWidth (toplevel->role);
|
||||
|
@ -1329,6 +1345,10 @@ HandleConfigureEvent (XdgToplevel *toplevel, XEvent *event)
|
|||
event->xconfigure.height))
|
||||
WriteStates (toplevel);
|
||||
|
||||
/* Set toplevel->width and toplevel->height correctly. */
|
||||
toplevel->width = event->xconfigure.width;
|
||||
toplevel->height = event->xconfigure.height;
|
||||
|
||||
/* Also set the bounds width and height to avoid resizing the
|
||||
window. */
|
||||
XLXdgRoleSetBoundsSize (toplevel->role,
|
||||
|
@ -1345,9 +1365,8 @@ HandleConfigureEvent (XdgToplevel *toplevel, XEvent *event)
|
|||
SendConfigure (toplevel, width, height);
|
||||
}
|
||||
|
||||
/* Set toplevel->width and toplevel->height correctly. */
|
||||
toplevel->width = event->xconfigure.width;
|
||||
toplevel->height = event->xconfigure.height;
|
||||
/* Now set toplevel->configure_width and
|
||||
toplevel->configure_height. */
|
||||
toplevel->configure_width = toplevel->width;
|
||||
toplevel->configure_height = toplevel->height;
|
||||
|
||||
|
@ -2248,7 +2267,12 @@ SetMode (struct wl_client *client, struct wl_resource *resource,
|
|||
return;
|
||||
}
|
||||
|
||||
SendDecorationConfigure (decoration->toplevel);
|
||||
/* According to #wayland the configure event shouldn't be sent for
|
||||
partially initialized surfaces. */
|
||||
if (decoration->toplevel->state & StateEverMapped)
|
||||
SendDecorationConfigure (decoration->toplevel);
|
||||
else
|
||||
decoration->toplevel->state |= StateNeedDecorationConfigure;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2263,7 +2287,13 @@ UnsetMode (struct wl_client *client, struct wl_resource *resource)
|
|||
|
||||
/* Default to using window manager decorations. */
|
||||
decoration->toplevel->decor = DecorationModeWindowManager;
|
||||
SendDecorationConfigure (decoration->toplevel);
|
||||
|
||||
/* According to #wayland the configure event shouldn't be sent for
|
||||
partially initialized surfaces. */
|
||||
if (decoration->toplevel->state & StateEverMapped)
|
||||
SendDecorationConfigure (decoration->toplevel);
|
||||
else
|
||||
decoration->toplevel->state |= StateNeedDecorationConfigure;
|
||||
}
|
||||
|
||||
static struct zxdg_toplevel_decoration_v1_interface decoration_impl =
|
||||
|
|
Loading…
Add table
Reference in a new issue