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 Using this protocol translator under a window manager that does not at
least support the least support the
.B _NET_WM_SYNC_REQUEST .B _NET_WM_SYNC_REQUEST
and window manager protocol will result in Wayland programs running badly.
.B _NET_WM_STATE
window manager hints will result in Wayland programs running
incorrectly.
.PP .PP
In addition, surfaces transforms are not supported nor reported. The In addition, surfaces transforms are not supported nor reported. The
vast majority of clients seem not to make use of this feature, and 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 _State State;
typedef struct _FrameCallback FrameCallback; typedef struct _FrameCallback FrameCallback;
typedef enum _RoleType RoleType; typedef enum _RoleType RoleType;
typedef enum _FocusMode FocusMode;
enum _FocusMode
{
SurfaceFocusIn,
SurfaceFocusOut,
};
enum _RoleType enum _RoleType
{ {
@ -1067,6 +1074,7 @@ struct _RoleFuncs
void (*note_desync_child) (Surface *, Role *); void (*note_desync_child) (Surface *, Role *);
void (*note_child_synced) (Surface *, Role *); void (*note_child_synced) (Surface *, Role *);
void (*select_extra_events) (Surface *, Role *, unsigned long); void (*select_extra_events) (Surface *, Role *, unsigned long);
void (*note_focus) (Surface *, Role *, FocusMode);
}; };
struct _Role struct _Role
@ -1116,6 +1124,7 @@ extern void XLSurfaceMoveBy (Surface *, int, int);
extern Window XLWindowFromSurface (Surface *); extern Window XLWindowFromSurface (Surface *);
extern void XLUpdateSurfaceOutputs (Surface *, int, int, int, int); extern void XLUpdateSurfaceOutputs (Surface *, int, int, int, int);
extern void XLSurfaceSelectExtraEvents (Surface *, unsigned long); extern void XLSurfaceSelectExtraEvents (Surface *, unsigned long);
extern void XLSurfaceNoteFocus (Surface *, FocusMode);
extern void SurfaceToWindow (Surface *, double, double, double *, double *); extern void SurfaceToWindow (Surface *, double, double, double *, double *);
extern void ScaleToWindow (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 (*post_resize) (Role *, XdgRoleImplementation *, int, int, int, int);
void (*commit_inside_frame) (Role *, XdgRoleImplementation *); void (*commit_inside_frame) (Role *, XdgRoleImplementation *);
Bool (*is_window_mapped) (Role *, XdgRoleImplementation *); Bool (*is_window_mapped) (Role *, XdgRoleImplementation *);
void (*note_focus) (Role *, XdgRoleImplementation *, FocusMode);
}; };
struct _XdgRoleImplementation struct _XdgRoleImplementation

87
seat.c
View file

