cursor: refactor surfaceAt() to return parent
This allows us to properly handle e.g. clicking on a xdg popup of a view that is not currently focused by focusing that xdg popup's parent view.
This commit is contained in:
parent
f62eedb048
commit
7428519a93
192
river/Cursor.zig
192
river/Cursor.zig
@ -232,32 +232,28 @@ fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sx: f64 = undefined;
|
if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y)) |result| {
|
||||||
var sy: f64 = undefined;
|
switch (result.parent) {
|
||||||
if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |surface| {
|
.view => |view| {
|
||||||
// If the found surface is a keyboard inteactive layer surface,
|
// If a view has been clicked on, give that view keyboard focus and
|
||||||
// give it keyboard focus.
|
// perhaps enter move/resize mode.
|
||||||
if (surface.isLayerSurface()) {
|
if (event.state == .pressed and self.pressed_count == 1) {
|
||||||
const wlr_layer_surface = wlr.LayerSurfaceV1.fromWlrSurface(surface);
|
// If there is an active mapping for this button which is
|
||||||
if (wlr_layer_surface.current.keyboard_interactive == .exclusive) {
|
// handled we are done here
|
||||||
const layer_surface = @intToPtr(*LayerSurface, wlr_layer_surface.data);
|
if (self.handlePointerMapping(event, view)) return;
|
||||||
self.seat.focusOutput(layer_surface.output);
|
// Otherwise enter cursor down mode, giving keyboard focus
|
||||||
self.seat.setFocusRaw(.{ .layer = layer_surface });
|
self.enterMode(.down, view);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
.layer_surface => |layer_surface| {
|
||||||
|
// If a keyboard inteactive layer surface has been clicked on,
|
||||||
|
// give it keyboard focus.
|
||||||
|
if (layer_surface.wlr_layer_surface.current.keyboard_interactive == .exclusive) {
|
||||||
|
self.seat.focusOutput(layer_surface.output);
|
||||||
|
self.seat.setFocusRaw(.{ .layer = layer_surface });
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the target surface has a view, give that view keyboard focus and
|
|
||||||
// perhaps enter move/resize mode.
|
|
||||||
if (View.fromWlrSurface(surface)) |view| {
|
|
||||||
if (event.state == .pressed and self.pressed_count == 1) {
|
|
||||||
// If there is an active mapping for this button which is
|
|
||||||
// handled we are done here
|
|
||||||
if (self.handlePointerMapping(event, view)) return;
|
|
||||||
// Otherwise enter cursor down mode
|
|
||||||
self.enterMode(.down, view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
_ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,11 +417,21 @@ fn handleRequestSetCursor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SurfaceAtResult = struct {
|
||||||
|
surface: *wlr.Surface,
|
||||||
|
sx: f64,
|
||||||
|
sy: f64,
|
||||||
|
parent: union(enum) {
|
||||||
|
view: *View,
|
||||||
|
layer_surface: *LayerSurface,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/// Find the topmost surface under the output layout coordinates lx/ly
|
/// Find the topmost surface under the output layout coordinates lx/ly
|
||||||
/// returns the surface if found and sets the sx/sy parameters to the
|
/// returns the surface if found and sets the sx/sy parameters to the
|
||||||
/// surface coordinates.
|
/// surface coordinates.
|
||||||
/// This function must be kept in sync with the rendering order in render.zig.
|
/// This function must be kept in sync with the rendering order in render.zig.
|
||||||
fn surfaceAt(self: Self, lx: f64, ly: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
|
fn surfaceAt(self: Self, lx: f64, ly: f64) ?SurfaceAtResult {
|
||||||
const wlr_output = server.root.output_layout.outputAt(lx, ly) orelse return null;
|
const wlr_output = server.root.output_layout.outputAt(lx, ly) orelse return null;
|
||||||
const output = @intToPtr(*Output, wlr_output.data);
|
const output = @intToPtr(*Output, wlr_output.data);
|
||||||
|
|
||||||
@ -455,24 +461,33 @@ fn surfaceAt(self: Self, lx: f64, ly: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
|
|||||||
// 5. view toplevels and popups
|
// 5. view toplevels and popups
|
||||||
// 6. bottom, background layer toplevels
|
// 6. bottom, background layer toplevels
|
||||||
|
|
||||||
if (layerSurfaceAt(output.getLayer(.overlay).*, ox, oy, sx, sy)) |s| return s;
|
if (layerSurfaceAt(output.getLayer(.overlay).*, ox, oy)) |s| return s;
|
||||||
|
|
||||||
if (fullscreen_view) |view| {
|
if (fullscreen_view) |view| {
|
||||||
if (build_options.xwayland) if (xwaylandUnmanagedSurfaceAt(ly, lx, sx, sy)) |s| return s;
|
//if (build_options.xwayland) if (xwaylandUnmanagedSurfaceAt(ly, lx)) |s| return s;
|
||||||
if (view.surfaceAt(ox, oy, sx, sy)) |s| return s;
|
var sx: f64 = undefined;
|
||||||
|
var sy: f64 = undefined;
|
||||||
|
if (view.surfaceAt(ox, oy, &sx, &sy)) |found| {
|
||||||
|
return SurfaceAtResult{
|
||||||
|
.surface = found,
|
||||||
|
.sx = sx,
|
||||||
|
.sy = sy,
|
||||||
|
.parent = .{ .view = view },
|
||||||
|
};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for ([_]zwlr.LayerShellV1.Layer{ .top, .bottom, .background }) |layer| {
|
for ([_]zwlr.LayerShellV1.Layer{ .top, .bottom, .background }) |layer| {
|
||||||
if (layerPopupSurfaceAt(output.getLayer(layer).*, ox, oy, sx, sy)) |s| return s;
|
if (layerPopupSurfaceAt(output.getLayer(layer).*, ox, oy)) |s| return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layerSurfaceAt(output.getLayer(.top).*, ox, oy, sx, sy)) |s| return s;
|
if (layerSurfaceAt(output.getLayer(.top).*, ox, oy)) |s| return s;
|
||||||
|
|
||||||
if (build_options.xwayland) if (xwaylandUnmanagedSurfaceAt(lx, ly, sx, sy)) |s| return s;
|
//if (build_options.xwayland) if (xwaylandUnmanagedSurfaceAt(lx, ly)) |s| return s;
|
||||||
|
|
||||||
if (viewSurfaceAt(output, ox, oy, sx, sy)) |s| return s;
|
if (viewSurfaceAt(output, ox, oy)) |s| return s;
|
||||||
|
|
||||||
for ([_]zwlr.LayerShellV1.Layer{ .bottom, .background }) |layer| {
|
for ([_]zwlr.LayerShellV1.Layer{ .bottom, .background }) |layer| {
|
||||||
if (layerSurfaceAt(output.getLayer(layer).*, ox, oy, sx, sy)) |s| return s;
|
if (layerSurfaceAt(output.getLayer(layer).*, ox, oy)) |s| return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,69 +495,118 @@ fn surfaceAt(self: Self, lx: f64, ly: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find the topmost popup surface on the given layer at ox,oy.
|
/// Find the topmost popup surface on the given layer at ox,oy.
|
||||||
fn layerPopupSurfaceAt(layer: std.TailQueue(LayerSurface), ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
|
fn layerPopupSurfaceAt(layer: std.TailQueue(LayerSurface), ox: f64, oy: f64) ?SurfaceAtResult {
|
||||||
var it = layer.first;
|
var it = layer.first;
|
||||||
while (it) |node| : (it = node.next) {
|
while (it) |node| : (it = node.next) {
|
||||||
const layer_surface = &node.data;
|
const layer_surface = &node.data;
|
||||||
|
var sx: f64 = undefined;
|
||||||
|
var sy: f64 = undefined;
|
||||||
if (layer_surface.wlr_layer_surface.popupSurfaceAt(
|
if (layer_surface.wlr_layer_surface.popupSurfaceAt(
|
||||||
ox - @intToFloat(f64, layer_surface.box.x),
|
ox - @intToFloat(f64, layer_surface.box.x),
|
||||||
oy - @intToFloat(f64, layer_surface.box.y),
|
oy - @intToFloat(f64, layer_surface.box.y),
|
||||||
sx,
|
&sx,
|
||||||
sy,
|
&sy,
|
||||||
)) |found| return found;
|
)) |found| {
|
||||||
|
return SurfaceAtResult{
|
||||||
|
.surface = found,
|
||||||
|
.sx = sx,
|
||||||
|
.sy = sy,
|
||||||
|
.parent = .{ .layer_surface = layer_surface },
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the topmost surface (or popup surface) on the given layer at ox,oy.
|
/// Find the topmost surface (or popup surface) on the given layer at ox,oy.
|
||||||
fn layerSurfaceAt(layer: std.TailQueue(LayerSurface), ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
|
fn layerSurfaceAt(layer: std.TailQueue(LayerSurface), ox: f64, oy: f64) ?SurfaceAtResult {
|
||||||
var it = layer.first;
|
var it = layer.first;
|
||||||
while (it) |node| : (it = node.next) {
|
while (it) |node| : (it = node.next) {
|
||||||
const layer_surface = &node.data;
|
const layer_surface = &node.data;
|
||||||
|
var sx: f64 = undefined;
|
||||||
|
var sy: f64 = undefined;
|
||||||
if (layer_surface.wlr_layer_surface.surfaceAt(
|
if (layer_surface.wlr_layer_surface.surfaceAt(
|
||||||
ox - @intToFloat(f64, layer_surface.box.x),
|
ox - @intToFloat(f64, layer_surface.box.x),
|
||||||
oy - @intToFloat(f64, layer_surface.box.y),
|
oy - @intToFloat(f64, layer_surface.box.y),
|
||||||
sx,
|
&sx,
|
||||||
sy,
|
&sy,
|
||||||
)) |found| return found;
|
)) |found| {
|
||||||
|
return SurfaceAtResult{
|
||||||
|
.surface = found,
|
||||||
|
.sx = sx,
|
||||||
|
.sy = sy,
|
||||||
|
.parent = .{ .layer_surface = layer_surface },
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the topmost visible view surface (incl. popups) at ox,oy.
|
/// Find the topmost visible view surface (incl. popups) at ox,oy.
|
||||||
fn viewSurfaceAt(output: *const Output, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
|
fn viewSurfaceAt(output: *const Output, ox: f64, oy: f64) ?SurfaceAtResult {
|
||||||
|
var sx: f64 = undefined;
|
||||||
|
var sy: f64 = undefined;
|
||||||
|
|
||||||
// focused, floating views
|
// focused, floating views
|
||||||
var it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
|
var it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
|
||||||
while (it.next()) |view| {
|
while (it.next()) |view| {
|
||||||
if (view.current.focus == 0 or !view.current.float) continue;
|
if (view.current.focus == 0 or !view.current.float) continue;
|
||||||
if (view.surfaceAt(ox, oy, sx, sy)) |found| return found;
|
if (view.surfaceAt(ox, oy, &sx, &sy)) |found| {
|
||||||
|
return SurfaceAtResult{
|
||||||
|
.surface = found,
|
||||||
|
.sx = sx,
|
||||||
|
.sy = sy,
|
||||||
|
.parent = .{ .view = view },
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-focused, floating views
|
// non-focused, floating views
|
||||||
it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
|
it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
|
||||||
while (it.next()) |view| {
|
while (it.next()) |view| {
|
||||||
if (view.current.focus != 0 or !view.current.float) continue;
|
if (view.current.focus != 0 or !view.current.float) continue;
|
||||||
if (view.surfaceAt(ox, oy, sx, sy)) |found| return found;
|
if (view.surfaceAt(ox, oy, &sx, &sy)) |found| {
|
||||||
|
return SurfaceAtResult{
|
||||||
|
.surface = found,
|
||||||
|
.sx = sx,
|
||||||
|
.sy = sy,
|
||||||
|
.parent = .{ .view = view },
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// focused, non-floating views
|
// focused, non-floating views
|
||||||
it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
|
it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
|
||||||
while (it.next()) |view| {
|
while (it.next()) |view| {
|
||||||
if (view.current.focus == 0 or view.current.float) continue;
|
if (view.current.focus == 0 or view.current.float) continue;
|
||||||
if (view.surfaceAt(ox, oy, sx, sy)) |found| return found;
|
if (view.surfaceAt(ox, oy, &sx, &sy)) |found| {
|
||||||
|
return SurfaceAtResult{
|
||||||
|
.surface = found,
|
||||||
|
.sx = sx,
|
||||||
|
.sy = sy,
|
||||||
|
.parent = .{ .view = view },
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-focused, non-floating views
|
// non-focused, non-floating views
|
||||||
it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
|
it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
|
||||||
while (it.next()) |view| {
|
while (it.next()) |view| {
|
||||||
if (view.current.focus != 0 or view.current.float) continue;
|
if (view.current.focus != 0 or view.current.float) continue;
|
||||||
if (view.surfaceAt(ox, oy, sx, sy)) |found| return found;
|
if (view.surfaceAt(ox, oy, &sx, &sy)) |found| {
|
||||||
|
return SurfaceAtResult{
|
||||||
|
.surface = found,
|
||||||
|
.sx = sx,
|
||||||
|
.sy = sy,
|
||||||
|
.parent = .{ .view = view },
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xwaylandUnmanagedSurfaceAt(lx: f64, ly: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
|
fn xwaylandUnmanagedSurfaceAt(lx: f64, ly: f64) ?*wlr.Surface {
|
||||||
var it = server.root.xwayland_unmanaged_views.first;
|
var it = server.root.xwayland_unmanaged_views.first;
|
||||||
while (it) |node| : (it = node.next) {
|
while (it) |node| : (it = node.next) {
|
||||||
const xwayland_surface = node.data.xwayland_surface;
|
const xwayland_surface = node.data.xwayland_surface;
|
||||||
@ -560,7 +624,6 @@ fn surfaceAtFilter(view: *View, filter_tags: u32) bool {
|
|||||||
return !view.destroying and view.current.tags & filter_tags != 0;
|
return !view.destroying and view.current.tags & filter_tags != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enter move or resize mode
|
|
||||||
pub fn enterMode(self: *Self, mode: std.meta.Tag((Mode)), view: *View) void {
|
pub fn enterMode(self: *Self, mode: std.meta.Tag((Mode)), view: *View) void {
|
||||||
log.debug("enter {s} cursor mode", .{@tagName(mode)});
|
log.debug("enter {s} cursor mode", .{@tagName(mode)});
|
||||||
|
|
||||||
@ -645,12 +708,12 @@ fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64,
|
|||||||
if (self.mode == .passthrough or self.mode == .down) {
|
if (self.mode == .passthrough or self.mode == .down) {
|
||||||
if (constraint.type == .locked) return;
|
if (constraint.type == .locked) return;
|
||||||
|
|
||||||
var sx: f64 = undefined;
|
const result = self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y) orelse return;
|
||||||
var sy: f64 = undefined;
|
|
||||||
const surface = self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy);
|
|
||||||
|
|
||||||
if (surface != constraint.surface) return;
|
if (result.surface != constraint.surface) return;
|
||||||
|
|
||||||
|
const sx = result.sx;
|
||||||
|
const sy = result.sy;
|
||||||
var sx_con: f64 = undefined;
|
var sx_con: f64 = undefined;
|
||||||
var sy_con: f64 = undefined;
|
var sy_con: f64 = undefined;
|
||||||
if (!wlr.region.confine(&constraint.region, sx, sy, sx + dx, sy + dy, &sx_con, &sy_con)) {
|
if (!wlr.region.confine(&constraint.region, sx, sy, sx + dx, sy + dy, &sx_con, &sy_con)) {
|
||||||
@ -749,25 +812,26 @@ pub fn maybeResetState(self: *Self) void {
|
|||||||
fn passthrough(self: *Self, time: u32) void {
|
fn passthrough(self: *Self, time: u32) void {
|
||||||
assert(self.mode == .passthrough);
|
assert(self.mode == .passthrough);
|
||||||
|
|
||||||
var sx: f64 = undefined;
|
if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y)) |result| {
|
||||||
var sy: f64 = undefined;
|
|
||||||
if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |surface| {
|
|
||||||
// If input is allowed on the surface, send pointer enter and motion
|
// If input is allowed on the surface, send pointer enter and motion
|
||||||
// events. Note that wlroots won't actually send an enter event if
|
// events. Note that wlroots won't actually send an enter event if
|
||||||
// the surface has already been entered.
|
// the surface has already been entered.
|
||||||
if (server.input_manager.inputAllowed(surface)) {
|
if (server.input_manager.inputAllowed(result.surface)) {
|
||||||
// The focus change must be checked before sending enter events
|
// The focus change must be checked before sending enter events
|
||||||
const focus_change = self.seat.wlr_seat.pointer_state.focused_surface != surface;
|
const focus_change = self.seat.wlr_seat.pointer_state.focused_surface != result.surface;
|
||||||
|
|
||||||
self.seat.wlr_seat.pointerNotifyEnter(surface, sx, sy);
|
self.seat.wlr_seat.pointerNotifyEnter(result.surface, result.sx, result.sy);
|
||||||
self.seat.wlr_seat.pointerNotifyMotion(time, sx, sy);
|
self.seat.wlr_seat.pointerNotifyMotion(time, result.sx, result.sy);
|
||||||
|
|
||||||
const follow_mode = server.config.focus_follows_cursor;
|
const follow_mode = server.config.focus_follows_cursor;
|
||||||
if (follow_mode == .strict or (follow_mode == .normal and focus_change)) {
|
if (follow_mode == .strict or (follow_mode == .normal and focus_change)) {
|
||||||
if (View.fromWlrSurface(surface)) |view| {
|
switch (result.parent) {
|
||||||
self.seat.focusOutput(view.output);
|
.view => |view| {
|
||||||
self.seat.focus(view);
|
self.seat.focusOutput(view.output);
|
||||||
server.root.startTransaction();
|
self.seat.focus(view);
|
||||||
|
server.root.startTransaction();
|
||||||
|
},
|
||||||
|
.layer_surface => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user