forked from 12to11/12to11
Improve behavior under older window managers
* 12to11.man: Update documentation. * compositor.h (enum _FocusMode): New enum. (struct _RoleFuncs, struct _XdgRoleImplementationFuncs): Add `note_focus'. * seat.c (CancelResizeOperation, InterceptButtonEventForResize): Re-enter surface underneath pointer upon completion. (InterceptResizeEvent): Accept subcompositor argument. (NoticeDeviceDisabled): Unfocus surface upon the seat becoming inert. (SetFocusSurface): Call surface note_focus function. (DispatchMotion, DispatchButton): Adjust calls to InterceptResizeEvent. (HandleResizeUnmapped): Adjust calls to CancelResizeOperation. (DispatchBarrierHit): Use correct surface. (FakePointerEdge): Leave any entered surface, as button events will no longer be reported. * surface.c (XLSurfaceNoteFocus): New function. * xdg_surface.c (NoteFocus): New function. (XLGetXdgSurface): Attach new function. * xdg_toplevel.c (struct _XdgToplevel): New field focus_seat_count. (SendStates): Don't compute width and height when just sending state. (NoteFocus): Increment focus seat count. (XLGetXdgToplevel): Attach new function if the window manager does not support _NET_WM_STATE_FOCUSED.
This commit is contained in:
parent
d38093f59e
commit
b515414946
6 changed files with 161 additions and 42 deletions
|
@ -245,10 +245,7 @@ environment variable may help.
|
|||
Using this protocol translator under a window manager that does not at
|
||||
least support the
|
||||
.B _NET_WM_SYNC_REQUEST
|
||||
and
|
||||
.B _NET_WM_STATE
|
||||
window manager hints will result in Wayland programs running
|
||||
incorrectly.
|
||||
window manager protocol will result in Wayland programs running badly.
|
||||
.PP
|
||||
In addition, surfaces transforms are not supported nor reported. The
|
||||
vast majority of clients seem not to make use of this feature, and
|
||||
|
|
10
compositor.h
10
compositor.h
|
@ -816,6 +816,13 @@ typedef struct _SyncRelease SyncRelease;
|
|||
typedef struct _State State;
|
||||
typedef struct _FrameCallback FrameCallback;
|
||||
typedef enum _RoleType RoleType;
|
||||
typedef enum _FocusMode FocusMode;
|
||||
|
||||
enum _FocusMode
|
||||
{
|
||||
SurfaceFocusIn,
|
||||
SurfaceFocusOut,
|
||||
};
|
||||
|
||||
enum _RoleType
|
||||
{
|
||||
|
@ -1067,6 +1074,7 @@ struct _RoleFuncs
|
|||
void (*note_desync_child) (Surface *, Role *);
|
||||
void (*note_child_synced) (Surface *, Role *);
|
||||
void (*select_extra_events) (Surface *, Role *, unsigned long);
|
||||
void (*note_focus) (Surface *, Role *, FocusMode);
|
||||
};
|
||||
|
||||
struct _Role
|
||||
|
@ -1116,6 +1124,7 @@ 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 XLSurfaceNoteFocus (Surface *, FocusMode);
|
||||
|
||||
extern void SurfaceToWindow (Surface *, double, double, double *, double *);
|
||||
extern void ScaleToWindow (Surface *, double, double, double *, double *);
|
||||
|
@ -1257,6 +1266,7 @@ struct _XdgRoleImplementationFuncs
|
|||
void (*post_resize) (Role *, XdgRoleImplementation *, int, int, int, int);
|
||||
void (*commit_inside_frame) (Role *, XdgRoleImplementation *);
|
||||
Bool (*is_window_mapped) (Role *, XdgRoleImplementation *);
|
||||
void (*note_focus) (Role *, XdgRoleImplementation *, FocusMode);
|
||||
};
|
||||
|
||||
struct _XdgRoleImplementation
|
||||
|
|
87
seat.c
87
seat.c
|
@ -1996,9 +1996,18 @@ RunResizeDoneCallbacks (Seat *seat)
|
|||
seat->resize_callbacks.last = &seat->resize_callbacks;
|
||||
}
|
||||
|
||||
/* Forward declarations. */
|
||||
static void TransformToSurface (Surface *, double, double, double *, double *);
|
||||
static Surface *FindSurfaceUnder (Subcompositor *, double, double);
|
||||
static void EnteredSurface (Seat *, Surface *, Time, double, double, Bool);
|
||||
|
||||
static void
|
||||
CancelResizeOperation (Seat *seat, Time time)
|
||||
CancelResizeOperation (Seat *seat, Time time, Subcompositor *subcompositor,
|
||||
XIDeviceEvent *xev)
|
||||
{
|
||||
Surface *dispatch;
|
||||
double x, y;
|
||||
|
||||
/* Stop the resize operation. */
|
||||
XLSurfaceCancelUnmapCallback (seat->resize_surface_callback);
|
||||
seat->resize_surface = NULL;
|
||||
|
@ -2008,11 +2017,30 @@ CancelResizeOperation (Seat *seat, Time time)
|
|||
|
||||
/* Ungrab the pointer. */
|
||||
XIUngrabDevice (compositor.display, seat->master_pointer,
|
||||
time);
|
||||
xev->time);
|
||||
|
||||
if (!subcompositor)
|
||||
return;
|
||||
|
||||
/* If there's no focus surface, look up the surface and enter
|
||||
it. The grab should not be held at this point. */
|
||||
dispatch = FindSurfaceUnder (subcompositor, xev->event_x, xev->event_y);
|
||||
|
||||
if (dispatch)
|
||||
{
|
||||
TransformToSurface (dispatch, xev->event_x, xev->event_y, &x, &y);
|
||||
|
||||
/* Enter the surface. */
|
||||
EnteredSurface (seat, dispatch, xev->time, x, y, False);
|
||||
}
|
||||
|
||||
/* Otherwise, there should be no need to leave any entered surface,
|
||||
since the entered surface should be NULL already. */
|
||||
}
|
||||
|
||||
static Bool
|
||||
InterceptButtonEventForResize (Seat *seat, XIDeviceEvent *xev)
|
||||
InterceptButtonEventForResize (Seat *seat, Subcompositor *subcompositor,
|
||||
XIDeviceEvent *xev)
|
||||
{
|
||||
if (xev->type == XI_ButtonPress)
|
||||
return True;
|
||||
|
@ -2020,7 +2048,7 @@ InterceptButtonEventForResize (Seat *seat, XIDeviceEvent *xev)
|
|||
/* If the button starting the resize has been released, cancel the
|
||||
resize operation. */
|
||||
if (xev->detail == seat->resize_button)
|
||||
CancelResizeOperation (seat, xev->time);
|
||||
CancelResizeOperation (seat, xev->time, subcompositor, xev);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
@ -2114,7 +2142,8 @@ InterceptMotionEventForResize (Seat *seat, XIDeviceEvent *xev)
|
|||
}
|
||||
|
||||
static Bool
|
||||
InterceptResizeEvent (Seat *seat, XIDeviceEvent *xev)
|
||||
InterceptResizeEvent (Seat *seat, Subcompositor *subcompositor,
|
||||
XIDeviceEvent *xev)
|
||||
{
|
||||
if (!seat->resize_surface)
|
||||
return False;
|
||||
|
@ -2122,7 +2151,7 @@ InterceptResizeEvent (Seat *seat, XIDeviceEvent *xev)
|
|||
switch (xev->evtype)
|
||||
{
|
||||
case XI_ButtonRelease:
|
||||
return InterceptButtonEventForResize (seat, xev);
|
||||
return InterceptButtonEventForResize (seat, subcompositor, xev);
|
||||
|
||||
case XI_Motion:
|
||||
return InterceptMotionEventForResize (seat, xev);
|
||||
|
@ -2145,6 +2174,9 @@ RunDestroyListeners (Seat *seat)
|
|||
}
|
||||
}
|
||||
|
||||
/* Forward declaration. */
|
||||
static void SetFocusSurface (Seat *, Surface *);
|
||||
|
||||
static void
|
||||
NoticeDeviceDisabled (int deviceid)
|
||||
{
|
||||
|
@ -2174,6 +2206,11 @@ NoticeDeviceDisabled (int deviceid)
|
|||
|
||||
seat->flags |= IsInert;
|
||||
|
||||
/* Set the focus surface to NULL, so surfaces don't mistakenly
|
||||
treat themselves as still focused. */
|
||||
|
||||
SetFocusSurface (seat, NULL);
|
||||
|
||||
/* Run destroy handlers. */
|
||||
|
||||
RunDestroyListeners (seat);
|
||||
|
@ -2455,7 +2492,6 @@ HandleResizeComplete (Seat *seat)
|
|||
/* Forward declarations. */
|
||||
|
||||
static int GetXButton (int);
|
||||
static void TransformToSurface (Surface *, double, double, double *, double *);
|
||||
static void SendButton (Seat *, Surface *, Time, uint32_t, uint32_t,
|
||||
double, double);
|
||||
|
||||
|
@ -2871,6 +2907,9 @@ SetFocusSurface (Seat *seat, Surface *focus)
|
|||
{
|
||||
SendKeyboardLeave (seat, seat->focus_surface);
|
||||
|
||||
/* Tell the surface it's no longer focused. */
|
||||
XLSurfaceNoteFocus (seat->focus_surface, SurfaceFocusOut);
|
||||
|
||||
/* Cancel any grab that may be associated with shortcut
|
||||
inhibition. */
|
||||
XLReleaseShortcutInhibition (seat, seat->focus_surface);
|
||||
|
@ -2905,6 +2944,9 @@ SetFocusSurface (Seat *seat, Surface *focus)
|
|||
|
||||
SendKeyboardEnter (seat, focus);
|
||||
|
||||
/* Tell the surface it's now focused. */
|
||||
XLSurfaceNoteFocus (seat->focus_surface, SurfaceFocusIn);
|
||||
|
||||
XLPrimarySelectionHandleFocusChange (seat);
|
||||
|
||||
if (seat->data_device)
|
||||
|
@ -3807,7 +3849,7 @@ DispatchMotion (Subcompositor *subcompositor, XIDeviceEvent *xev)
|
|||
if (!seat)
|
||||
return;
|
||||
|
||||
if (InterceptResizeEvent (seat, xev))
|
||||
if (InterceptResizeEvent (seat, subcompositor, xev))
|
||||
return;
|
||||
|
||||
/* Move the drag-and-drop icon window. */
|
||||
|
@ -3834,8 +3876,7 @@ DispatchMotion (Subcompositor *subcompositor, XIDeviceEvent *xev)
|
|||
dispatch = seat->last_seen_surface;
|
||||
}
|
||||
else
|
||||
dispatch = FindSurfaceUnder (subcompositor, xev->event_x,
|
||||
xev->event_y);
|
||||
dispatch = actual_dispatch;
|
||||
|
||||
event_x = xev->event_x;
|
||||
event_y = xev->event_y;
|
||||
|
@ -4080,7 +4121,7 @@ DispatchButton (Subcompositor *subcompositor, XIDeviceEvent *xev)
|
|||
if (!seat)
|
||||
return;
|
||||
|
||||
if (InterceptResizeEvent (seat, xev))
|
||||
if (InterceptResizeEvent (seat, subcompositor, xev))
|
||||
return;
|
||||
|
||||
if (seat->flags & IsDragging)
|
||||
|
@ -4265,8 +4306,8 @@ DispatchBarrierHit (XIBarrierEvent *barrier)
|
|||
|
||||
/* Report a barrier hit event as relative motion. */
|
||||
|
||||
if (seat->focus_surface)
|
||||
SendRelativeMotion (seat, seat->focus_surface,
|
||||
if (seat->last_seen_surface)
|
||||
SendRelativeMotion (seat, seat->last_seen_surface,
|
||||
barrier->dx, barrier->dy,
|
||||
barrier->time);
|
||||
|
||||
|
@ -4538,9 +4579,14 @@ HandleKeyboardEdge (Seat *seat, Surface *target, uint32_t serial,
|
|||
seat->its_press_time);
|
||||
|
||||
/* Clear the grab immediately since it is no longer used. */
|
||||
|
||||
if (seat->grab_held)
|
||||
CancelGrabEarly (seat);
|
||||
|
||||
/* Send leave to any surface that the pointer is currently within.
|
||||
The position of the pointer is then restored upon entry. */
|
||||
EnteredSurface (seat, NULL, seat->its_press_time, 0, 0, False);
|
||||
|
||||
/* Send the message to the window manager. */
|
||||
XSendEvent (compositor.display,
|
||||
DefaultRootWindow (compositor.display),
|
||||
|
@ -4559,7 +4605,8 @@ HandleResizeUnmapped (void *data)
|
|||
Seat *seat;
|
||||
|
||||
seat = data;
|
||||
CancelResizeOperation (seat, seat->resize_time);
|
||||
CancelResizeOperation (seat, seat->resize_time,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static Bool
|
||||
|
@ -4626,13 +4673,15 @@ FakePointerEdge (Seat *seat, Surface *target, uint32_t serial,
|
|||
if (state != Success)
|
||||
return False;
|
||||
|
||||
/* On the other hand, cancel focus locking, since we will not be
|
||||
reporting motion events until the resize operation completes.
|
||||
/* On the other hand, cancel focus locking and leave the surface,
|
||||
since we will not be reporting motion events until the resize
|
||||
operation completes. */
|
||||
|
||||
Send leave events to any surface, since the pointer is
|
||||
(logically) no longer inside. */
|
||||
if (seat->grab_held)
|
||||
CancelGrabEarly (seat);
|
||||
{
|
||||
SwapUnlockSurface (seat, NULL);
|
||||
CancelGrabEarly (seat);
|
||||
}
|
||||
|
||||
/* Set the surface as the surface undergoing resize. */
|
||||
seat->resize_surface = target;
|
||||
|
|
15
surface.c
15
surface.c
|
@ -1741,6 +1741,21 @@ XLSurfaceSelectExtraEvents (Surface *surface, unsigned long event_mask)
|
|||
event_mask);
|
||||
}
|
||||
|
||||
/* This function doesn't provide the seat that has now been focused
|
||||
in. It is assumed that the role will perform some kind of
|
||||
reference counting in order to determine how many seats currently
|
||||
have it focused. */
|
||||
|
||||
void
|
||||
XLSurfaceNoteFocus (Surface *surface, FocusMode focus)
|
||||
{
|
||||
if (!surface->role || !surface->role->funcs.note_focus)
|
||||
return;
|
||||
|
||||
surface->role->funcs.note_focus (surface, surface->role,
|
||||
focus);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* The following functions convert from window to surface
|
||||
|
|
|
@ -1424,6 +1424,18 @@ SelectExtraEvents (Surface *surface, Role *role,
|
|||
DefaultEventMask | event_mask);
|
||||
}
|
||||
|
||||
static void
|
||||
NoteFocus (Surface *surface, Role *role, FocusMode focus)
|
||||
{
|
||||
XdgRole *xdg_role;
|
||||
|
||||
xdg_role = XdgRoleFromRole (role);
|
||||
|
||||
if (xdg_role->impl && xdg_role->impl->funcs.note_focus)
|
||||
xdg_role->impl->funcs.note_focus (role, xdg_role->impl,
|
||||
focus);
|
||||
}
|
||||
|
||||
void
|
||||
XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
|
||||
uint32_t id, struct wl_resource *surface_resource)
|
||||
|
@ -1509,6 +1521,7 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
|
|||
role->role.funcs.note_desync_child = NoteDesyncChild;
|
||||
role->role.funcs.note_child_synced = NoteChildSynced;
|
||||
role->role.funcs.select_extra_events = SelectExtraEvents;
|
||||
role->role.funcs.note_focus = NoteFocus;
|
||||
|
||||
attrs.colormap = compositor.colormap;
|
||||
attrs.border_pixel = border_pixel;
|
||||
|
|
|
@ -232,6 +232,9 @@ struct _XdgToplevel
|
|||
StatePendingConfigureSize is set. */
|
||||
int configure_width, configure_height;
|
||||
|
||||
/* The number of seats that currently have this surface focused. */
|
||||
int focus_seat_count;
|
||||
|
||||
/* Array of states. */
|
||||
struct wl_array states;
|
||||
|
||||
|
@ -571,27 +574,14 @@ WriteStates (XdgToplevel *toplevel)
|
|||
static void
|
||||
SendStates (XdgToplevel *toplevel)
|
||||
{
|
||||
int width, height;
|
||||
|
||||
WriteStates (toplevel);
|
||||
|
||||
/* Adjust the width and height we're sending by the window
|
||||
geometry. */
|
||||
if (toplevel->state & StateMissingState)
|
||||
XLXdgRoleGetCurrentGeometry (toplevel->role, NULL, NULL,
|
||||
&width, &height);
|
||||
else
|
||||
{
|
||||
/* toplevel->role->surface should not be NULL here. */
|
||||
TruncateScaleToSurface (toplevel->role->surface,
|
||||
toplevel->width, toplevel->height,
|
||||
&width, &height);
|
||||
|
||||
XLXdgRoleCalcNewWindowSize (toplevel->role, width,
|
||||
height, &width, &height);
|
||||
}
|
||||
|
||||
SendConfigure (toplevel, width, height);
|
||||
/* When SendStates is called, it means the width and height of the
|
||||
surface did not change. weston-terminal and some other clients
|
||||
that implement resize increments themselves will keep growing
|
||||
their toplevels if width and height are specified here, so simply
|
||||
send 0, 0 to make those clients decide their own size. */
|
||||
SendConfigure (toplevel, 0, 0);
|
||||
|
||||
/* Mark the state has having been calculated if some state
|
||||
transition has occured. */
|
||||
|
@ -2148,6 +2138,46 @@ ReplyToPing (XEvent *event)
|
|||
| SubstructureNotifyMask), ©);
|
||||
}
|
||||
|
||||
static void
|
||||
NoteFocus (Role *role, XdgRoleImplementation *impl, FocusMode mode)
|
||||
{
|
||||
XdgToplevel *toplevel;
|
||||
int old_focus;
|
||||
|
||||
toplevel = ToplevelFromRoleImpl (impl);
|
||||
old_focus = toplevel->focus_seat_count;
|
||||
|
||||
/* Increase or decrease the number of seats that currently have this
|
||||
surface under input focus. */
|
||||
switch (mode)
|
||||
{
|
||||
case SurfaceFocusIn:
|
||||
toplevel->focus_seat_count++;
|
||||
break;
|
||||
|
||||
case SurfaceFocusOut:
|
||||
toplevel->focus_seat_count
|
||||
= MAX (toplevel->focus_seat_count - 1, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now, change the toplevel state accordingly. */
|
||||
if (old_focus && !toplevel->focus_seat_count)
|
||||
{
|
||||
/* The surface should no longer be activated. */
|
||||
toplevel->toplevel_state.activated = False;
|
||||
WriteStates (toplevel);
|
||||
SendStates (toplevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The surface should now be activated. */
|
||||
toplevel->toplevel_state.activated = True;
|
||||
WriteStates (toplevel);
|
||||
SendStates (toplevel);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct xdg_toplevel_interface xdg_toplevel_impl =
|
||||
{
|
||||
.destroy = Destroy,
|
||||
|
@ -2210,6 +2240,11 @@ XLGetXdgToplevel (struct wl_client *client, struct wl_resource *resource,
|
|||
toplevel->impl.funcs.commit_inside_frame = CommitInsideFrame;
|
||||
toplevel->impl.funcs.is_window_mapped = IsWindowMapped;
|
||||
|
||||
if (!XLWmSupportsHint (_NET_WM_STATE_FOCUSED))
|
||||
/* If _NET_WM_STATE_FOCUSED is unsupported, fall back to utilizing
|
||||
focus in and focus out events to determine the focus state. */
|
||||
toplevel->impl.funcs.note_focus = NoteFocus;
|
||||
|
||||
/* Set up the sentinel node for the list of unmap callbacks. */
|
||||
toplevel->unmap_callbacks.next = &toplevel->unmap_callbacks;
|
||||
toplevel->unmap_callbacks.last = &toplevel->unmap_callbacks;
|
||||
|
|
Loading…
Add table
Reference in a new issue