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:
hujianwei 2022-10-20 06:56:03 +00:00
parent d38093f59e
commit b515414946
6 changed files with 161 additions and 42 deletions

View file

@ -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

View file

@ -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

85
seat.c
View file

@ -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)
{
SwapUnlockSurface (seat, NULL);
CancelGrabEarly (seat);
}
/* Set the surface as the surface undergoing resize. */
seat->resize_surface = target;

View file

@ -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

View file

@ -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;

View file

@ -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), &copy);
}
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;