@ -1996,9 +1996,18 @@ RunResizeDoneCallbacks (Seat *seat)
seat->resize_callbacks.last = &seat->resize_callbacks; 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 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. */ /* Stop the resize operation. */
XLSurfaceCancelUnmapCallback (seat->resize_surface_callback); XLSurfaceCancelUnmapCallback (seat->resize_surface_callback);
seat->resize_surface = NULL; seat->resize_surface = NULL;
@ -2008,11 +2017,30 @@ CancelResizeOperation (Seat *seat, Time time)
/* Ungrab the pointer. */ /* Ungrab the pointer. */
XIUngrabDevice (compositor.display, seat->master_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 static Bool
InterceptButtonEventForResize (Seat *seat, XIDeviceEvent *xev) InterceptButtonEventForResize (Seat *seat, Subcompositor *subcompositor,
XIDeviceEvent *xev)
{ {
if (xev->type == XI_ButtonPress) if (xev->type == XI_ButtonPress)
return True; return True;
@ -2020,7 +2048,7 @@ InterceptButtonEventForResize (Seat *seat, XIDeviceEvent *xev)
/* If the button starting the resize has been released, cancel the /* If the button starting the resize has been released, cancel the
resize operation. */ resize operation. */
if (xev->detail == seat->resize_button) if (xev->detail == seat->resize_button)
CancelResizeOperation (seat, xev->time); CancelResizeOperation (seat, xev->time, subcompositor, xev);
return True; return True;
} }
@ -2114,7 +2142,8 @@ InterceptMotionEventForResize (Seat *seat, XIDeviceEvent *xev)
} }
static Bool static Bool
InterceptResizeEvent (Seat *seat, XIDeviceEvent *xev) InterceptResizeEvent (Seat *seat, Subcompositor *subcompositor,
XIDeviceEvent *xev)
{ {
if (!seat->resize_surface) if (!seat->resize_surface)
return False; return False;
@ -2122,7 +2151,7 @@ InterceptResizeEvent (Seat *seat, XIDeviceEvent *xev)
switch (xev->evtype) switch (xev->evtype)
{ {
case XI_ButtonRelease: case XI_ButtonRelease:
return InterceptButtonEventForResize (seat, xev); return InterceptButtonEventForResize (seat, subcompositor, xev);
case XI_Motion: case XI_Motion:
return InterceptMotionEventForResize (seat, xev); return InterceptMotionEventForResize (seat, xev);
@ -2145,6 +2174,9 @@ RunDestroyListeners (Seat *seat)
} }
} }
/* Forward declaration. */
static void SetFocusSurface (Seat *, Surface *);
static void static void
NoticeDeviceDisabled (int deviceid) NoticeDeviceDisabled (int deviceid)
{ {
@ -2174,6 +2206,11 @@ NoticeDeviceDisabled (int deviceid)
seat->flags |= IsInert; 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. */ /* Run destroy handlers. */
RunDestroyListeners (seat); RunDestroyListeners (seat);
@ -2455,7 +2492,6 @@ HandleResizeComplete (Seat *seat)
/* Forward declarations. */ /* Forward declarations. */
static int GetXButton (int); static int GetXButton (int);
static void TransformToSurface (Surface *, double, double, double *, double *);
static void SendButton (Seat *, Surface *, Time, uint32_t, uint32_t, static void SendButton (Seat *, Surface *, Time, uint32_t, uint32_t,
double, double); double, double);
@ -2871,6 +2907,9 @@ SetFocusSurface (Seat *seat, Surface *focus)
{ {
SendKeyboardLeave (seat, seat->focus_surface); 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 /* Cancel any grab that may be associated with shortcut
inhibition. */ inhibition. */
XLReleaseShortcutInhibition (seat, seat->focus_surface); XLReleaseShortcutInhibition (seat, seat->focus_surface);
@ -2905,6 +2944,9 @@ SetFocusSurface (Seat *seat, Surface *focus)
SendKeyboardEnter (seat, focus); SendKeyboardEnter (seat, focus);
/* Tell the surface it's now focused. */
XLSurfaceNoteFocus (seat->focus_surface, SurfaceFocusIn);
XLPrimarySelectionHandleFocusChange (seat); XLPrimarySelectionHandleFocusChange (seat);
if (seat->data_device) if (seat->data_device)
@ -3807,7 +3849,7 @@ DispatchMotion (Subcompositor *subcompositor, XIDeviceEvent *xev)
if (!seat) if (!seat)
return; return;
if (InterceptResizeEvent (seat, xev)) if (InterceptResizeEvent (seat, subcompositor, xev))
return; return;
/* Move the drag-and-drop icon window. */ /* Move the drag-and-drop icon window. */
@ -3834,8 +3876,7 @@ DispatchMotion (Subcompositor *subcompositor, XIDeviceEvent *xev)
dispatch = seat->last_seen_surface; dispatch = seat->last_seen_surface;
} }
else else
dispatch = FindSurfaceUnder (subcompositor, xev->event_x, dispatch = actual_dispatch;
xev->event_y);
event_x = xev->event_x; event_x = xev->event_x;
event_y = xev->event_y; event_y = xev->event_y;
@ -4080,7 +4121,7 @@ DispatchButton (Subcompositor *subcompositor, XIDeviceEvent *xev)
if (!seat) if (!seat)
return; return;
if (InterceptResizeEvent (seat, xev)) if (InterceptResizeEvent (seat, subcompositor, xev))
return; return;
if (seat->flags & IsDragging) if (seat->flags & IsDragging)
@ -4265,8 +4306,8 @@ DispatchBarrierHit (XIBarrierEvent *barrier)
/* Report a barrier hit event as relative motion. */ /* Report a barrier hit event as relative motion. */
if (seat->focus_surface) if (seat->last_seen_surface)
SendRelativeMotion (seat, seat->focus_surface, SendRelativeMotion (seat, seat->last_seen_surface,
barrier->dx, barrier->dy, barrier->dx, barrier->dy,
barrier->time); barrier->time);
@ -4538,9 +4579,14 @@ HandleKeyboardEdge (Seat *seat, Surface *target, uint32_t serial,
seat->its_press_time); seat->its_press_time);
/* Clear the grab immediately since it is no longer used. */ /* Clear the grab immediately since it is no longer used. */
if (seat->grab_held) if (seat->grab_held)
CancelGrabEarly (seat); 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. */ /* Send the message to the window manager. */
XSendEvent (compositor.display, XSendEvent (compositor.display,
DefaultRootWindow (compositor.display), DefaultRootWindow (compositor.display),
@ -4559,7 +4605,8 @@ HandleResizeUnmapped (void *data)
Seat *seat; Seat *seat;
seat = data; seat = data;
CancelResizeOperation (seat, seat->resize_time); CancelResizeOperation (seat, seat->resize_time,
NULL, NULL);
} }
static Bool static Bool
@ -4626,13 +4673,15 @@ FakePointerEdge (Seat *seat, Surface *target, uint32_t serial,
if (state != Success) if (state != Success)
return False; return False;
/* On the other hand, cancel focus locking, since we will not be /* On the other hand, cancel focus locking and leave the surface,
reporting motion events until the resize operation completes. 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) if (seat->grab_held)
CancelGrabEarly (seat); {
SwapUnlockSurface (seat, NULL);
CancelGrabEarly (seat);
}
/* Set the surface as the surface undergoing resize. */ /* Set the surface as the surface undergoing resize. */
seat->resize_surface = target; seat->resize_surface = target;

View file

@ -1741,6 +1741,21 @@ XLSurfaceSelectExtraEvents (Surface *surface, unsigned long event_mask)
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 /* The following functions convert from window to surface

View file

@ -1424,6 +1424,18 @@ SelectExtraEvents (Surface *surface, Role *role,
DefaultEventMask | event_mask); 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 void
XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource, XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
uint32_t id, struct wl_resource *surface_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_desync_child = NoteDesyncChild;
role->role.funcs.note_child_synced = NoteChildSynced; role->role.funcs.note_child_synced = NoteChildSynced;
role->role.funcs.select_extra_events = SelectExtraEvents; role->role.funcs.select_extra_events = SelectExtraEvents;
role->role.funcs.note_focus = NoteFocus;
attrs.colormap = compositor.colormap; attrs.colormap = compositor.colormap;
attrs.border_pixel = border_pixel; attrs.border_pixel = border_pixel;

View file

@ -232,6 +232,9 @@ struct _XdgToplevel
StatePendingConfigureSize is set. */ StatePendingConfigureSize is set. */
int configure_width, configure_height; int configure_width, configure_height;
/* The number of seats that currently have this surface focused. */
int focus_seat_count;
/* Array of states. */ /* Array of states. */
struct wl_array states; struct wl_array states;
@ -571,27 +574,14 @@ WriteStates (XdgToplevel *toplevel)
static void static void
SendStates (XdgToplevel *toplevel) SendStates (XdgToplevel *toplevel)
{ {
int width, height;
WriteStates (toplevel); WriteStates (toplevel);
/* Adjust the width and height we're sending by the window /* When SendStates is called, it means the width and height of the
geometry. */ surface did not change. weston-terminal and some other clients
if (toplevel->state & StateMissingState) that implement resize increments themselves will keep growing
XLXdgRoleGetCurrentGeometry (toplevel->role, NULL, NULL, their toplevels if width and height are specified here, so simply
&width, &height); send 0, 0 to make those clients decide their own size. */
else SendConfigure (toplevel, 0, 0);
{
/* 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);
/* Mark the state has having been calculated if some state /* Mark the state has having been calculated if some state
transition has occured. */ transition has occured. */
@ -2148,6 +2138,46 @@ ReplyToPing (XEvent *event)
| SubstructureNotifyMask), &copy); | 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 = static const struct xdg_toplevel_interface xdg_toplevel_impl =
{ {
.destroy = Destroy, .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.commit_inside_frame = CommitInsideFrame;
toplevel->impl.funcs.is_window_mapped = IsWindowMapped; 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. */ /* Set up the sentinel node for the list of unmap callbacks. */
toplevel->unmap_callbacks.next = &toplevel->unmap_callbacks; toplevel->unmap_callbacks.next = &toplevel->unmap_callbacks;
toplevel->unmap_callbacks.last = &toplevel->unmap_callbacks; toplevel->unmap_callbacks.last = &toplevel->unmap_callbacks;