diff --git a/12to11.c b/12to11.c index 3b19f2f..6aa6061 100644 --- a/12to11.c +++ b/12to11.c @@ -236,6 +236,7 @@ XLMain (int argc, char **argv) XLInitDrmLease (); XLInitPointerConstraints (); XLInitRelativePointer (); + XLInitKeyboardShortcutsInhibit (); /* This has to come after the rest of the initialization. */ DetermineServerTime (); diff --git a/Imakefile b/Imakefile index 4c79140..22547a0 100644 --- a/Imakefile +++ b/Imakefile @@ -24,7 +24,7 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \ picture_renderer.c explicit_synchronization.c transform.c \ wp_viewporter.c decoration.c text_input.c \ single_pixel_buffer.c drm_lease.c pointer_constraints.c \ - time.c relative_pointer.c + time.c relative_pointer.c keyboard_shortcuts_inhibit.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 \ @@ -35,7 +35,7 @@ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \ picture_renderer.o explicit_synchronization.o transform.o \ wp_viewporter.o decoration.o text_input.o \ single_pixel_buffer.o drm_lease.o pointer_constraints.o \ - time.o relative_pointer.o + time.o relative_pointer.o keyboard_shortcuts_inhibit.o GENHEADERS = transfer_atoms.h @@ -121,6 +121,7 @@ ScannerTarget(single-pixel-buffer-v1) ScannerTarget(drm-lease-v1) ScannerTarget(pointer-constraints-unstable-v1) ScannerTarget(relative-pointer-unstable-v1) +ScannerTarget(keyboard-shortcuts-inhibit-unstable-v1) /* Make OBJS depend on scanner headers, and depend on both them and SRCS. */ $(OBJS): $(GENHEADERS) diff --git a/compositor.h b/compositor.h index e517752..d1c48e2 100644 --- a/compositor.h +++ b/compositor.h @@ -892,11 +892,13 @@ typedef struct _RoleFuncs RoleFuncs; typedef struct _CommitCallback CommitCallback; typedef struct _UnmapCallback UnmapCallback; typedef struct _DestroyCallback DestroyCallback; +typedef struct _ClientData ClientData; enum _ClientDataType { SubsurfaceData, PointerConfinementData, + ShortcutInhibitData, MaxClientData, }; @@ -937,6 +939,21 @@ struct _CommitCallback CommitCallback *next, *last; }; +struct _ClientData +{ + /* The next piece of client data attached to this surface. */ + ClientData *next; + + /* The client data itself. */ + void *data; + + /* The free function. */ + void (*free_function) (void *); + + /* The type of the client data. */ + ClientDataType type; +}; + struct _Surface { /* The view associated with this surface. */ @@ -968,11 +985,8 @@ struct _Surface /* List of subsurfaces. */ XLList *subsurfaces; - /* Array of "client data". */ - void *client_data[MaxClientData]; - - /* List of functions for freeing "client data". */ - void (*free_client_data[MaxClientData]) (void *); + /* List of client data. */ + ClientData *client_data; /* List of commit callbacks. */ CommitCallback commit_callbacks; @@ -1087,6 +1101,7 @@ extern DestroyCallback *XLSurfaceRunOnFree (Surface *, void (*) (void *), extern void XLSurfaceCancelRunOnFree (DestroyCallback *); extern void *XLSurfaceGetClientData (Surface *, ClientDataType, size_t, void (*) (void *)); +extern void *XLSurfaceFindClientData (Surface *, ClientDataType); extern Bool XLSurfaceGetResizeDimensions (Surface *, int *, int *); extern void XLSurfacePostResize (Surface *, int, int, int, int); extern void XLSurfaceMoveBy (Surface *, int, int); @@ -1450,6 +1465,8 @@ extern void XLSeatLockPointer (Seat *); extern void XLSeatUnlockPointer (Seat *); extern RelativePointer *XLSeatGetRelativePointer (Seat *, struct wl_resource *); extern void XLSeatDestroyRelativePointer (RelativePointer *); +extern Bool XLSeatApplyExternalGrab (Seat *, Surface *); +extern void XLSeatCancelExternalGrab (Seat *); extern Cursor InitDefaultCursor (void); @@ -1623,6 +1640,12 @@ extern void XLInitRelativePointer (void); extern void XLRelativePointerSendRelativeMotion (struct wl_resource *, uint64_t, double, double); +/* Defined in keyboard_shortcuts_inhibit.c. */ + +extern void XLInitKeyboardShortcutsInhibit (void); +extern void XLCheckShortcutInhibition (Seat *, Surface *); +extern void XLReleaseShortcutInhibition (Seat *, Surface *); + /* Utility functions that don't belong in a specific file. */ #define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0]) diff --git a/pointer_constraints.c b/pointer_constraints.c index 5970840..b670db1 100644 --- a/pointer_constraints.c +++ b/pointer_constraints.c @@ -1776,7 +1776,7 @@ Reconfine (Surface *surface, int *root_x, int *root_y, if (!XLWindowFromSurface (surface)) return; - record = surface->client_data[PointerConfinementData]; + record = XLSurfaceFindClientData (surface, PointerConfinementData); if (!record) return; @@ -1813,7 +1813,7 @@ XLPointerBarrierLeft (Seat *seat, Surface *surface) /* The pointer has now left the given surface. If there is an active confinement for that surface and seat, disable it. */ - record = surface->client_data[PointerConfinementData]; + record = XLSurfaceFindClientData (surface, PointerConfinementData); if (!record) return; @@ -1834,7 +1834,7 @@ XLPointerBarrierCheck (Seat *seat, Surface *dispatch, double x, double y, pixman_box32_t box; int offset_x, offset_y; - record = dispatch->client_data[PointerConfinementData]; + record = XLSurfaceFindClientData (dispatch, PointerConfinementData); if (!record) return; @@ -1959,7 +1959,7 @@ XLPointerConstraintsSurfaceMovedTo (Surface *surface, int root_x, query for the position manually. Simply move the lines for the surface's window and each of its subsurfaces. */ - record = surface->client_data[PointerConfinementData]; + record = XLSurfaceFindClientData (surface, PointerConfinementData); if (!record) return; diff --git a/seat.c b/seat.c index 5e3c1bd..aac45bf 100644 --- a/seat.c +++ b/seat.c @@ -82,13 +82,14 @@ XLList *live_seats; enum { - IsInert = 1, - IsWindowMenuShown = (1 << 2), - IsDragging = (1 << 3), - IsDropped = (1 << 4), - IsTextInputSeat = (1 << 5), - IsPointerLocked = (1 << 6), - IsSurfaceCoordSet = (1 << 7), + IsInert = 1, + IsWindowMenuShown = (1 << 2), + IsDragging = (1 << 3), + IsDropped = (1 << 4), + IsTextInputSeat = (1 << 5), + IsPointerLocked = (1 << 6), + IsSurfaceCoordSet = (1 << 7), + IsExternalGrabApplied = (1 << 8), }; enum @@ -362,6 +363,12 @@ struct _Seat /* The last user time. */ Timestamp last_user_time; + /* The last time the focus changed into a surface. */ + Timestamp last_focus_time; + + /* When the last external grab was applied. */ + Time external_grab_time; + /* wl_global associated with this seat. */ struct wl_global *global; @@ -1749,7 +1756,7 @@ HandleBind (struct wl_client *client, void *data, { struct wl_resource *resource; char *name; - ptrdiff_t length; + size_t length; Seat *seat; seat = data; @@ -2665,7 +2672,7 @@ static void SelectDeviceEvents (void) { XIEventMask mask; - ptrdiff_t length; + size_t length; length = XIMaskLen (XI_LASTEVENT); mask.mask = alloca (length); @@ -2864,6 +2871,10 @@ SetFocusSurface (Seat *seat, Surface *focus) { SendKeyboardLeave (seat, seat->focus_surface); + /* Cancel any grab that may be associated with shortcut + inhibition. */ + XLReleaseShortcutInhibition (seat, seat->focus_surface); + XLSurfaceCancelRunOnFree (seat->focus_destroy_callback); seat->focus_destroy_callback = NULL; seat->focus_surface = NULL; @@ -2881,6 +2892,9 @@ SetFocusSurface (Seat *seat, Surface *focus) return; } + /* Apply any shortcut inhibition. */ + XLCheckShortcutInhibition (seat, focus); + if (input_funcs) /* Tell any input method about the change. */ input_funcs->focus_in (seat, focus); @@ -2907,6 +2921,9 @@ DispatchFocusIn (Surface *surface, XIFocusInEvent *event) if (!seat) return; + /* Record the time the focus changed for the external grab. */ + seat->last_focus_time = TimestampFromServerTime (event->time); + SetFocusSurface (seat, surface); } @@ -4553,7 +4570,7 @@ FakePointerEdge (Seat *seat, Surface *target, uint32_t serial, Status state; Window window; XIEventMask mask; - ptrdiff_t length; + size_t length; if (edge == NoneEdge) return False; @@ -4751,7 +4768,7 @@ void XLSelectStandardEvents (Window window) { XIEventMask mask; - ptrdiff_t length; + size_t length; length = XIMaskLen (XI_LASTEVENT); mask.mask = alloca (length); @@ -4965,7 +4982,7 @@ XLSeatExplicitlyGrabSurface (Seat *seat, Surface *surface, uint32_t serial) WhatEdge edge; Time time; XIEventMask mask; - ptrdiff_t length; + size_t length; Cursor cursor; if (seat->flags & IsInert @@ -5017,6 +5034,8 @@ XLSeatExplicitlyGrabSurface (Seat *seat, Surface *surface, uint32_t serial) XISetMask (mask.mask, XI_Motion); XISetMask (mask.mask, XI_ButtonPress); XISetMask (mask.mask, XI_ButtonRelease); + XISetMask (mask.mask, XI_KeyPress); + XISetMask (mask.mask, XI_KeyRelease); cursor = (seat->cursor ? seat->cursor->cursor : None); @@ -5036,9 +5055,14 @@ XLSeatExplicitlyGrabSurface (Seat *seat, Surface *surface, uint32_t serial) that keyboard focus cannot be changed, which is not very crucial, so it is allowed to fail. */ - XIGrabDevice (compositor.display, seat->master_keyboard, - window, time, None, XIGrabModeAsync, - XIGrabModeAsync, True, &mask); + state = XIGrabDevice (compositor.display, seat->master_keyboard, + window, time, None, XIGrabModeAsync, + XIGrabModeAsync, True, &mask); + + /* Cancel any external grab that might be applied if the keyboard + grab succeeded. */ + if (state == Success) + seat->flags &= ~IsExternalGrabApplied; /* And record the grab surface, so that owner_events can be implemented correctly. */ @@ -5332,7 +5356,7 @@ XLSeatBeginDrag (Seat *seat, DataSource *data_source, Surface *start_surface, Window window; Time time; XIEventMask mask; - ptrdiff_t length; + size_t length; WhatEdge edge; Status state; @@ -5737,3 +5761,59 @@ XLSeatDestroyRelativePointer (RelativePointer *relative_pointer) XLFree (relative_pointer); } + +Bool +XLSeatApplyExternalGrab (Seat *seat, Surface *surface) +{ + Window window; + Status state; + XIEventMask mask; + size_t length; + + /* Grab the toplevel SURFACE on SEAT. */ + + window = XLWindowFromSurface (surface); + + if (!window) + return None; + + length = XIMaskLen (XI_LASTEVENT); + mask.mask = alloca (length); + mask.mask_len = length; + mask.deviceid = XIAllMasterDevices; + + memset (mask.mask, 0, length); + + /* Grab focus and key events. */ + XISetMask (mask.mask, XI_FocusIn); + XISetMask (mask.mask, XI_FocusOut); + XISetMask (mask.mask, XI_KeyPress); + XISetMask (mask.mask, XI_KeyRelease); + + state = XIGrabDevice (compositor.display, seat->master_keyboard, + window, seat->last_focus_time.milliseconds, None, + XIGrabModeAsync, XIGrabModeAsync, True, &mask); + if (state == Success) + { + /* Mark an external grab as having been applied. */ + seat->flags |= IsExternalGrabApplied; + + /* Record the time when it was applied. */ + seat->external_grab_time = seat->last_focus_time.milliseconds; + + return True; + } + + return False; +} + +void +XLSeatCancelExternalGrab (Seat *seat) +{ + if (!(seat->flags & IsExternalGrabApplied)) + return; + + /* Cancel the external grab. */ + XIUngrabDevice (compositor.display, seat->master_keyboard, + seat->external_grab_time); +} diff --git a/subsurface.c b/subsurface.c index 92ce10a..b9182ff 100644 --- a/subsurface.c +++ b/subsurface.c @@ -792,7 +792,8 @@ Teardown (Surface *surface, Role *role) ViewUnparent (surface->under); ViewSetSubcompositor (surface->under, NULL); - client = subsurface->parent->client_data[SubsurfaceData]; + client = XLSurfaceFindClientData (subsurface->parent, + SubsurfaceData); if (client) { @@ -986,7 +987,7 @@ XLSubsurfaceHandleParentCommit (Surface *parent) { SurfaceActionClientData *client; - client = parent->client_data[SubsurfaceData]; + client = XLSurfaceFindClientData (parent, SubsurfaceData); if (client) RunSurfaceActions (&client->actions); diff --git a/surface.c b/surface.c index 6ec7ec5..a793be6 100644 --- a/surface.c +++ b/surface.c @@ -1305,7 +1305,7 @@ static void HandleSurfaceDestroy (struct wl_resource *resource) { Surface *surface; - int i; + ClientData *data, *last; surface = wl_resource_get_user_data (resource); @@ -1322,13 +1322,18 @@ HandleSurfaceDestroy (struct wl_resource *resource) NotifySubsurfaceDestroyed); /* Then release all client data. */ - for (i = 0; i < MaxClientData; ++i) - { - if (surface->client_data[i]) - surface->free_client_data[i] (surface->client_data[i]); - XLFree (surface->client_data[i]); + data = surface->client_data; - surface->client_data[i] = NULL; + while (data) + { + /* Free the client data. */ + data->free_function (data->data); + XLFree (data->data); + + /* And its record. */ + last = data; + data = data->next; + XLFree (last); } /* Release the output region. */ @@ -1641,13 +1646,38 @@ void * XLSurfaceGetClientData (Surface *surface, ClientDataType type, size_t size, void (*free_func) (void *)) { - if (surface->client_data[type]) - return surface->client_data[type]; + ClientData *data; - surface->client_data[type] = XLCalloc (1, size); - surface->free_client_data[type] = free_func; + /* First, look for existing client data. */ + for (data = surface->client_data; data; data = data->next) + { + if (data->type == type) + return data->data; + } - return surface->client_data[type]; + /* Next, allocate some new client data. */ + data = XLMalloc (sizeof *data); + data->next = surface->client_data; + surface->client_data = data; + data->data = XLCalloc (1, size); + data->free_function = free_func; + data->type = type; + + return data->data; +} + +void * +XLSurfaceFindClientData (Surface *surface, ClientDataType type) +{ + ClientData *data; + + for (data = surface->client_data; data; data = data->next) + { + if (data->type == type) + return data->data; + } + + return NULL; } Window diff --git a/text_input.c b/text_input.c index ca45df9..0feb053 100644 --- a/text_input.c +++ b/text_input.c @@ -3217,26 +3217,29 @@ FilterInputCallback (Seat *seat, Surface *surface, void *event, /* Find the enabled text input. */ if (info) - input = FindEnabledTextInput (info); - - /* If there is an enabled text input, start filtering the event. */ - if (info && input && input->xic) { - DebugPrint ("found enabled text input %p on client-seat info %p", - input, info); + input = FindEnabledTextInput (info); - /* Convert the extension event into a fake core event that the - input method can understand. */ - ConvertKeyEvent (xev, &xkey); + /* If there is an enabled text input, start filtering the + event. */ + if (input && input->xic) + { + DebugPrint ("found enabled text input %p on client-seat info %p", + input, info); - /* And return the result of filtering the event. */ - if (XFilterEvent (&xkey, XLWindowFromSurface (surface))) - return True; + /* Convert the extension event into a fake core event that + the input method can understand. */ + ConvertKeyEvent (xev, &xkey); - /* Otherwise, call XmbLookupString. If a keysym is returned, - return False. Otherwise, commit the string looked up and - return True. */ - return LookupString (input, &xkey, keysym); + /* And return the result of filtering the event. */ + if (XFilterEvent (&xkey, XLWindowFromSurface (surface))) + return True; + + /* Otherwise, call XmbLookupString. If a keysym is + returned, return False. Otherwise, commit the string + looked up and return True. */ + return LookupString (input, &xkey, keysym); + } } /* Otherwise, do nothing. */