From c190ead1224aa417937c11e2e94809dc8041092e Mon Sep 17 00:00:00 2001 From: oldosfan Date: Thu, 6 Oct 2022 02:09:36 +0000 Subject: [PATCH] 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. --- 12to11.c | 79 +++++++++++++++++++++ 12to11.man | 94 ++++++++++++++++++++++++- Imakefile | 5 +- README | 1 + alloc.c | 8 ++- atoms.c | 7 ++ compositor.h | 30 ++++++++ run.c | 6 ++ seat.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++- surface.c | 15 ++++ xdg_surface.c | 55 ++++++++++++--- xdg_toplevel.c | 94 ++++++++++++++++--------- 12 files changed, 526 insertions(+), 49 deletions(-) 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 =