river: update to wlroots 0.16

This commit is contained in:
Isaac Freund 2022-11-11 20:25:21 +01:00
parent 5eb0e23780
commit 489a49735a
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
34 changed files with 289 additions and 369 deletions

View File

@ -42,7 +42,7 @@ distribution.
- [zig](https://ziglang.org/download/) 0.9
- wayland
- wayland-protocols
- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.15
- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.16
- xkbcommon
- libevdev
- pixman

2
deps/zig-wayland vendored

@ -1 +1 @@
Subproject commit c19ffc58a88573df3e47c08599c472eb715ef7a5
Subproject commit f64abbd016a918fe6e74e0cf337410cecad5eb5d

2
deps/zig-wlroots vendored

@ -1 +1 @@
Subproject commit 06297208176ac530b4882c92d6b064bda467c001
Subproject commit 916aa5041f4300c0e7a0e34d4f256528cdbd0507

2
deps/zig-xkbcommon vendored

@ -1 +1 @@
Subproject commit 0f6eda023e6f52ea001c597fda0a7c3e7a2ccce0
Subproject commit c97f8e18dddda04414067cf8fbfdaa7682dcb44a

View File

@ -1,42 +0,0 @@
// This file is part of river, a dynamic tiling wayland compositor.
//
// Copyright 2020 The River Developers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const Self = @This();
const wlr = @import("wlroots");
x: i32,
y: i32,
width: u32,
height: u32,
pub fn fromWlrBox(wlr_box: wlr.Box) Self {
return Self{
.x = @intCast(i32, wlr_box.x),
.y = @intCast(i32, wlr_box.y),
.width = @intCast(u32, wlr_box.width),
.height = @intCast(u32, wlr_box.height),
};
}
pub fn toWlrBox(self: Self) wlr.Box {
return wlr.Box{
.x = @intCast(c_int, self.x),
.y = @intCast(c_int, self.y),
.width = @intCast(c_int, self.width),
.height = @intCast(c_int, self.height),
};
}

View File

@ -47,7 +47,7 @@ pub const HideCursorWhenTypingMode = enum {
background_color: [4]f32 = [_]f32{ 0.0, 0.16862745, 0.21176471, 1.0 }, // Solarized base03
/// Width of borders in pixels
border_width: u32 = 2,
border_width: u31 = 2,
/// Color of border of focused window in RGBA
border_color_focused: [4]f32 = [_]f32{ 0.57647059, 0.63137255, 0.63137255, 1.0 }, // Solarized base1

View File

@ -30,7 +30,6 @@ const c = @import("c.zig");
const server = &@import("main.zig").server;
const util = @import("util.zig");
const Box = @import("Box.zig");
const Config = @import("Config.zig");
const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig");
@ -855,8 +854,8 @@ pub fn enterMode(self: *Self, mode: enum { move, resize }, view: *View) void {
const cur_box = &view.current.box;
self.mode = .{ .resize = .{
.view = view,
.offset_x = cur_box.x + @intCast(i32, cur_box.width) - @floatToInt(i32, self.wlr_cursor.x),
.offset_y = cur_box.y + @intCast(i32, cur_box.height) - @floatToInt(i32, self.wlr_cursor.y),
.offset_x = cur_box.x + cur_box.width - @floatToInt(i32, self.wlr_cursor.x),
.offset_y = cur_box.y + cur_box.height - @floatToInt(i32, self.wlr_cursor.y),
} };
view.setResizing(true);
},
@ -971,23 +970,25 @@ fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64,
const border_width = if (data.view.draw_borders) server.config.border_width else 0;
// Set width/height of view, clamp to view size constraints and output dimensions
const box = &data.view.pending.box;
box.width = @intCast(u32, math.max(0, @intCast(i32, box.width) + @floatToInt(i32, dx)));
box.height = @intCast(u32, math.max(0, @intCast(i32, box.height) + @floatToInt(i32, dy)));
data.view.pending.box.width += @floatToInt(i32, dx);
data.view.pending.box.height += @floatToInt(i32, dy);
data.view.applyConstraints();
const output_resolution = data.view.output.getEffectiveResolution();
box.width = math.min(box.width, output_resolution.width - border_width - @intCast(u32, box.x));
box.height = math.min(box.height, output_resolution.height - border_width - @intCast(u32, box.y));
var output_width: i32 = undefined;
var output_height: i32 = undefined;
data.view.output.wlr_output.effectiveResolution(&output_width, &output_height);
const box = &data.view.pending.box;
box.width = math.min(box.width, output_width - border_width - box.x);
box.height = math.min(box.height, output_height - border_width - box.y);
data.view.applyPending();
// Keep cursor locked to the original offset from the bottom right corner
self.wlr_cursor.warpClosest(
device,
@intToFloat(f64, box.x + @intCast(i32, box.width) - data.offset_x),
@intToFloat(f64, box.y + @intCast(i32, box.height) - data.offset_y),
@intToFloat(f64, box.x + box.width - data.offset_x),
@intToFloat(f64, box.y + box.height - data.offset_y),
);
},
}
@ -1025,7 +1026,7 @@ pub fn updateState(self: *Self) void {
var now: os.timespec = undefined;
os.clock_gettime(os.CLOCK.MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported");
const msec = @intCast(u32, now.tv_sec * std.time.ms_per_s +
@divFloor(now.tv_nsec, std.time.ns_per_ms));
@divTrunc(now.tv_nsec, std.time.ns_per_ms));
self.passthrough(msec);
}
}

View File

