diff --git a/12to11.c b/12to11.c
index 26765e5..b7ddcf0 100644
--- a/12to11.c
+++ b/12to11.c
@@ -19,8 +19,10 @@ along with 12to11. If not, see . */
#include
#include
+#include
#include
+#include
#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 ();
diff --git a/12to11.man b/12to11.man
index 701e6ae..2331b80 100644
--- a/12to11.man
+++ b/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.
diff --git a/Imakefile b/Imakefile
index 9a4f08a..ab01e1c 100644
--- a/Imakefile
+++ b/Imakefile
@@ -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)
diff --git a/README b/README
index f376cd9..6137870 100644
--- a/README
+++ b/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:
diff --git a/alloc.c b/alloc.c
index 8b89538..37f7cb3 100644
--- a/alloc.c
+++ b/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;
}
diff --git a/atoms.c b/atoms.c
index 30ade15..357c0f9 100644
--- a/atoms.c
+++ b/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");
}
diff --git a/compositor.h b/compositor.h
index 7a7a79a..b44e076 100644
--- a/compositor.h
+++ b/compositor.h
@@ -26,6 +26,7 @@ along with 12to11. If not, see . */
#include
#include
#include
+#include
#include
#include
@@ -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);
diff --git a/run.c b/run.c
index 7a282c9..27e6001 100644
--- a/run.c
+++ b/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;
diff --git a/seat.c b/seat.c
index 111a5ef..812347b 100644
--- a/seat.c
+++ b/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);
+}
diff --git a/surface.c b/surface.c
index 502c609..223144d 100644
--- a/surface.c
+++ b/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:
diff --git a/xdg_surface.c b/xdg_surface.c
index 82d5378..2bdb31c 100644
--- a/xdg_surface.c
+++ b/xdg_surface.c
@@ -29,6 +29,10 @@ along with 12to11. If not, see . */
#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;
diff --git a/xdg_toplevel.c b/xdg_toplevel.c
index 90bce59..f485301 100644
--- a/xdg_toplevel.c
+++ b/xdg_toplevel.c
@@ -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 =