Start to handle layer surfaces in cursor code
This commit is contained in:
parent
56400a5955
commit
822976e3a5
287
src/cursor.zig
287
src/cursor.zig
@ -1,8 +1,11 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const c = @import("c.zig");
|
const c = @import("c.zig");
|
||||||
|
|
||||||
|
const Output = @import("output.zig").Output;
|
||||||
|
const LayerSurface = @import("layer_surface.zig").LayerSurface;
|
||||||
const Seat = @import("seat.zig").Seat;
|
const Seat = @import("seat.zig").Seat;
|
||||||
const View = @import("view.zig").View;
|
const View = @import("view.zig").View;
|
||||||
|
const ViewStack = @import("view_stack.zig").ViewStack;
|
||||||
|
|
||||||
const CursorMode = enum {
|
const CursorMode = enum {
|
||||||
Passthrough,
|
Passthrough,
|
||||||
@ -119,34 +122,41 @@ pub const Cursor = struct {
|
|||||||
*c.wlr_event_pointer_button,
|
*c.wlr_event_pointer_button,
|
||||||
@alignCast(@alignOf(*c.wlr_event_pointer_button), data),
|
@alignCast(@alignOf(*c.wlr_event_pointer_button), data),
|
||||||
);
|
);
|
||||||
// Notify the client with pointer focus that a button press has occurred
|
var sx: f64 = undefined;
|
||||||
|
var sy: f64 = undefined;
|
||||||
|
|
||||||
|
if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |wlr_surface| {
|
||||||
|
// If the found surface is a keyboard inteactive layer surface,
|
||||||
|
// focus it.
|
||||||
|
if (c.wlr_surface_is_layer_surface(wlr_surface)) {
|
||||||
|
const wlr_layer_surface = c.wlr_layer_surface_v1_from_wlr_surface(wlr_surface);
|
||||||
|
if (wlr_layer_surface.*.current.keyboard_interactive) {
|
||||||
|
const layer_surface = @ptrCast(
|
||||||
|
*LayerSurface,
|
||||||
|
@alignCast(@alignOf(*LayerSurface), wlr_layer_surface.*.data),
|
||||||
|
);
|
||||||
|
self.seat.setFocusRaw(.{ .layer = layer_surface });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the found surface is an xdg toplevel surface, send focus to
|
||||||
|
// the view.
|
||||||
|
if (c.wlr_surface_is_xdg_surface(wlr_surface)) {
|
||||||
|
const wlr_xdg_surface = c.wlr_xdg_surface_from_wlr_surface(wlr_surface);
|
||||||
|
if (wlr_xdg_surface.*.role ==
|
||||||
|
c.enum_wlr_xdg_surface_role.WLR_XDG_SURFACE_ROLE_TOPLEVEL)
|
||||||
|
{
|
||||||
|
const view = @ptrCast(*View, @alignCast(@alignOf(*View), wlr_xdg_surface.*.data));
|
||||||
|
self.seat.focus(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ = c.wlr_seat_pointer_notify_button(
|
_ = c.wlr_seat_pointer_notify_button(
|
||||||
self.seat.wlr_seat,
|
self.seat.wlr_seat,
|
||||||
event.time_msec,
|
event.time_msec,
|
||||||
event.button,
|
event.button,
|
||||||
event.state,
|
event.state,
|
||||||
);
|
);
|
||||||
|
|
||||||
var sx: f64 = undefined;
|
|
||||||
var sy: f64 = undefined;
|
|
||||||
|
|
||||||
var surface: ?*c.wlr_surface = null;
|
|
||||||
const view = self.seat.input_manager.server.root.viewAt(
|
|
||||||
self.wlr_cursor.x,
|
|
||||||
self.wlr_cursor.y,
|
|
||||||
&surface,
|
|
||||||
&sx,
|
|
||||||
&sy,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (event.state == c.enum_wlr_button_state.WLR_BUTTON_RELEASED) {
|
|
||||||
// If you released any buttons, we exit interactive move/resize mode.
|
|
||||||
self.mode = CursorMode.Passthrough;
|
|
||||||
} else {
|
|
||||||
// Focus that client if the button was _pressed_
|
|
||||||
if (view) |v| {
|
|
||||||
self.seat.focus(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,15 +170,6 @@ pub const Cursor = struct {
|
|||||||
c.wlr_seat_pointer_notify_frame(self.seat.wlr_seat);
|
c.wlr_seat_pointer_notify_frame(self.seat.wlr_seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn processMove(self: Self, time: u32) void {
|
|
||||||
// Move the grabbed view to the new position.
|
|
||||||
// TODO: log on null
|
|
||||||
if (self.grabbed_view) |view| {
|
|
||||||
view.current_box.x = @floatToInt(c_int, self.wlr_cursor.x - self.grab_x);
|
|
||||||
view.current_box.y = @floatToInt(c_int, self.wlr_cursor.y - self.grab_y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handleMotionAbsolute(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
fn handleMotionAbsolute(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
// This event is forwarded by the cursor when a pointer emits an _absolute_
|
// This event is forwarded by the cursor when a pointer emits an _absolute_
|
||||||
// motion event, from 0..1 on each axis. This happens, for example, when
|
// motion event, from 0..1 on each axis. This happens, for example, when
|
||||||
@ -227,107 +228,159 @@ pub const Cursor = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn processsResize(self: Self, time: u32) void {
|
|
||||||
// Resizing the grabbed view can be a little bit complicated, because we
|
|
||||||
// could be resizing from any corner or edge. This not only resizes the view
|
|
||||||
// on one or two axes, but can also move the view if you resize from the top
|
|
||||||
// or left edges (or top-left corner).
|
|
||||||
//
|
|
||||||
// Note that I took some shortcuts here. In a more fleshed-out compositor,
|
|
||||||
// you'd wait for the client to prepare a buffer at the new size, then
|
|
||||||
// commit any movement that was prepared.
|
|
||||||
|
|
||||||
// TODO: Handle null view
|
|
||||||
const view = self.grabbed_view.?;
|
|
||||||
|
|
||||||
const dx: f64 = self.wlr_cursor.x - self.grab_x;
|
|
||||||
const dy: f64 = self.wlr_cursor.y - self.grab_y;
|
|
||||||
|
|
||||||
var x: f64 = @intToFloat(f64, view.current_box.x);
|
|
||||||
var y: f64 = @intToFloat(f64, view.current_box.y);
|
|
||||||
|
|
||||||
var width = @intToFloat(f64, self.grab_width);
|
|
||||||
var height = @intToFloat(f64, self.grab_height);
|
|
||||||
|
|
||||||
if (self.resize_edges & @intCast(u32, c.WLR_EDGE_TOP) != 0) {
|
|
||||||
y = self.grab_y + dy;
|
|
||||||
height -= dy;
|
|
||||||
if (height < 1) {
|
|
||||||
y += height;
|
|
||||||
}
|
|
||||||
} else if (self.resize_edges & @intCast(u32, c.WLR_EDGE_BOTTOM) != 0) {
|
|
||||||
height += dy;
|
|
||||||
}
|
|
||||||
if (self.resize_edges & @intCast(u32, c.WLR_EDGE_LEFT) != 0) {
|
|
||||||
x = self.grab_x + dx;
|
|
||||||
width -= dx;
|
|
||||||
if (width < 1) {
|
|
||||||
x += width;
|
|
||||||
}
|
|
||||||
} else if (self.resize_edges & @intCast(u32, c.WLR_EDGE_RIGHT) != 0) {
|
|
||||||
width += dx;
|
|
||||||
}
|
|
||||||
view.current_box.x = @floatToInt(c_int, x);
|
|
||||||
view.current_box.y = @floatToInt(c_int, y);
|
|
||||||
_ = c.wlr_xdg_toplevel_set_size(
|
|
||||||
view.wlr_xdg_surface,
|
|
||||||
@floatToInt(u32, width),
|
|
||||||
@floatToInt(u32, height),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn processMotion(self: Self, time: u32) void {
|
fn processMotion(self: Self, time: u32) void {
|
||||||
// If the mode is non-passthrough, delegate to those functions.
|
|
||||||
if (self.mode == CursorMode.Move) {
|
|
||||||
self.processMove(time);
|
|
||||||
return;
|
|
||||||
} else if (self.mode == CursorMode.Resize) {
|
|
||||||
self.processsResize(time);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, find the view under the pointer and send the event along.
|
|
||||||
var sx: f64 = undefined;
|
var sx: f64 = undefined;
|
||||||
var sy: f64 = undefined;
|
var sy: f64 = undefined;
|
||||||
var opt_surface: ?*c.wlr_surface = null;
|
if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |wlr_surface| {
|
||||||
const view = self.seat.input_manager.server.root.viewAt(
|
|
||||||
self.wlr_cursor.x,
|
|
||||||
self.wlr_cursor.y,
|
|
||||||
&opt_surface,
|
|
||||||
&sx,
|
|
||||||
&sy,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (view == null) {
|
|
||||||
// If there's no view under the cursor, set the cursor image to a
|
|
||||||
// default. This is what makes the cursor image appear when you move it
|
|
||||||
// around the screen, not over any views.
|
|
||||||
c.wlr_xcursor_manager_set_cursor_image(
|
|
||||||
self.wlr_xcursor_manager,
|
|
||||||
"left_ptr",
|
|
||||||
self.wlr_cursor,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const wlr_seat = self.seat.wlr_seat;
|
|
||||||
if (opt_surface) |surface| {
|
|
||||||
const focus_changed = wlr_seat.pointer_state.focused_surface != surface;
|
|
||||||
// "Enter" the surface if necessary. This lets the client know that the
|
// "Enter" the surface if necessary. This lets the client know that the
|
||||||
// cursor has entered one of its surfaces.
|
// cursor has entered one of its surfaces.
|
||||||
//
|
//
|
||||||
// Note that this gives the surface "pointer focus", which is distinct
|
// Note that this gives the surface "pointer focus", which is distinct
|
||||||
// from keyboard focus. You get pointer focus by moving the pointer over
|
// from keyboard focus. You get pointer focus by moving the pointer over
|
||||||
// a window.
|
// a window.
|
||||||
c.wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
|
if (self.seat.input_manager.inputAllowed(wlr_surface)) {
|
||||||
|
const wlr_seat = self.seat.wlr_seat;
|
||||||
|
const focus_changed = wlr_seat.pointer_state.focused_surface != wlr_surface;
|
||||||
|
c.wlr_seat_pointer_notify_enter(wlr_seat, wlr_surface, sx, sy);
|
||||||
if (!focus_changed) {
|
if (!focus_changed) {
|
||||||
// The enter event contains coordinates, so we only need to notify
|
// The enter event contains coordinates, so we only need to notify
|
||||||
//on motion if the focus did not change.
|
// on motion if the focus did not change.
|
||||||
c.wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
|
c.wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is either no surface under the cursor or input is disallowed
|
||||||
|
// Reset the cursor image to the default
|
||||||
|
c.wlr_xcursor_manager_set_cursor_image(
|
||||||
|
self.wlr_xcursor_manager,
|
||||||
|
"left_ptr",
|
||||||
|
self.wlr_cursor,
|
||||||
|
);
|
||||||
// Clear pointer focus so future button events and such are not sent to
|
// Clear pointer focus so future button events and such are not sent to
|
||||||
// the last client to have the cursor over it.
|
// the last client to have the cursor over it.
|
||||||
c.wlr_seat_pointer_clear_focus(wlr_seat);
|
c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the topmost surface under the output layout coordinates lx/ly
|
||||||
|
/// returns the surface if found and sets the sx/sy parametes to the
|
||||||
|
/// surface coordinates.
|
||||||
|
fn surfaceAt(self: Self, lx: f64, ly: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
|
||||||
|
// Find the output to check
|
||||||
|
const root = self.seat.input_manager.server.root;
|
||||||
|
const wlr_output = c.wlr_output_layout_output_at(root.wlr_output_layout, lx, ly) orelse
|
||||||
|
return null;
|
||||||
|
const output = @ptrCast(
|
||||||
|
*Output,
|
||||||
|
@alignCast(@alignOf(*Output), wlr_output.*.data orelse return null),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get output-local coords from the layout coords
|
||||||
|
var ox = lx;
|
||||||
|
var oy = ly;
|
||||||
|
c.wlr_output_layout_output_coords(root.wlr_output_layout, wlr_output, &ox, &oy);
|
||||||
|
|
||||||
|
// Check layers and views from top to bottom
|
||||||
|
const layer_idxs = [_]usize{
|
||||||
|
c.ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
|
||||||
|
c.ZWLR_LAYER_SHELL_V1_LAYER_TOP,
|
||||||
|
c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM,
|
||||||
|
c.ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check overlay layer incl. popups
|
||||||
|
if (layerSurfaceAt(output.*, output.layers[0], ox, oy, sx, sy, false)) |surface| {
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check top-background popups only
|
||||||
|
for (layer_idxs[1..4]) |layer_idx| {
|
||||||
|
if (layerSurfaceAt(output.*, output.layers[layer_idx], ox, oy, sx, sy, true)) |surface| {
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check top layer
|
||||||
|
if (layerSurfaceAt(output.*, output.layers[1], ox, oy, sx, sy, false)) |surface| {
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check floating views then normal views
|
||||||
|
if (viewSurfaceAt(output.*, ox, oy, sx, sy, true)) |surface| {
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
if (viewSurfaceAt(output.*, ox, oy, sx, sy, false)) |surface| {
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the bottom-background layers
|
||||||
|
for (layer_idxs[2..4]) |layer_idx| {
|
||||||
|
if (layerSurfaceAt(output.*, output.layers[layer_idx], ox, oy, sx, sy, false)) |surface| {
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the topmost surface on the given layer at ox,oy. Will only check
|
||||||
|
/// popups if popups_only is true.
|
||||||
|
fn layerSurfaceAt(
|
||||||
|
output: Output,
|
||||||
|
layer: std.TailQueue(LayerSurface),
|
||||||
|
ox: f64,
|
||||||
|
oy: f64,
|
||||||
|
sx: *f64,
|
||||||
|
sy: *f64,
|
||||||
|
popups_only: bool,
|
||||||
|
) ?*c.wlr_surface {
|
||||||
|
var it = layer.first;
|
||||||
|
while (it) |node| : (it = node.next) {
|
||||||
|
const layer_surface = &node.data;
|
||||||
|
const surface = c.wlr_layer_surface_v1_surface_at(
|
||||||
|
layer_surface.wlr_layer_surface,
|
||||||
|
ox - @intToFloat(f64, layer_surface.box.x),
|
||||||
|
oy - @intToFloat(f64, layer_surface.box.y),
|
||||||
|
sx,
|
||||||
|
sy,
|
||||||
|
);
|
||||||
|
if (surface) |found| {
|
||||||
|
if (!popups_only) {
|
||||||
|
return found;
|
||||||
|
} else if (c.wlr_surface_is_xdg_surface(found)) {
|
||||||
|
const wlr_xdg_surface = c.wlr_xdg_surface_from_wlr_surface(found);
|
||||||
|
if (wlr_xdg_surface.*.role ==
|
||||||
|
c.enum_wlr_xdg_surface_role.WLR_XDG_SURFACE_ROLE_POPUP)
|
||||||
|
{
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the topmost visible view surface (incl. popups) at ox,oy. Will
|
||||||
|
/// check only floating views if floating is true.
|
||||||
|
fn viewSurfaceAt(output: Output, ox: f64, oy: f64, sx: *f64, sy: *f64, floating: bool) ?*c.wlr_surface {
|
||||||
|
var it = ViewStack(View).iterator(output.views.first, output.current_focused_tags);
|
||||||
|
while (it.next()) |node| {
|
||||||
|
const view = &node.view;
|
||||||
|
if (view.floating != floating) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const surface = c.wlr_xdg_surface_surface_at(
|
||||||
|
view.wlr_xdg_surface,
|
||||||
|
ox - @intToFloat(f64, view.current_box.x),
|
||||||
|
oy - @intToFloat(f64, view.current_box.y),
|
||||||
|
sx,
|
||||||
|
sy,
|
||||||
|
);
|
||||||
|
if (surface) |found| {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -34,6 +34,7 @@ pub const LayerSurface = struct {
|
|||||||
) void {
|
) void {
|
||||||
self.output = output;
|
self.output = output;
|
||||||
self.wlr_layer_surface = wlr_layer_surface;
|
self.wlr_layer_surface = wlr_layer_surface;
|
||||||
|
wlr_layer_surface.data = self;
|
||||||
|
|
||||||
self.mapped = false;
|
self.mapped = false;
|
||||||
|
|
||||||
|
17
src/root.zig
17
src/root.zig
@ -75,23 +75,6 @@ pub const Root = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the topmost view under the output layout coordinates lx, ly
|
|
||||||
/// returns the view if found, and a pointer to the wlr_surface as well as the surface coordinates
|
|
||||||
pub fn viewAt(self: Self, lx: f64, ly: f64, surface: *?*c.wlr_surface, sx: *f64, sy: *f64) ?*View {
|
|
||||||
// Iterate over all views of all outputs
|
|
||||||
var output_it = self.outputs.first;
|
|
||||||
while (output_it) |node| : (output_it = node.next) {
|
|
||||||
const output = &node.data;
|
|
||||||
var view_it = ViewStack(View).iterator(output.views.first, 0xFFFFFFFF);
|
|
||||||
while (view_it.next()) |view_node| {
|
|
||||||
if (view_node.view.isAt(lx, ly, surface, sx, sy)) {
|
|
||||||
return &view_node.view;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clear the current focus.
|
/// Clear the current focus.
|
||||||
pub fn clearFocus(self: *Self) void {
|
pub fn clearFocus(self: *Self) void {
|
||||||
if (self.focused_view) |view| {
|
if (self.focused_view) |view| {
|
||||||
|
27
src/view.zig
27
src/view.zig
@ -47,6 +47,7 @@ pub const View = struct {
|
|||||||
pub fn init(self: *Self, output: *Output, wlr_xdg_surface: *c.wlr_xdg_surface, tags: u32) void {
|
pub fn init(self: *Self, output: *Output, wlr_xdg_surface: *c.wlr_xdg_surface, tags: u32) void {
|
||||||
self.output = output;
|
self.output = output;
|
||||||
self.wlr_xdg_surface = wlr_xdg_surface;
|
self.wlr_xdg_surface = wlr_xdg_surface;
|
||||||
|
wlr_xdg_surface.data = self;
|
||||||
|
|
||||||
// Inform the xdg toplevel that it is tiled.
|
// Inform the xdg toplevel that it is tiled.
|
||||||
// For example this prevents firefox from drawing shadows around itself
|
// For example this prevents firefox from drawing shadows around itself
|
||||||
@ -266,30 +267,4 @@ pub const View = struct {
|
|||||||
}
|
}
|
||||||
// TODO: check for unexpected change in size and react as needed
|
// TODO: check for unexpected change in size and react as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isAt(self: Self, lx: f64, ly: f64, surface: *?*c.wlr_surface, sx: *f64, sy: *f64) bool {
|
|
||||||
// XDG toplevels may have nested surfaces, such as popup windows for context
|
|
||||||
// menus or tooltips. This function tests if any of those are underneath the
|
|
||||||
// coordinates lx and ly (in output Layout Coordinates). If so, it sets the
|
|
||||||
// surface pointer to that wlr_surface and the sx and sy coordinates to the
|
|
||||||
// coordinates relative to that surface's top-left corner.
|
|
||||||
const view_sx = lx - @intToFloat(f64, self.current_box.x);
|
|
||||||
const view_sy = ly - @intToFloat(f64, self.current_box.y);
|
|
||||||
|
|
||||||
// This variable seems to have been unsued in TinyWL
|
|
||||||
// struct wlr_surface_box *state = &view->xdg_surface->surface->current;
|
|
||||||
|
|
||||||
var _sx: f64 = undefined;
|
|
||||||
var _sy: f64 = undefined;
|
|
||||||
const _surface = c.wlr_xdg_surface_surface_at(self.wlr_xdg_surface, view_sx, view_sy, &_sx, &_sy);
|
|
||||||
|
|
||||||
if (_surface) |surface_at| {
|
|
||||||
sx.* = _sx;
|
|
||||||
sy.* = _sy;
|
|
||||||
surface.* = surface_at;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user