@ -40,14 +40,14 @@ pub const EventState = enum {
@"disabled-on-external-mouse",
pub fn apply(event_state: EventState, device: *c.libinput_device) void {
const want = switch (event_state) {
const want: u32 = switch (event_state) {
.enabled => c.LIBINPUT_CONFIG_SEND_EVENTS_ENABLED,
.disabled => c.LIBINPUT_CONFIG_SEND_EVENTS_DISABLED,
.@"disabled-on-external-mouse" => c.LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE,
};
const current = c.libinput_device_config_send_events_get_mode(device);
if (want != current) {
_ = c.libinput_device_config_send_events_set_mode(device, @intCast(u32, want));
_ = c.libinput_device_config_send_events_set_mode(device, want);
}
}
};
@ -259,7 +259,7 @@ pub const ScrollButton = struct {
pub fn apply(scroll_button: ScrollButton, device: *c.libinput_device) void {
const supports = c.libinput_device_config_scroll_get_methods(device);
if (supports & ~@intCast(u32, c.LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0) return;
if (supports & ~@as(u32, c.LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0) return;
_ = c.libinput_device_config_scroll_set_button(device, scroll_button.button);
}
};

View File

@ -50,12 +50,6 @@ pub fn init(device: *InputDevice, seat: *Seat, wlr_device: *wlr.InputDevice) !vo
else => @tagName(wlr_device.type),
};
// wlroots 0.15 leaves wlr_input_device->name NULL for keyboard groups.
// This wart has been cleaned up in 0.16, so just work around it until that is released.
// TODO(wlroots): Remove this hack
const name = if (isKeyboardGroup(wlr_device)) "wlr_keyboard_group" else wlr_device.name;
const identifier = try std.fmt.allocPrint(
util.gpa,
"{s}-{}-{}-{s}",
@ -63,7 +57,7 @@ pub fn init(device: *InputDevice, seat: *Seat, wlr_device: *wlr.InputDevice) !vo
device_type,
wlr_device.vendor,
wlr_device.product,
mem.trim(u8, mem.span(name), &ascii.spaces),
mem.trim(u8, mem.span(wlr_device.name), &ascii.spaces),
},
);
errdefer util.gpa.free(identifier);
@ -115,7 +109,7 @@ pub fn deinit(device: *InputDevice) void {
fn isKeyboardGroup(wlr_device: *wlr.InputDevice) bool {
return wlr_device.type == .keyboard and
wlr.KeyboardGroup.fromKeyboard(wlr_device.device.keyboard) != null;
wlr.KeyboardGroup.fromKeyboard(wlr_device.toKeyboard()) != null;
}
fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice) void {

View File

@ -210,7 +210,7 @@ fn handleNewVirtualPointer(
log.debug("Ignoring output suggestion from virtual pointer", .{});
}
self.defaultSeat().addDevice(&event.new_pointer.input_device);
self.defaultSeat().addDevice(&event.new_pointer.pointer.base);
}
fn handleNewVirtualKeyboard(
@ -218,5 +218,5 @@ fn handleNewVirtualKeyboard(
virtual_keyboard: *wlr.VirtualKeyboardV1,
) void {
const seat = @intToPtr(*Seat, virtual_keyboard.seat.data);
seat.addDevice(&virtual_keyboard.input_device);
seat.addDevice(&virtual_keyboard.keyboard.base);
}

View File

@ -54,7 +54,7 @@ pub fn init(self: *Self, seat: *Seat, wlr_device: *wlr.InputDevice) !void {
const keymap = xkb.Keymap.newFromNames(context, null, .no_flags) orelse return error.XkbKeymapFailed;
defer keymap.unref();
const wlr_keyboard = self.device.wlr_device.device.keyboard;
const wlr_keyboard = self.device.wlr_device.toKeyboard();
wlr_keyboard.data = @ptrToInt(self);
if (!wlr_keyboard.setKeymap(keymap)) return error.SetKeymapFailed;
@ -77,7 +77,7 @@ pub fn deinit(self: *Self) void {
fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboard.event.Key) void {
// This event is raised when a key is pressed or released.
const self = @fieldParentPtr(Self, "key", listener);
const wlr_keyboard = self.device.wlr_device.device.keyboard;
const wlr_keyboard = self.device.wlr_device.toKeyboard();
// If the keyboard is in a group, this event will be handled by the group's Keyboard instance.
if (wlr_keyboard.group != null) return;
@ -125,7 +125,7 @@ fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboa
if (!eaten) {
// If key was not handled, we pass it along to the client.
const wlr_seat = self.device.seat.wlr_seat;
wlr_seat.setKeyboard(self.device.wlr_device);
wlr_seat.setKeyboard(self.device.wlr_device.toKeyboard());
wlr_seat.keyboardNotifyKey(event.time_msec, event.keycode, event.state);
}
}
@ -136,12 +136,12 @@ fn isModifier(keysym: xkb.Keysym) bool {
fn handleModifiers(listener: *wl.Listener(*wlr.Keyboard), _: *wlr.Keyboard) void {
const self = @fieldParentPtr(Self, "modifiers", listener);
const wlr_keyboard = self.device.wlr_device.device.keyboard;
const wlr_keyboard = self.device.wlr_device.toKeyboard();
// If the keyboard is in a group, this event will be handled by the group's Keyboard instance.
if (wlr_keyboard.group != null) return;
self.device.seat.wlr_seat.setKeyboard(self.device.wlr_device);
self.device.seat.wlr_seat.setKeyboard(self.device.wlr_device.toKeyboard());
self.device.seat.wlr_seat.keyboardNotifyModifiers(&wlr_keyboard.modifiers);
}

View File

@ -55,7 +55,7 @@ pub fn create(seat: *Seat, name: []const u8) !void {
.seat = seat,
};
seat.addDevice(wlr_group.input_device);
seat.addDevice(&wlr_group.keyboard.base);
seat.keyboard_groups.append(node);
}
@ -95,8 +95,7 @@ pub fn addIdentifier(group: *KeyboardGroup, new_id: []const u8) !void {
if (mem.eql(u8, new_id, device.identifier)) {
log.debug("found existing matching keyboard; adding to group", .{});
const wlr_keyboard = device.wlr_device.device.keyboard;
if (!group.wlr_group.addKeyboard(wlr_keyboard)) {
if (!group.wlr_group.addKeyboard(device.wlr_device.toKeyboard())) {
// wlroots logs an error message to explain why this failed.
continue;
}
@ -119,7 +118,7 @@ pub fn removeIdentifier(group: *KeyboardGroup, id: []const u8) !void {
if (device.wlr_device.type != .keyboard) continue;
if (mem.eql(u8, device.identifier, id)) {
const wlr_keyboard = device.wlr_device.device.keyboard;
const wlr_keyboard = device.wlr_device.toKeyboard();
assert(wlr_keyboard.group == group.wlr_group);
group.wlr_group.removeKeyboard(wlr_keyboard);
}

View File

@ -25,7 +25,6 @@ const zwlr = @import("wayland").server.zwlr;
const server = &@import("main.zig").server;
const util = @import("util.zig");
const Box = @import("Box.zig");
const Output = @import("Output.zig");
const Subsurface = @import("Subsurface.zig");
const XdgPopup = @import("XdgPopup.zig");
@ -35,7 +34,7 @@ const log = std.log.scoped(.layer_shell);
output: *Output,
wlr_layer_surface: *wlr.LayerSurfaceV1,
box: Box = undefined,
box: wlr.Box = undefined,
layer: zwlr.LayerShellV1.Layer,
destroy: wl.Listener(*wlr.LayerSurfaceV1) = wl.Listener(*wlr.LayerSurfaceV1).init(handleDestroy),

View File

@ -18,6 +18,7 @@ const Self = @This();
const std = @import("std");
const assert = std.debug.assert;
const math = std.math;
const mem = std.mem;
const wlr = @import("wlroots");
const wayland = @import("wayland");
@ -27,7 +28,6 @@ const river = wayland.server.river;
const server = &@import("main.zig").server;
const util = @import("util.zig");
const Box = @import("Box.zig");
const Server = @import("Server.zig");
const Output = @import("Output.zig");
const View = @import("View.zig");
@ -111,8 +111,8 @@ pub fn startLayoutDemand(self: *Self, views: u32) void {
self.layout.sendLayoutDemand(
views,
self.output.usable_box.width,
self.output.usable_box.height,
@intCast(u32, self.output.usable_box.width),
@intCast(u32, self.output.usable_box.height),
self.output.pending.tags,
self.output.layout_demand.?.serial,
);
@ -137,7 +137,13 @@ fn handleRequest(layout: *river.LayoutV3, request: river.LayoutV3.Request, self:
// because we do not keep track of old serials server-side.
// Therefore, simply ignore requests with old/wrong serials.
if (layout_demand.serial != req.serial) return;
layout_demand.pushViewDimensions(self.output, req.x, req.y, req.width, req.height);
layout_demand.pushViewDimensions(
self.output,
req.x,
req.y,
@intCast(u31, math.min(math.maxInt(u31), req.width)),
@intCast(u31, math.min(math.maxInt(u31), req.height)),
);
}
},

View File

@ -26,7 +26,6 @@ const server = &@import("main.zig").server;
const util = @import("util.zig");
const Layout = @import("Layout.zig");
const Box = @import("Box.zig");
const Server = @import("Server.zig");
const Output = @import("Output.zig");
const View = @import("View.zig");
@ -43,7 +42,7 @@ serial: u32,
/// This will go negative if the client pushes too many dimensions.
views: i32,
/// Proposed view dimensions
view_boxen: []Box,
view_boxen: []wlr.Box,
timeout_timer: *wl.EventSource,
pub fn init(layout: *Layout, views: u32) !Self {
@ -55,7 +54,7 @@ pub fn init(layout: *Layout, views: u32) !Self {
return Self{
.serial = server.wl_server.nextSerial(),
.views = @intCast(i32, views),
.view_boxen = try util.gpa.alloc(Box, views),
.view_boxen = try util.gpa.alloc(wlr.Box, views),
.timeout_timer = timeout_timer,
};
}
@ -81,7 +80,7 @@ fn handleTimeout(layout: *Layout) callconv(.C) c_int {
}
/// Push a set of proposed view dimensions and position to the list
pub fn pushViewDimensions(self: *Self, output: *Output, x: i32, y: i32, width: u32, height: u32) void {
pub fn pushViewDimensions(self: *Self, output: *Output, x: i32, y: i32, width: u31, height: u31) void {
// The client pushed too many dimensions
if (self.views <= 0) {
self.views -= 1;
@ -92,8 +91,8 @@ pub fn pushViewDimensions(self: *Self, output: *Output, x: i32, y: i32, width: u
// usable area and shrink the dimensions to accomodate the border size.
const border_width = server.config.border_width;
self.view_boxen[self.view_boxen.len - @intCast(usize, self.views)] = .{
.x = x + output.usable_box.x + @intCast(i32, border_width),
.y = y + output.usable_box.y + @intCast(i32, border_width),
.x = x + output.usable_box.x + border_width,
.y = y + output.usable_box.y + border_width,
.width = if (width > 2 * border_width) width - 2 * border_width else width,
.height = if (height > 2 * border_width) height - 2 * border_width else height,
};

View File

@ -18,6 +18,7 @@ const Self = @This();
const std = @import("std");
const assert = std.debug.assert;
const math = std.math;
const mem = std.mem;
const fmt = std.fmt;
const wlr = @import("wlroots");
@ -29,7 +30,6 @@ const render = @import("render.zig");
const server = &@import("main.zig").server;
const util = @import("util.zig");
const Box = @import("Box.zig");
const LayerSurface = @import("LayerSurface.zig");
const Layout = @import("Layout.zig");
const LayoutDemand = @import("LayoutDemand.zig");
@ -62,7 +62,7 @@ layers: [4]std.TailQueue(LayerSurface) = [1]std.TailQueue(LayerSurface){.{}} **
/// The area left for views and other layer surfaces after applying the
/// exclusive zones of exclusive layer surfaces.
/// TODO: this should be part of the output's State
usable_box: Box,
usable_box: wlr.Box,
/// The top of the stack is the "most important" view.
views: ViewStack(View) = .{},
@ -86,7 +86,7 @@ layouts: std.TailQueue(Layout) = .{},
layout_namespace: ?[]const u8 = null,
/// Bitmask that whitelists tags for newly spawned views
spawn_tagmask: u32 = std.math.maxInt(u32),
spawn_tagmask: u32 = math.maxInt(u32),
/// List of status tracking objects relaying changes to this output to clients.
status_trackers: std.SinglyLinkedList(OutputStatus) = .{},
@ -145,13 +145,13 @@ pub fn init(self: *Self, wlr_output: *wlr.Output) !void {
std.log.scoped(.cursor).err("failed to load xcursor theme at scale {}", .{wlr_output.scale});
}
const effective_resolution = self.getEffectiveResolution();
self.usable_box = .{
.x = 0,
.y = 0,
.width = effective_resolution.width,
.height = effective_resolution.height,
.width = undefined,
.height = undefined,
};
self.wlr_output.effectiveResolution(&self.usable_box.width, &self.usable_box.height);
self.setTitle();
}
@ -230,13 +230,13 @@ const ArrangeLayersTarget = enum { mapped, unmapped };
/// If target is unmapped, this function is pure aside from the
/// wlr.LayerSurfaceV1.configure() calls made on umapped layer surfaces.
pub fn arrangeLayers(self: *Self, target: ArrangeLayersTarget) void {
const effective_resolution = self.getEffectiveResolution();
const full_box: Box = .{
var full_box: wlr.Box = .{
.x = 0,
.y = 0,
.width = effective_resolution.width,
.height = effective_resolution.height,
.width = undefined,
.height = undefined,
};
self.wlr_output.effectiveResolution(&full_box.width, &full_box.height);
// This box is modified as exclusive zones are applied
var usable_box = full_box;
@ -296,8 +296,8 @@ pub fn arrangeLayers(self: *Self, target: ArrangeLayersTarget) void {
/// Arrange the layer surfaces of a given layer
fn arrangeLayer(
layer: std.TailQueue(LayerSurface),
full_box: Box,
usable_box: *Box,
full_box: wlr.Box,
usable_box: *wlr.Box,
exclusive: bool,
target: ArrangeLayersTarget,
) void {
@ -306,6 +306,9 @@ fn arrangeLayer(
const layer_surface = &node.data;
const current_state = layer_surface.wlr_layer_surface.current;
const desired_width = @intCast(u31, math.min(math.maxInt(u31), current_state.desired_width));
const desired_height = @intCast(u31, math.min(math.maxInt(u31), current_state.desired_height));
// If the value of exclusive_zone is greater than zero, then it exclusivly
// occupies some area of the screen.
if (exclusive != (current_state.exclusive_zone > 0)) continue;
@ -314,46 +317,40 @@ fn arrangeLayer(
// to ignore any exclusive zones and use the full area of the output.
const bounds = if (current_state.exclusive_zone == -1) &full_box else usable_box;
var new_box: Box = undefined;
var new_box: wlr.Box = undefined;
// Horizontal alignment
if (current_state.desired_width == 0) {
if (desired_width == 0) {
assert(current_state.anchor.right and current_state.anchor.left);
new_box.x = bounds.x + @intCast(i32, current_state.margin.left);
new_box.x = bounds.x + current_state.margin.left;
new_box.width = bounds.width - (current_state.margin.left + current_state.margin.right);
} else if (current_state.anchor.left == current_state.anchor.right) {
new_box.x = bounds.x + @intCast(i32, bounds.width / 2 -| current_state.desired_width / 2);
new_box.width = current_state.desired_width;
new_box.x = bounds.x + @divTrunc(bounds.width, 2) - desired_width / 2;
new_box.width = desired_width;
} else if (current_state.anchor.left) {
new_box.x = bounds.x + @intCast(i32, current_state.margin.left);
new_box.width = current_state.desired_width;
new_box.x = bounds.x + current_state.margin.left;
new_box.width = desired_width;
} else {
assert(current_state.anchor.right);
new_box.x = bounds.x + @intCast(i32, bounds.width) -
@intCast(i32, current_state.desired_width) -
// TODO(wlroots) this type has been corrected to i32 for the next release
@intCast(i32, current_state.margin.right);
new_box.width = current_state.desired_width;
new_box.x = bounds.x + bounds.width - desired_width - current_state.margin.right;
new_box.width = desired_width;
}
// Vertical alignment
if (current_state.desired_height == 0) {
if (desired_height == 0) {
assert(current_state.anchor.top and current_state.anchor.bottom);
new_box.y = bounds.y + @intCast(i32, current_state.margin.top);
new_box.y = bounds.y + current_state.margin.top;
new_box.height = bounds.height - (current_state.margin.top + current_state.margin.bottom);
} else if (current_state.anchor.top == current_state.anchor.bottom) {
new_box.y = bounds.y + @intCast(i32, bounds.height / 2 -| current_state.desired_height / 2);
new_box.height = current_state.desired_height;
new_box.y = bounds.y + @divTrunc(bounds.height, 2) - desired_height / 2;
new_box.height = desired_height;
} else if (current_state.anchor.top) {
new_box.y = bounds.y + @intCast(i32, current_state.margin.top);
new_box.height = current_state.desired_height;
new_box.y = bounds.y + current_state.margin.top;
new_box.height = desired_height;
} else {
assert(current_state.anchor.bottom);
new_box.y = bounds.y + @intCast(i32, bounds.height) -
@intCast(i32, current_state.desired_height) -
// TODO(wlroots) this type has been corrected to i32 for the next release
@intCast(i32, current_state.margin.bottom);
new_box.height = current_state.desired_height;
new_box.y = bounds.y + bounds.height - desired_height - current_state.margin.bottom;
new_box.height = desired_height;
}
// Apply the exclusive zone to the current bounds
@ -361,8 +358,8 @@ fn arrangeLayer(
single: zwlr.LayerSurfaceV1.Anchor,
triple: zwlr.LayerSurfaceV1.Anchor,
to_increase: ?*i32,
to_decrease: *u32,
margin: u32,
to_decrease: *i32,
margin: i32,
}{
.{
.single = .{ .top = true },
@ -396,11 +393,11 @@ fn arrangeLayer(
for (edges) |edge| {
if ((std.meta.eql(current_state.anchor, edge.single) or std.meta.eql(current_state.anchor, edge.triple)) and
current_state.exclusive_zone + @intCast(i32, edge.margin) > 0)
current_state.exclusive_zone + edge.margin > 0)
{
const delta = current_state.exclusive_zone + @intCast(i32, edge.margin);
const delta = current_state.exclusive_zone + edge.margin;
if (edge.to_increase) |value| value.* += delta;
edge.to_decrease.* -= @intCast(u32, delta);
edge.to_decrease.* -= delta;
break;
}
}
@ -409,10 +406,16 @@ fn arrangeLayer(
.mapped => {
assert(layer_surface.wlr_layer_surface.mapped);
layer_surface.box = new_box;
_ = layer_surface.wlr_layer_surface.configure(new_box.width, new_box.height);
_ = layer_surface.wlr_layer_surface.configure(
@intCast(u32, new_box.width),
@intCast(u32, new_box.height),
);
},
.unmapped => if (!layer_surface.wlr_layer_surface.mapped) {
_ = layer_surface.wlr_layer_surface.configure(new_box.width, new_box.height);
_ = layer_surface.wlr_layer_surface.configure(
@intCast(u32, new_box.width),
@intCast(u32, new_box.height),
);
},
}
}
@ -484,16 +487,6 @@ fn handleMode(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
server.root.startTransaction();
}
pub fn getEffectiveResolution(self: *Self) struct { width: u32, height: u32 } {
var width: c_int = undefined;
var height: c_int = undefined;
self.wlr_output.effectiveResolution(&width, &height);
return .{
.width = @intCast(u32, width),
.height = @intCast(u32, height),
};
}
fn setTitle(self: Self) void {
const title = fmt.allocPrintZ(util.gpa, "river - {s}", .{self.wlr_output.name}) catch return;
defer util.gpa.free(title);

View File

@ -80,7 +80,8 @@ fn constrainToRegion(self: *Self) void {
if (self.cursor.constraint != self.constraint) return;
if (View.fromWlrSurface(self.constraint.surface)) |view| {
const output = view.output;
const output_box = server.root.output_layout.getBox(output.wlr_output).?;
var output_box: wlr.Box = undefined;
server.root.output_layout.getBox(output.wlr_output, &output_box);
const surface_lx = @intToFloat(f64, output_box.x + view.current.box.x - view.surface_box.x);
const surface_ly = @intToFloat(f64, output_box.y + view.current.box.y - view.surface_box.y);
@ -126,7 +127,8 @@ pub fn warpToHint(cursor: *Cursor) void {
if (constraint.current.committed.cursor_hint) {
if (View.fromWlrSurface(constraint.surface)) |view| {
const output = view.output;
const output_box = server.root.output_layout.getBox(output.wlr_output).?;
var output_box: wlr.Box = undefined;
server.root.output_layout.getBox(output.wlr_output, &output_box);
const surface_lx = @intToFloat(f64, output_box.x + view.current.box.x - view.surface_box.x);
const surface_ly = @intToFloat(f64, output_box.y + view.current.box.y - view.surface_box.y);

View File

@ -527,13 +527,13 @@ fn currentOutputConfig(self: *Self) !*wlr.OutputConfigurationV1 {
const output = node.data;
const head = try wlr.OutputConfigurationV1.Head.create(config, output.wlr_output);
// If the output is not part of the layout (and thus disabled) we dont care
// about the position
if (self.output_layout.getBox(output.wlr_output)) |box| {
// If the output is not part of the layout (and thus disabled)
// the box will be zeroed out.
var box: wlr.Box = undefined;
self.output_layout.getBox(output.wlr_output, &box);
head.state.x = box.x;
head.state.y = box.y;
}
}
return config;
}

View File

@ -316,11 +316,14 @@ pub fn focusOutput(self: *Self, output: *Output) void {
switch (server.config.warp_cursor) {
.disabled => {},
.@"on-output-change" => {
const layout_box = server.root.output_layout.getBox(output.wlr_output).?;
var layout_box: wlr.Box = undefined;
server.root.output_layout.getBox(output.wlr_output, &layout_box);
if (!layout_box.containsPoint(self.cursor.wlr_cursor.x, self.cursor.wlr_cursor.y)) {
const eff_res = output.getEffectiveResolution();
const lx = @intToFloat(f32, layout_box.x + @intCast(i32, eff_res.width / 2));
const ly = @intToFloat(f32, layout_box.y + @intCast(i32, eff_res.height / 2));
var output_width: i32 = undefined;
var output_height: i32 = undefined;
output.wlr_output.effectiveResolution(&output_width, &output_height);
const lx = @intToFloat(f64, layout_box.x + @divTrunc(output_width, 2));
const ly = @intToFloat(f64, layout_box.y + @divTrunc(output_height, 2));
if (!self.cursor.wlr_cursor.warp(null, lx, ly)) {
log.err("failed to warp cursor on output change", .{});
}
@ -482,7 +485,7 @@ fn tryAddDevice(self: *Self, wlr_device: *wlr.InputDevice) !void {
try keyboard.init(self, wlr_device);
self.wlr_seat.setKeyboard(keyboard.device.wlr_device);
self.wlr_seat.setKeyboard(keyboard.device.wlr_device.toKeyboard());
if (self.wlr_seat.keyboard_state.focused_surface) |wlr_surface| {
self.wlr_seat.keyboardNotifyClearFocus();
self.keyboardNotifyEnter(wlr_surface);

View File

@ -97,8 +97,9 @@ pub fn init(self: *Self) !void {
errdefer self.allocator.destroy();
const compositor = try wlr.Compositor.create(self.wl_server, self.renderer);
_ = try wlr.Subcompositor.create(self.wl_server);
self.xdg_shell = try wlr.XdgShell.create(self.wl_server);
self.xdg_shell = try wlr.XdgShell.create(self.wl_server, 2);
self.new_xdg_surface.setNotify(handleNewXdgSurface);
self.xdg_shell.events.new_surface.add(&self.new_xdg_surface);
@ -188,7 +189,7 @@ fn handleNewXdgSurface(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wl
log.debug("new xdg_toplevel", .{});
const output = self.input_manager.defaultSeat().focused_output;
XdgToplevel.create(output, xdg_surface) catch {
XdgToplevel.create(output, xdg_surface.role_data.toplevel) catch {
log.err("out of memory", .{});
xdg_surface.resource.postNoMemory();
return;

View File

@ -59,7 +59,7 @@ pub fn init(self: *Self, seat: *Seat, wlr_device: *wlr.InputDevice) !void {
try self.device.init(seat, wlr_device);
errdefer self.device.deinit();
wlr_device.device.switch_device.events.toggle.add(&self.toggle);
wlr_device.toSwitch().events.toggle.add(&self.toggle);
}
pub fn deinit(self: *Self) void {
@ -83,7 +83,6 @@ fn handleToggle(listener: *wl.Listener(*wlr.Switch.event.Toggle), event: *wlr.Sw
switch_state = switch (event.switch_state) {
.off => .{ .lid = .open },
.on => .{ .lid = .close },
.toggle => unreachable,
};
},
.tablet_mode => {
@ -91,7 +90,6 @@ fn handleToggle(listener: *wl.Listener(*wlr.Switch.event.Toggle), event: *wlr.Sw
switch_state = switch (event.switch_state) {
.off => .{ .tablet = .off },
.on => .{ .tablet = .on },
.toggle => unreachable,
};
},
}

View File

@ -27,7 +27,6 @@ const wl = @import("wayland").server.wl;
const server = &@import("main.zig").server;
const util = @import("util.zig");
const Box = @import("Box.zig");
const Output = @import("Output.zig");
const Seat = @import("Seat.zig");
const ViewStack = @import("view_stack.zig").ViewStack;
@ -37,10 +36,10 @@ const XwaylandView = if (build_options.xwayland) @import("XwaylandView.zig") els
const log = std.log.scoped(.view);
pub const Constraints = struct {
min_width: u32,
max_width: u32,
min_height: u32,
max_height: u32,
min_width: u31,
max_width: u31,
min_height: u31,
max_height: u31,
};
const Impl = union(enum) {
@ -52,7 +51,7 @@ const State = struct {
/// The output-relative effective coordinates and effective dimensions of the view. The
/// surface itself may have other dimensions which are stored in the
/// surface_box member.
box: Box = Box{ .x = 0, .y = 0, .width = 0, .height = 0 },
box: wlr.Box = wlr.Box{ .x = 0, .y = 0, .width = 0, .height = 0 },
/// The tags of the view, as a bitmask
tags: u32,
@ -68,7 +67,7 @@ const State = struct {
const SavedBuffer = struct {
client_buffer: *wlr.ClientBuffer,
/// x/y relative to the root surface in the surface tree.
surface_box: Box,
surface_box: wlr.Box,
source_box: wlr.FBox,
transform: wl.Output.Transform,
};
@ -95,22 +94,22 @@ pending_serial: ?u32 = null,
/// The currently commited geometry of the surface. The x/y may be negative if
/// for example the client has decided to draw CSD shadows a la GTK.
surface_box: Box = undefined,
surface_box: wlr.Box = undefined,
/// The geometry the view's surface had when the transaction started and
/// buffers were saved.
saved_surface_box: Box = undefined,
saved_surface_box: wlr.Box = undefined,
/// These are what we render while a transaction is in progress
saved_buffers: std.ArrayListUnmanaged(SavedBuffer) = .{},
/// The floating dimensions the view, saved so that they can be restored if the
/// view returns to floating mode.
float_box: Box = undefined,
float_box: wlr.Box = undefined,
/// This state exists purely to allow for more intuitive behavior when
/// exiting fullscreen if there is no active layout.
post_fullscreen_box: Box = undefined,
post_fullscreen_box: wlr.Box = undefined,
draw_borders: bool = true,
@ -172,13 +171,14 @@ pub fn applyPending(self: *Self) void {
// If switching to fullscreen, set the dimensions to the full area of the output
self.setFullscreen(true);
self.post_fullscreen_box = self.current.box;
const dimensions = self.output.getEffectiveResolution();
self.pending.box = .{
.x = 0,
.y = 0,
.width = dimensions.width,
.height = dimensions.height,
.width = undefined,
.height = undefined,
};
self.output.wlr_output.effectiveResolution(&self.pending.box.width, &self.pending.box.height);
} else if (self.lastSetFullscreenState() and !self.pending.fullscreen) {
self.setFullscreen(false);
self.pending.box = self.post_fullscreen_box;
@ -246,8 +246,8 @@ fn saveBuffersIterator(
.surface_box = .{
.x = surface_x,
.y = surface_y,
.width = @intCast(u32, surface.current.width),
.height = @intCast(u32, surface.current.height),
.width = surface.current.width,
.height = surface.current.height,
},
.source_box = source_box,
.transform = surface.current.transform,
@ -284,22 +284,19 @@ pub fn sendToOutput(self: *Self, destination_output: *Output) void {
}
self.output = destination_output;
const dimensions = destination_output.getEffectiveResolution();
var output_width: i32 = undefined;
var output_height: i32 = undefined;
destination_output.wlr_output.effectiveResolution(&output_width, &output_height);
if (self.pending.float) {
// Adapt dimensions of view to new output. Only necessary when floating,
// because for tiled views the output will be rearranged, taking care
// of this.
if (self.pending.fullscreen) self.pending.box = self.post_fullscreen_box;
const border_width = if (self.draw_borders) @intCast(i32, server.config.border_width) else 0;
self.pending.box.width = math.min(
self.pending.box.width,
@intCast(i32, dimensions.width) - (2 * border_width),
);
self.pending.box.height = math.min(
self.pending.box.height,
@intCast(i32, dimensions.height) - (2 * border_width),
);
const border_width = if (self.draw_borders) server.config.border_width else 0;
self.pending.box.width = math.min(self.pending.box.width, output_width - (2 * border_width));
self.pending.box.height = math.min(self.pending.box.height, output_height - (2 * border_width));
// Adjust position of view so that it is fully inside the target output.
self.move(0, 0);
@ -313,8 +310,8 @@ pub fn sendToOutput(self: *Self, destination_output: *Output) void {
self.pending.box = .{
.x = 0,
.y = 0,
.width = dimensions.width,
.height = dimensions.height,
.width = output_width,
.height = output_height,
};
}
}
@ -374,7 +371,7 @@ pub inline fn forEachSurface(
) void {
switch (self.impl) {
.xdg_toplevel => |xdg_toplevel| {
xdg_toplevel.xdg_surface.forEachSurface(T, iterator, user_data);
xdg_toplevel.xdg_toplevel.base.forEachSurface(T, iterator, user_data);
},
.xwayland_view => {
assert(build_options.xwayland);
@ -427,16 +424,18 @@ pub fn getConstraints(self: Self) Constraints {
/// Modify the pending x/y of the view by the given deltas, clamping to the
/// bounds of the output.
pub fn move(self: *Self, delta_x: i32, delta_y: i32) void {
const border_width = if (self.draw_borders) @intCast(i32, server.config.border_width) else 0;
const output_resolution = self.output.getEffectiveResolution();
const border_width = if (self.draw_borders) server.config.border_width else 0;
var output_width: i32 = undefined;
var output_height: i32 = undefined;
self.output.wlr_output.effectiveResolution(&output_width, &output_height);
const max_x = @intCast(i32, output_resolution.width) - @intCast(i32, self.pending.box.width) - border_width;
const max_x = output_width - self.pending.box.width - border_width;
self.pending.box.x += delta_x;
self.pending.box.x = math.max(self.pending.box.x, border_width);
self.pending.box.x = math.min(self.pending.box.x, max_x);
self.pending.box.x = math.max(self.pending.box.x, 0);
const max_y = @intCast(i32, output_resolution.height) - @intCast(i32, self.pending.box.height) - border_width;
const max_y = output_height - self.pending.box.height - border_width;
self.pending.box.y += delta_y;
self.pending.box.y = math.max(self.pending.box.y, border_width);
self.pending.box.y = math.min(self.pending.box.y, max_y);

View File

@ -19,7 +19,6 @@ const Self = @This();
const std = @import("std");
const wlr = @import("wlroots");
const Box = @import("Box.zig");
const View = @import("View.zig");
pub fn needsConfigure(_: Self) bool {

View File

@ -31,9 +31,9 @@ parent: Parent,
wlr_xdg_popup: *wlr.XdgPopup,
// Always active
surface_destroy: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleDestroy),
map: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleMap),
unmap: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleUnmap),
surface_destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy),
map: wl.Listener(void) = wl.Listener(void).init(handleMap),
unmap: wl.Listener(void) = wl.Listener(void).init(handleUnmap),
new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
new_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleNewSubsurface),
@ -55,25 +55,25 @@ pub fn create(wlr_xdg_popup: *wlr.XdgPopup, parent: Parent) void {
switch (parent) {
.xdg_toplevel => |xdg_toplevel| {
const output_dimensions = xdg_toplevel.view.output.getEffectiveResolution();
// The output box relative to the parent of the xdg_popup
var box = wlr.Box{
.x = xdg_toplevel.view.surface_box.x - xdg_toplevel.view.pending.box.x,
.y = xdg_toplevel.view.surface_box.y - xdg_toplevel.view.pending.box.y,
.width = @intCast(c_int, output_dimensions.width),
.height = @intCast(c_int, output_dimensions.height),
.width = undefined,
.height = undefined,
};
xdg_toplevel.view.output.wlr_output.effectiveResolution(&box.width, &box.height);
wlr_xdg_popup.unconstrainFromBox(&box);
},
.layer_surface => |layer_surface| {
const output_dimensions = layer_surface.output.getEffectiveResolution();
// The output box relative to the parent of the xdg_popup
var box = wlr.Box{
.x = -layer_surface.box.x,
.y = -layer_surface.box.y,
.width = @intCast(c_int, output_dimensions.width),
.height = @intCast(c_int, output_dimensions.height),
.width = undefined,
.height = undefined,
};
layer_surface.output.wlr_output.effectiveResolution(&box.width, &box.height);
wlr_xdg_popup.unconstrainFromBox(&box);
},
.drag_icon => unreachable,
@ -111,19 +111,19 @@ pub fn destroyPopups(wlr_xdg_surface: *wlr.XdgSurface) void {
}
}
fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
fn handleDestroy(listener: *wl.Listener(void)) void {
const xdg_popup = @fieldParentPtr(XdgPopup, "surface_destroy", listener);
xdg_popup.destroy();
}
fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
fn handleMap(listener: *wl.Listener(void)) void {
const xdg_popup = @fieldParentPtr(XdgPopup, "map", listener);
xdg_popup.wlr_xdg_popup.base.surface.events.commit.add(&xdg_popup.commit);
xdg_popup.parent.damageWholeOutput();
}
fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
fn handleUnmap(listener: *wl.Listener(void)) void {
const xdg_popup = @fieldParentPtr(XdgPopup, "unmap", listener);
xdg_popup.commit.link.remove();

View File

@ -24,7 +24,6 @@ const wl = @import("wayland").server.wl;
const server = &@import("main.zig").server;
const util = @import("util.zig");
const Box = @import("Box.zig");
const Output = @import("Output.zig");
const Seat = @import("Seat.zig");
const Subsurface = @import("Subsurface.zig");
@ -38,15 +37,15 @@ const log = std.log.scoped(.xdg_shell);
view: *View,
/// The corresponding wlroots object
xdg_surface: *wlr.XdgSurface,
xdg_toplevel: *wlr.XdgToplevel,
/// Set to true when the client acks the configure with serial View.pending_serial.
acked_pending_serial: bool = false,
// Listeners that are always active over the view's lifetime
destroy: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleDestroy),
map: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleMap),
unmap: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleUnmap),
destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy),
map: wl.Listener(void) = wl.Listener(void).init(handleMap),
unmap: wl.Listener(void) = wl.Listener(void).init(handleUnmap),
new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
new_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleNewSubsurface),
@ -54,42 +53,41 @@ new_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init
ack_configure: wl.Listener(*wlr.XdgSurface.Configure) =
wl.Listener(*wlr.XdgSurface.Configure).init(handleAckConfigure),
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
request_fullscreen: wl.Listener(*wlr.XdgToplevel.event.SetFullscreen) =
wl.Listener(*wlr.XdgToplevel.event.SetFullscreen).init(handleRequestFullscreen),
request_fullscreen: wl.Listener(void) = wl.Listener(void).init(handleRequestFullscreen),
request_move: wl.Listener(*wlr.XdgToplevel.event.Move) =
wl.Listener(*wlr.XdgToplevel.event.Move).init(handleRequestMove),
request_resize: wl.Listener(*wlr.XdgToplevel.event.Resize) =
wl.Listener(*wlr.XdgToplevel.event.Resize).init(handleRequestResize),
set_title: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleSetTitle),
set_app_id: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleSetAppId),
set_title: wl.Listener(void) = wl.Listener(void).init(handleSetTitle),
set_app_id: wl.Listener(void) = wl.Listener(void).init(handleSetAppId),
/// The View will add itself to the output's view stack on map
pub fn create(output: *Output, xdg_surface: *wlr.XdgSurface) error{OutOfMemory}!void {
pub fn create(output: *Output, xdg_toplevel: *wlr.XdgToplevel) error{OutOfMemory}!void {
const node = try util.gpa.create(ViewStack(View).Node);
const view = &node.view;
view.init(output, .{ .xdg_toplevel = .{
.view = view,
.xdg_surface = xdg_surface,
.xdg_toplevel = xdg_toplevel,
} });
const self = &node.view.impl.xdg_toplevel;
xdg_surface.data = @ptrToInt(self);
xdg_toplevel.base.data = @ptrToInt(self);
// Add listeners that are active over the view's entire lifetime
xdg_surface.events.destroy.add(&self.destroy);
xdg_surface.events.map.add(&self.map);
xdg_surface.events.unmap.add(&self.unmap);
xdg_surface.events.new_popup.add(&self.new_popup);
xdg_surface.surface.events.new_subsurface.add(&self.new_subsurface);
xdg_toplevel.base.events.destroy.add(&self.destroy);
xdg_toplevel.base.events.map.add(&self.map);
xdg_toplevel.base.events.unmap.add(&self.unmap);
xdg_toplevel.base.events.new_popup.add(&self.new_popup);
xdg_toplevel.base.surface.events.new_subsurface.add(&self.new_subsurface);
Subsurface.handleExisting(xdg_surface.surface, .{ .xdg_toplevel = self });
Subsurface.handleExisting(xdg_toplevel.base.surface, .{ .xdg_toplevel = self });
}
/// Returns true if a configure must be sent to ensure that the pending
/// dimensions are applied.
pub fn needsConfigure(self: Self) bool {
const scheduled = &self.xdg_surface.role_data.toplevel.scheduled;
const scheduled = &self.xdg_toplevel.scheduled;
const state = &self.view.pending;
// We avoid a special case for newly mapped views which we have not yet
@ -100,38 +98,37 @@ pub fn needsConfigure(self: Self) bool {
/// Send a configure event, applying the pending state of the view.
pub fn configure(self: *Self) void {
const toplevel = self.xdg_surface.role_data.toplevel;
const state = &self.view.pending;
self.view.pending_serial = toplevel.setSize(state.box.width, state.box.height);
self.view.pending_serial = self.xdg_toplevel.setSize(state.box.width, state.box.height);
self.acked_pending_serial = false;
}
pub fn lastSetFullscreenState(self: Self) bool {
return self.xdg_surface.role_data.toplevel.scheduled.fullscreen;
return self.xdg_toplevel.scheduled.fullscreen;
}
/// Close the view. This will lead to the unmap and destroy events being sent
pub fn close(self: Self) void {
self.xdg_surface.role_data.toplevel.sendClose();
self.xdg_toplevel.sendClose();
}
pub fn setActivated(self: Self, activated: bool) void {
_ = self.xdg_surface.role_data.toplevel.setActivated(activated);
_ = self.xdg_toplevel.setActivated(activated);
}
pub fn setFullscreen(self: Self, fullscreen: bool) void {
_ = self.xdg_surface.role_data.toplevel.setFullscreen(fullscreen);
_ = self.xdg_toplevel.setFullscreen(fullscreen);
}
pub fn setResizing(self: Self, resizing: bool) void {
_ = self.xdg_surface.role_data.toplevel.setResizing(resizing);
_ = self.xdg_toplevel.setResizing(resizing);
}
/// Return the surface at output coordinates ox, oy and set sx, sy to the
/// corresponding surface-relative coordinates, if there is a surface.
pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
const view = self.view;
return self.xdg_surface.surfaceAt(
return self.xdg_toplevel.base.surfaceAt(
ox - @intToFloat(f64, view.current.box.x - view.surface_box.x),
oy - @intToFloat(f64, view.current.box.y - view.surface_box.y),
sx,
@ -141,26 +138,26 @@ pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface
/// Return the current title of the toplevel if any.
pub fn getTitle(self: Self) ?[*:0]const u8 {
return self.xdg_surface.role_data.toplevel.title;
return self.xdg_toplevel.title;
}
/// Return the current app_id of the toplevel if any .
pub fn getAppId(self: Self) ?[*:0]const u8 {
return self.xdg_surface.role_data.toplevel.app_id;
return self.xdg_toplevel.app_id;
}
/// Return bounds on the dimensions of the toplevel.
pub fn getConstraints(self: Self) View.Constraints {
const state = &self.xdg_surface.role_data.toplevel.current;
const state = &self.xdg_toplevel.current;
return .{
.min_width = math.max(state.min_width, 1),
.max_width = if (state.max_width > 0) state.max_width else math.maxInt(u32),
.min_height = math.max(state.min_height, 1),
.max_height = if (state.max_height > 0) state.max_height else math.maxInt(u32),
.min_width = @intCast(u31, math.max(state.min_width, 1)),
.max_width = if (state.max_width > 0) @intCast(u31, state.max_width) else math.maxInt(u31),
.min_height = @intCast(u31, math.max(state.min_height, 1)),
.max_height = if (state.max_height > 0) @intCast(u31, state.max_height) else math.maxInt(u31),
};
}
fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
fn handleDestroy(listener: *wl.Listener(void)) void {
const self = @fieldParentPtr(Self, "destroy", listener);
// Remove listeners that are active for the entire lifetime of the view
@ -170,55 +167,55 @@ fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) vo
self.new_popup.link.remove();
self.new_subsurface.link.remove();
Subsurface.destroySubsurfaces(self.xdg_surface.surface);
XdgPopup.destroyPopups(self.xdg_surface);
Subsurface.destroySubsurfaces(self.xdg_toplevel.base.surface);
XdgPopup.destroyPopups(self.xdg_toplevel.base);
self.view.destroy();
}
fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
fn handleMap(listener: *wl.Listener(void)) void {
const self = @fieldParentPtr(Self, "map", listener);
const view = self.view;
const toplevel = self.xdg_surface.role_data.toplevel;
// Add listeners that are only active while mapped
self.xdg_surface.events.ack_configure.add(&self.ack_configure);
self.xdg_surface.surface.events.commit.add(&self.commit);
toplevel.events.request_fullscreen.add(&self.request_fullscreen);
toplevel.events.request_move.add(&self.request_move);
toplevel.events.request_resize.add(&self.request_resize);
toplevel.events.set_title.add(&self.set_title);
toplevel.events.set_app_id.add(&self.set_app_id);
self.xdg_toplevel.base.events.ack_configure.add(&self.ack_configure);
self.xdg_toplevel.base.surface.events.commit.add(&self.commit);
self.xdg_toplevel.events.request_fullscreen.add(&self.request_fullscreen);
self.xdg_toplevel.events.request_move.add(&self.request_move);
self.xdg_toplevel.events.request_resize.add(&self.request_resize);
self.xdg_toplevel.events.set_title.add(&self.set_title);
self.xdg_toplevel.events.set_app_id.add(&self.set_app_id);
// Use the view's initial size centered on the output as the default
// floating dimensions
var initial_box: wlr.Box = undefined;
self.xdg_surface.getGeometry(&initial_box);
view.float_box.width = @intCast(u32, initial_box.width);
view.float_box.height = @intCast(u32, initial_box.height);
view.float_box.x = math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.width) -
@intCast(i32, view.float_box.width), 2));
view.float_box.y = math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.height) -
@intCast(i32, view.float_box.height), 2));
self.xdg_toplevel.base.getGeometry(&initial_box);
view.float_box = .{
.x = @divTrunc(math.max(0, view.output.usable_box.width - initial_box.width), 2),
.y = @divTrunc(math.max(0, view.output.usable_box.height - initial_box.height), 2),
.width = initial_box.width,
.height = initial_box.height,
};
// We initialize these to avoid special-casing newly mapped views in
// the check preformed in needsConfigure().
toplevel.scheduled.width = @intCast(u32, initial_box.width);
toplevel.scheduled.height = @intCast(u32, initial_box.height);
self.xdg_toplevel.scheduled.width = initial_box.width;
self.xdg_toplevel.scheduled.height = initial_box.height;
view.surface = self.xdg_surface.surface;
view.surface_box = Box.fromWlrBox(initial_box);
view.surface = self.xdg_toplevel.base.surface;
view.surface_box = initial_box;
// Also use the view's "natural" size as the initial regular dimensions,
// for the case that it does not get arranged by a lyaout.
view.pending.box = view.float_box;
const state = &toplevel.current;
const state = &self.xdg_toplevel.current;
const has_fixed_size = state.min_width != 0 and state.min_height != 0 and
(state.min_width == state.max_width or state.min_height == state.max_height);
if (toplevel.parent != null or has_fixed_size) {
// If the toplevel has a parent or has a fixed size make it float
if (self.xdg_toplevel.parent != null or has_fixed_size) {
// If the self.xdg_toplevel has a parent or has a fixed size make it float
view.current.float = true;
view.pending.float = true;
view.pending.box = view.float_box;
@ -233,17 +230,17 @@ fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurfa
if (server.config.csdAllowed(view)) {
view.draw_borders = false;
} else {
_ = toplevel.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true });
_ = self.xdg_toplevel.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true });
}
view.map() catch {
log.err("out of memory", .{});
xdg_surface.resource.getClient().postNoMemory();
self.xdg_toplevel.resource.getClient().postNoMemory();
};
}
/// Called when the surface is unmapped and will no longer be displayed.
fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
fn handleUnmap(listener: *wl.Listener(void)) void {
const self = @fieldParentPtr(Self, "unmap", listener);
// Remove listeners that are only active while mapped
@ -274,9 +271,8 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
const self = @fieldParentPtr(Self, "commit", listener);
const view = self.view;
var wlr_box: wlr.Box = undefined;
self.xdg_surface.getGeometry(&wlr_box);
const new_box = Box.fromWlrBox(wlr_box);
var new_box: wlr.Box = undefined;
self.xdg_toplevel.base.getGeometry(&new_box);
// If we have sent a configure changing the size
if (view.pending_serial != null) {
@ -338,13 +334,10 @@ fn handleNewSubsurface(listener: *wl.Listener(*wlr.Subsurface), new_wlr_subsurfa
/// Called when the client asks to be fullscreened. We always honor the request
/// for now, perhaps it should be denied in some cases in the future.
fn handleRequestFullscreen(
listener: *wl.Listener(*wlr.XdgToplevel.event.SetFullscreen),
event: *wlr.XdgToplevel.event.SetFullscreen,
) void {
fn handleRequestFullscreen(listener: *wl.Listener(void)) void {
const self = @fieldParentPtr(Self, "request_fullscreen", listener);
if (self.view.pending.fullscreen != event.fullscreen) {
self.view.pending.fullscreen = event.fullscreen;
if (self.view.pending.fullscreen != self.xdg_toplevel.requested.fullscreen) {
self.view.pending.fullscreen = self.xdg_toplevel.requested.fullscreen;
self.view.applyPending();
}
}
@ -370,13 +363,13 @@ fn handleRequestResize(listener: *wl.Listener(*wlr.XdgToplevel.event.Resize), ev
}
/// Called when the client sets / updates its title
fn handleSetTitle(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
fn handleSetTitle(listener: *wl.Listener(void)) void {
const self = @fieldParentPtr(Self, "set_title", listener);
self.view.notifyTitle();
}
/// Called when the client sets / updates its app_id
fn handleSetAppId(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
fn handleSetAppId(listener: *wl.Listener(void)) void {
const self = @fieldParentPtr(Self, "set_app_id", listener);
self.view.notifyAppId();
}

View File

@ -25,7 +25,6 @@ const wl = @import("wayland").server.wl;
const server = &@import("main.zig").server;
const util = @import("util.zig");
const Box = @import("Box.zig");
const View = @import("View.zig");
const XwaylandView = @import("XwaylandView.zig");
const ViewStack = @import("view_stack.zig").ViewStack;

View File

@ -26,7 +26,6 @@ const wl = @import("wayland").server.wl;
const server = &@import("main.zig").server;
const util = @import("util.zig");
const Box = @import("Box.zig");
const Output = @import("Output.zig");
const View = @import("View.zig");
const ViewStack = @import("view_stack.zig").ViewStack;
@ -90,7 +89,8 @@ pub fn create(output: *Output, xwayland_surface: *wlr.XwaylandSurface) error{Out
pub fn needsConfigure(self: Self) bool {
const output = self.view.output;
const output_box = server.root.output_layout.getBox(output.wlr_output).?;
var output_box: wlr.Box = undefined;
server.root.output_layout.getBox(output.wlr_output, &output_box);
return self.xwayland_surface.x != self.view.pending.box.x + output_box.x or
self.xwayland_surface.y != self.view.pending.box.y + output_box.y or
self.xwayland_surface.width != self.view.pending.box.width or
@ -101,7 +101,8 @@ pub fn needsConfigure(self: Self) bool {
/// shouldTrackConfigure() is always false for xwayland views.
pub fn configure(self: Self) void {
const output = self.view.output;
const output_box = server.root.output_layout.getBox(output.wlr_output).?;
var output_box: wlr.Box = undefined;
server.root.output_layout.getBox(output.wlr_output, &output_box);
const state = &self.view.pending;
self.xwayland_surface.configure(
@ -161,22 +162,14 @@ pub fn getConstraints(self: Self) View.Constraints {
const hints = self.xwayland_surface.size_hints orelse return .{
.min_width = 1,
.min_height = 1,
.max_width = math.maxInt(u32),
.max_height = math.maxInt(u32),
.max_width = math.maxInt(u31),
.max_height = math.maxInt(u31),
};
return .{
.min_width = @intCast(u32, math.max(hints.min_width, 1)),
.min_height = @intCast(u32, math.max(hints.min_height, 1)),
.max_width = if (hints.max_width > 0)
@intCast(u32, hints.max_width)
else
math.maxInt(u32),
.max_height = if (hints.max_height > 0)
@intCast(u32, hints.max_height)
else
math.maxInt(u32),
.min_width = @intCast(u31, math.max(hints.min_width, 1)),
.min_height = @intCast(u31, math.max(hints.min_height, 1)),
.max_width = if (hints.max_width > 0) @intCast(u31, hints.max_width) else math.maxInt(u31),
.max_height = if (hints.max_height > 0) @intCast(u31, hints.max_height) else math.maxInt(u31),
};
}
@ -211,18 +204,18 @@ pub fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface:
self.view.surface_box = .{
.x = 0,
.y = 0,
.width = @intCast(u32, surface.current.width),
.height = @intCast(u32, surface.current.height),
.width = surface.current.width,
.height = surface.current.height,
};
// Use the view's "natural" size centered on the output as the default
// floating dimensions
view.float_box.width = self.xwayland_surface.width;
view.float_box.height = self.xwayland_surface.height;
view.float_box.x = math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.width) -
@intCast(i32, view.float_box.width), 2));
view.float_box.y = math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.height) -
@intCast(i32, view.float_box.height), 2));
view.float_box = .{
.x = @divTrunc(math.max(0, view.output.usable_box.width - self.xwayland_surface.width), 2),
.y = @divTrunc(math.max(0, view.output.usable_box.height - self.xwayland_surface.height), 2),
.width = self.xwayland_surface.width,
.height = self.xwayland_surface.height,
};
const has_fixed_size = if (self.xwayland_surface.size_hints) |size_hints|
size_hints.min_width != 0 and size_hints.min_height != 0 and
@ -312,8 +305,8 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) voi
self.view.surface_box = .{
.x = 0,
.y = 0,
.width = @intCast(u32, surface.current.width),
.height = @intCast(u32, surface.current.height),
.width = surface.current.width,
.height = surface.current.height,
};
}

View File

@ -32,7 +32,7 @@ pub fn borderWidth(
if (args.len < 2) return Error.NotEnoughArguments;
if (args.len > 2) return Error.TooManyArguments;
server.config.border_width = try fmt.parseInt(u32, args[1], 10);
server.config.border_width = try fmt.parseInt(u31, args[1], 10);
server.root.arrangeAll();
server.root.startTransaction();
}

View File

@ -120,7 +120,7 @@ fn csdFilterUpdateViews(kind: FilterKind, pattern: []const u8, operation: enum {
const view = @intToPtr(*View, xdg_toplevel_decoration.surface.data);
if (viewMatchesPattern(kind, pattern, view)) {
const toplevel = view.impl.xdg_toplevel.xdg_surface.role_data.toplevel;
const toplevel = view.impl.xdg_toplevel.xdg_toplevel;
switch (operation) {
.add => {
_ = xdg_toplevel_decoration.setMode(.client_side);

View File

@ -24,7 +24,6 @@ const PhysicalDirection = @import("../command.zig").PhysicalDirection;
const Orientation = @import("../command.zig").Orientation;
const Seat = @import("../Seat.zig");
const View = @import("../View.zig");
const Box = @import("../Box.zig");
pub fn move(
seat: *Seat,
@ -61,15 +60,15 @@ pub fn snap(
return Error.InvalidPhysicalDirection;
const view = getView(seat) orelse return;
const border_width = @intCast(i32, server.config.border_width);
const output_box = view.output.getEffectiveResolution();
const border_width = server.config.border_width;
var output_width: i32 = undefined;
var output_height: i32 = undefined;
view.output.wlr_output.effectiveResolution(&output_width, &output_height);
switch (direction) {
.up => view.pending.box.y = border_width,
.down => view.pending.box.y =
@intCast(i32, output_box.height - view.pending.box.height) - border_width,
.down => view.pending.box.y = output_width - view.pending.box.height - border_width,
.left => view.pending.box.x = border_width,
.right => view.pending.box.x =
@intCast(i32, output_box.width - view.pending.box.width) - border_width,
.right => view.pending.box.x = output_height - view.pending.box.width - border_width,
}
apply(view);
@ -88,44 +87,27 @@ pub fn resize(
return Error.InvalidOrientation;
const view = getView(seat) orelse return;
const border_width = @intCast(i32, server.config.border_width);
const output_box = view.output.getEffectiveResolution();
var output_width: i32 = undefined;
var output_height: i32 = undefined;
view.output.wlr_output.effectiveResolution(&output_width, &output_height);
switch (orientation) {
.horizontal => {
var real_delta: i32 = @intCast(i32, view.pending.box.width);
if (delta > 0) {
view.pending.box.width += @intCast(u32, delta);
} else {
// Prevent underflow
view.pending.box.width -=
math.min(view.pending.box.width, @intCast(u32, -1 * delta));
}
view.pending.box.width += delta;
view.applyConstraints();
// Do not grow bigger than the output
view.pending.box.width = math.min(
view.pending.box.width,
output_box.width - @intCast(u32, 2 * border_width),
output_width - 2 * server.config.border_width,
);
real_delta -= @intCast(i32, view.pending.box.width);
view.move(@divFloor(real_delta, 2), 0);
},
.vertical => {
var real_delta: i32 = @intCast(i32, view.pending.box.height);
if (delta > 0) {
view.pending.box.height += @intCast(u32, delta);
} else {
// Prevent underflow
view.pending.box.height -=
math.min(view.pending.box.height, @intCast(u32, -1 * delta));
}
view.pending.box.height += delta;
view.applyConstraints();
// Do not grow bigger than the output
view.pending.box.height = math.min(
view.pending.box.height,
output_box.height - @intCast(u32, 2 * border_width),
output_height - 2 * server.config.border_width,
);
real_delta -= @intCast(i32, view.pending.box.height);
view.move(0, @divFloor(real_delta, 2));
},
}

View File

@ -86,12 +86,15 @@ fn getOutput(seat: *Seat, str: []const u8) !?*Output {
.previous => if (focused_node.prev) |node| &node.data else &server.root.outputs.last.?.data,
};
} else if (std.meta.stringToEnum(wlr.OutputLayout.Direction, str)) |direction| { // Spacial direction
const focus_box = server.root.output_layout.getBox(seat.focused_output.wlr_output) orelse return null;
var focus_box: wlr.Box = undefined;
server.root.output_layout.getBox(seat.focused_output.wlr_output, &focus_box);
if (focus_box.empty()) return null;
const wlr_output = server.root.output_layout.adjacentOutput(
direction,
seat.focused_output.wlr_output,
@intToFloat(f64, focus_box.x + @divFloor(focus_box.width, 2)),
@intToFloat(f64, focus_box.y + @divFloor(focus_box.height, 2)),
@intToFloat(f64, focus_box.x + @divTrunc(focus_box.width, 2)),
@intToFloat(f64, focus_box.y + @divTrunc(focus_box.height, 2)),
) orelse return null;
return @intToPtr(*Output, wlr_output.data);
} else {

View File

@ -39,7 +39,7 @@ pub fn setRepeat(
var it = server.input_manager.devices.iterator(.forward);
while (it.next()) |device| {
if (device.wlr_device.type == .keyboard) {
device.wlr_device.device.keyboard.setRepeatInfo(rate, delay);
device.wlr_device.toKeyboard().setRepeatInfo(rate, delay);
}
}
}

View File

@ -18,7 +18,6 @@ const std = @import("std");
const server = &@import("../main.zig").server;
const Box = @import("../Box.zig");
const Error = @import("../command.zig").Error;
const Seat = @import("../Seat.zig");

View File

@ -24,7 +24,6 @@ const pixman = @import("pixman");
const server = &@import("main.zig").server;
const util = @import("util.zig");
const Box = @import("Box.zig");
const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig");
const Server = @import("Server.zig");
@ -201,8 +200,8 @@ fn renderView(output: *const Output, view: *View, now: *os.timespec) void {
.{
.x = saved_buffer.surface_box.x + view.current.box.x - view.saved_surface_box.x,
.y = saved_buffer.surface_box.y + view.current.box.y - view.saved_surface_box.y,
.width = @intCast(c_int, saved_buffer.surface_box.width),
.height = @intCast(c_int, saved_buffer.surface_box.height),
.width = saved_buffer.surface_box.width,
.height = saved_buffer.surface_box.height,
},
&saved_buffer.source_box,
saved_buffer.transform,
@ -223,7 +222,8 @@ fn renderView(output: *const Output, view: *View, now: *os.timespec) void {
}
fn renderDragIcons(output: *const Output, now: *os.timespec) void {
const output_box = server.root.output_layout.getBox(output.wlr_output).?;
var output_box: wlr.Box = undefined;
server.root.output_layout.getBox(output.wlr_output, &output_box);
var it = server.root.drag_icons.first;
while (it) |node| : (it = node.next) {
@ -243,7 +243,8 @@ fn renderDragIcons(output: *const Output, now: *os.timespec) void {
/// Render all override redirect xwayland windows that appear on the output
fn renderXwaylandOverrideRedirect(output: *const Output, now: *os.timespec) void {
const output_box = server.root.output_layout.getBox(output.wlr_output).?;
var output_box: wlr.Box = undefined;
server.root.output_layout.getBox(output.wlr_output, &output_box);
var it = server.root.xwayland_override_redirect_views.last;
while (it) |node| : (it = node.prev) {
@ -321,42 +322,41 @@ fn renderBorders(output: *const Output, view: *View) void {
if (view.current.focus != 0) break :blk &config.border_color_focused;
break :blk &config.border_color_unfocused;
};
const border_width = config.border_width;
const actual_box = if (view.saved_buffers.items.len != 0) view.saved_surface_box else view.surface_box;
var border: Box = undefined;
var border: wlr.Box = undefined;
// left and right, covering the corners as well
border.y = view.current.box.y - @intCast(i32, border_width);
border.width = border_width;
border.height = actual_box.height + border_width * 2;
border.y = view.current.box.y - config.border_width;
border.width = config.border_width;
border.height = actual_box.height + config.border_width * 2;
// left
border.x = view.current.box.x - @intCast(i32, border_width);
border.x = view.current.box.x - config.border_width;
renderRect(output, border, color);
// right
border.x = view.current.box.x + @intCast(i32, actual_box.width);
border.x = view.current.box.x + actual_box.width;
renderRect(output, border, color);
// top and bottom
border.x = view.current.box.x;
border.width = actual_box.width;
border.height = border_width;
border.height = config.border_width;
// top
border.y = view.current.box.y - @intCast(i32, border_width);
border.y = view.current.box.y - config.border_width;
renderRect(output, border, color);
// bottom border
border.y = view.current.box.y + @intCast(i32, actual_box.height);
border.y = view.current.box.y + actual_box.height;
renderRect(output, border, color);
}
fn renderRect(output: *const Output, box: Box, color: *const [4]f32) void {
var wlr_box = box.toWlrBox();
scaleBox(&wlr_box, output.wlr_output.scale);
server.renderer.renderRect(&wlr_box, color, &output.wlr_output.transform_matrix);
fn renderRect(output: *const Output, box: wlr.Box, color: *const [4]f32) void {
var scaled = box;
scaleBox(&scaled, output.wlr_output.scale);
server.renderer.renderRect(&scaled, color, &output.wlr_output.transform_matrix);
}
/// Scale a wlr_box, taking the possibility of fractional scaling into account.