river-status: implement protocol
This commit is contained in:
parent
e8aaadb228
commit
5aa7fe8af8
@ -80,6 +80,7 @@ fn addProtocolDeps(exe: *std.build.LibExeObjStep, protocol_step: *std.build.Step
|
|||||||
exe.step.dependOn(protocol_step);
|
exe.step.dependOn(protocol_step);
|
||||||
exe.addIncludeDir("protocol");
|
exe.addIncludeDir("protocol");
|
||||||
exe.addCSourceFile("protocol/river-control-unstable-v1-protocol.c", &[_][]const u8{"-std=c99"});
|
exe.addCSourceFile("protocol/river-control-unstable-v1-protocol.c", &[_][]const u8{"-std=c99"});
|
||||||
|
exe.addCSourceFile("protocol/river-status-unstable-v1-protocol.c", &[_][]const u8{"-std=c99"});
|
||||||
}
|
}
|
||||||
|
|
||||||
const ScanProtocolsStep = struct {
|
const ScanProtocolsStep = struct {
|
||||||
|
@ -44,7 +44,7 @@ pub fn init(self: *Self, server: *Server) !void {
|
|||||||
protocol_version,
|
protocol_version,
|
||||||
self,
|
self,
|
||||||
bind,
|
bind,
|
||||||
) orelse return error.CantCreateRiverWindowManagementGlobal;
|
) orelse return error.CantCreateWlGlobal;
|
||||||
|
|
||||||
self.listen_display_destroy.notify = handleDisplayDestroy;
|
self.listen_display_destroy.notify = handleDisplayDestroy;
|
||||||
c.wl_display_add_destroy_listener(server.wl_display, &self.listen_display_destroy);
|
c.wl_display_add_destroy_listener(server.wl_display, &self.listen_display_destroy);
|
||||||
@ -67,11 +67,7 @@ fn bind(wl_client: ?*c.wl_client, data: ?*c_void, version: u32, id: u32) callcon
|
|||||||
c.wl_client_post_no_memory(wl_client);
|
c.wl_client_post_no_memory(wl_client);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
c.wl_resource_set_implementation(wl_resource, &implementation, self, resourceDestroy);
|
c.wl_resource_set_implementation(wl_resource, &implementation, self, null);
|
||||||
}
|
|
||||||
|
|
||||||
fn resourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
|
|
||||||
// TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runCommand(
|
fn runCommand(
|
||||||
|
@ -28,6 +28,7 @@ const Log = @import("log.zig").Log;
|
|||||||
const Root = @import("Root.zig");
|
const Root = @import("Root.zig");
|
||||||
const View = @import("View.zig");
|
const View = @import("View.zig");
|
||||||
const ViewStack = @import("view_stack.zig").ViewStack;
|
const ViewStack = @import("view_stack.zig").ViewStack;
|
||||||
|
const OutputStatus = @import("OutputStatus.zig");
|
||||||
|
|
||||||
root: *Root,
|
root: *Root,
|
||||||
wlr_output: *c.wlr_output,
|
wlr_output: *c.wlr_output,
|
||||||
@ -55,6 +56,9 @@ master_factor: f64,
|
|||||||
/// Current layout of the output.
|
/// Current layout of the output.
|
||||||
layout: Layout,
|
layout: Layout,
|
||||||
|
|
||||||
|
/// List of status tracking objects relaying changes to this output to clients.
|
||||||
|
status_trackers: std.SinglyLinkedList(OutputStatus),
|
||||||
|
|
||||||
// All listeners for this output, in alphabetical order
|
// All listeners for this output, in alphabetical order
|
||||||
listen_destroy: c.wl_listener,
|
listen_destroy: c.wl_listener,
|
||||||
listen_frame: c.wl_listener,
|
listen_frame: c.wl_listener,
|
||||||
@ -133,6 +137,8 @@ pub fn init(self: *Self, root: *Root, wlr_output: *c.wlr_output) !void {
|
|||||||
// LeftMaster is the default layout for all outputs
|
// LeftMaster is the default layout for all outputs
|
||||||
self.layout = Layout.LeftMaster;
|
self.layout = Layout.LeftMaster;
|
||||||
|
|
||||||
|
self.status_trackers = std.SinglyLinkedList(OutputStatus).init();
|
||||||
|
|
||||||
// Set up listeners
|
// Set up listeners
|
||||||
self.listen_destroy.notify = handleDestroy;
|
self.listen_destroy.notify = handleDestroy;
|
||||||
c.wl_signal_add(&wlr_output.events.destroy, &self.listen_destroy);
|
c.wl_signal_add(&wlr_output.events.destroy, &self.listen_destroy);
|
||||||
@ -170,20 +176,6 @@ pub fn init(self: *Self, root: *Root, wlr_output: *c.wlr_output) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
|
||||||
for (self.layers) |*layer| {
|
|
||||||
while (layer.pop()) |layer_surface_node| {
|
|
||||||
self.root.server.allocator.destroy(layer_surface_node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (self.views.first) |node| {
|
|
||||||
node.view.deinit();
|
|
||||||
self.views.remove(node);
|
|
||||||
self.root.server.allocator.destroy(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getRenderer(self: Self) *c.wlr_renderer {
|
pub fn getRenderer(self: Self) *c.wlr_renderer {
|
||||||
return c.river_wlr_backend_get_renderer(self.wlr_output.backend);
|
return c.river_wlr_backend_get_renderer(self.wlr_output.backend);
|
||||||
}
|
}
|
||||||
@ -708,7 +700,7 @@ fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
|||||||
while (seat_it) |seat_node| : (seat_it = seat_node.next) {
|
while (seat_it) |seat_node| : (seat_it = seat_node.next) {
|
||||||
const seat = &seat_node.data;
|
const seat = &seat_node.data;
|
||||||
if (seat.focused_output == self) {
|
if (seat.focused_output == self) {
|
||||||
seat.focused_output = fallback_output;
|
seat.focusOutput(self);
|
||||||
seat.focus(null);
|
seat.focus(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
77
river/OutputStatus.zig
Normal file
77
river/OutputStatus.zig
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// This file is part of river, a dynamic tiling wayland compositor.
|
||||||
|
//
|
||||||
|
// Copyright 2020 Isaac Freund
|
||||||
|
//
|
||||||
|
// 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, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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 std = @import("std");
|
||||||
|
|
||||||
|
const c = @import("c.zig");
|
||||||
|
|
||||||
|
const Log = @import("log.zig").Log;
|
||||||
|
const Output = @import("Output.zig");
|
||||||
|
const View = @import("View.zig");
|
||||||
|
const ViewStack = @import("view_stack.zig").ViewStack;
|
||||||
|
|
||||||
|
const implementation = c.struct_zriver_output_status_v1_interface{
|
||||||
|
.destroy = destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
output: *Output,
|
||||||
|
wl_resource: *c.wl_resource,
|
||||||
|
|
||||||
|
pub fn init(self: *Self, output: *Output, wl_resource: *c.wl_resource) void {
|
||||||
|
self.output = output;
|
||||||
|
self.wl_resource = wl_resource;
|
||||||
|
|
||||||
|
c.wl_resource_set_implementation(wl_resource, &implementation, self, handleResourceDestroy);
|
||||||
|
|
||||||
|
// Send view/focused tags once on bind.
|
||||||
|
self.sendViewTags();
|
||||||
|
self.sendFocusedTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleResourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
|
||||||
|
const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
|
||||||
|
const node = @fieldParentPtr(std.SinglyLinkedList(Self).Node, "data", self);
|
||||||
|
self.output.status_trackers.remove(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource) callconv(.C) void {
|
||||||
|
c.wl_resource_destroy(wl_resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send the current tags of each view on the output to the client.
|
||||||
|
pub fn sendViewTags(self: Self) void {
|
||||||
|
var view_tags: c.wl_array = undefined;
|
||||||
|
c.wl_array_init(&view_tags);
|
||||||
|
var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32));
|
||||||
|
while (it.next()) |node| {
|
||||||
|
const ptr = c.wl_array_add(&view_tags, @sizeOf(u32)) orelse {
|
||||||
|
c.wl_resource_post_no_memory(self.wl_resource);
|
||||||
|
Log.Error.log("out of memory", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const ptr_u32 = @ptrCast(*u32, @alignCast(@alignOf(u32), ptr));
|
||||||
|
ptr_u32.* = node.view.current_tags;
|
||||||
|
}
|
||||||
|
c.zriver_output_status_v1_send_view_tags(self.wl_resource, &view_tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send the currently focused tags of the output to the client.
|
||||||
|
pub fn sendFocusedTags(self: Self) void {
|
||||||
|
c.zriver_output_status_v1_send_focused_tags(self.wl_resource, self.output.current_focused_tags);
|
||||||
|
}
|
@ -104,7 +104,7 @@ pub fn addOutput(self: *Self, wlr_output: *c.wlr_output) void {
|
|||||||
// TODO: move views from the noop output to the new one and focus(null)
|
// TODO: move views from the noop output to the new one and focus(null)
|
||||||
var it = self.server.input_manager.seats.first;
|
var it = self.server.input_manager.seats.first;
|
||||||
while (it) |seat_node| : (it = seat_node.next) {
|
while (it) |seat_node| : (it = seat_node.next) {
|
||||||
seat_node.data.focused_output = &self.outputs.first.?.data;
|
seat_node.data.focusOutput(&self.outputs.first.?.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,8 +220,12 @@ fn commitTransaction(self: *Self) void {
|
|||||||
);
|
);
|
||||||
output.current_focused_tags = tags;
|
output.current_focused_tags = tags;
|
||||||
output.pending_focused_tags = null;
|
output.pending_focused_tags = null;
|
||||||
|
var it = output.status_trackers.first;
|
||||||
|
while (it) |node| : (it = node.next) node.data.sendFocusedTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var view_tags_changed = false;
|
||||||
|
|
||||||
var view_it = ViewStack(View).iterator(output.views.first, std.math.maxInt(u32));
|
var view_it = ViewStack(View).iterator(output.views.first, std.math.maxInt(u32));
|
||||||
while (view_it.next()) |view_node| {
|
while (view_it.next()) |view_node| {
|
||||||
const view = &view_node.view;
|
const view = &view_node.view;
|
||||||
@ -236,10 +240,16 @@ fn commitTransaction(self: *Self) void {
|
|||||||
if (view.pending_tags) |tags| {
|
if (view.pending_tags) |tags| {
|
||||||
view.current_tags = tags;
|
view.current_tags = tags;
|
||||||
view.pending_tags = null;
|
view.pending_tags = null;
|
||||||
|
view_tags_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
view.dropStashedBuffer();
|
view.dropStashedBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (view_tags_changed) {
|
||||||
|
var it = output.status_trackers.first;
|
||||||
|
while (it) |node| : (it = node.next) node.data.sendViewTags();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over all seats and update focus
|
// Iterate over all seats and update focus
|
||||||
|
@ -27,9 +27,12 @@ const InputManager = @import("InputManager.zig");
|
|||||||
const Keyboard = @import("Keyboard.zig");
|
const Keyboard = @import("Keyboard.zig");
|
||||||
const LayerSurface = @import("LayerSurface.zig");
|
const LayerSurface = @import("LayerSurface.zig");
|
||||||
const Output = @import("Output.zig");
|
const Output = @import("Output.zig");
|
||||||
|
const SeatStatus = @import("SeatStatus.zig");
|
||||||
const View = @import("View.zig");
|
const View = @import("View.zig");
|
||||||
const ViewStack = @import("view_stack.zig").ViewStack;
|
const ViewStack = @import("view_stack.zig").ViewStack;
|
||||||
|
|
||||||
|
// TODO: remove none variant, unify focused_view and focused_layer fields
|
||||||
|
// with type ?FocusTarget
|
||||||
const FocusTarget = union(enum) {
|
const FocusTarget = union(enum) {
|
||||||
view: *View,
|
view: *View,
|
||||||
layer: *LayerSurface,
|
layer: *LayerSurface,
|
||||||
@ -62,6 +65,9 @@ focus_stack: ViewStack(*View),
|
|||||||
/// recieve focus.
|
/// recieve focus.
|
||||||
focused_layer: ?*LayerSurface,
|
focused_layer: ?*LayerSurface,
|
||||||
|
|
||||||
|
/// List of status tracking objects relaying changes to this seat to clients.
|
||||||
|
status_trackers: std.SinglyLinkedList(SeatStatus),
|
||||||
|
|
||||||
listen_request_set_selection: c.wl_listener,
|
listen_request_set_selection: c.wl_listener,
|
||||||
|
|
||||||
pub fn init(self: *Self, input_manager: *InputManager, name: []const u8) !void {
|
pub fn init(self: *Self, input_manager: *InputManager, name: []const u8) !void {
|
||||||
@ -70,6 +76,7 @@ pub fn init(self: *Self, input_manager: *InputManager, name: []const u8) !void {
|
|||||||
// This will be automatically destroyed when the display is destroyed
|
// This will be automatically destroyed when the display is destroyed
|
||||||
self.wlr_seat = c.wlr_seat_create(input_manager.server.wl_display, name.ptr) orelse
|
self.wlr_seat = c.wlr_seat_create(input_manager.server.wl_display, name.ptr) orelse
|
||||||
return error.CantCreateWlrSeat;
|
return error.CantCreateWlrSeat;
|
||||||
|
self.wlr_seat.data = self;
|
||||||
|
|
||||||
try self.cursor.init(self);
|
try self.cursor.init(self);
|
||||||
errdefer self.cursor.destroy();
|
errdefer self.cursor.destroy();
|
||||||
@ -86,6 +93,8 @@ pub fn init(self: *Self, input_manager: *InputManager, name: []const u8) !void {
|
|||||||
|
|
||||||
self.focused_layer = null;
|
self.focused_layer = null;
|
||||||
|
|
||||||
|
self.status_trackers = std.SinglyLinkedList(SeatStatus).init();
|
||||||
|
|
||||||
self.listen_request_set_selection.notify = handleRequestSetSelection;
|
self.listen_request_set_selection.notify = handleRequestSetSelection;
|
||||||
c.wl_signal_add(&self.wlr_seat.events.request_set_selection, &self.listen_request_set_selection);
|
c.wl_signal_add(&self.wlr_seat.events.request_set_selection, &self.listen_request_set_selection);
|
||||||
}
|
}
|
||||||
@ -167,9 +176,7 @@ pub fn setFocusRaw(self: *Self, focus_target: FocusTarget) void {
|
|||||||
.view => |target_view| target_view == self.focused_view,
|
.view => |target_view| target_view == self.focused_view,
|
||||||
.layer => |target_layer| target_layer == self.focused_layer,
|
.layer => |target_layer| target_layer == self.focused_layer,
|
||||||
.none => false,
|
.none => false,
|
||||||
}) {
|
}) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtain the target wlr_surface
|
// Obtain the target wlr_surface
|
||||||
const target_wlr_surface = switch (focus_target) {
|
const target_wlr_surface = switch (focus_target) {
|
||||||
@ -223,6 +230,23 @@ pub fn setFocusRaw(self: *Self, focus_target: FocusTarget) void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inform any clients tracking status of the change
|
||||||
|
var it = self.status_trackers.first;
|
||||||
|
while (it) |node| : (it = node.next) node.data.sendFocusedView();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Focus the given output, notifying any listening clients of the change.
|
||||||
|
pub fn focusOutput(self: *Self, output: *Output) void {
|
||||||
|
const root = &self.input_manager.server.root;
|
||||||
|
|
||||||
|
var it = self.status_trackers.first;
|
||||||
|
while (it) |node| : (it = node.next) node.data.sendOutput(.unfocused);
|
||||||
|
|
||||||
|
self.focused_output = output;
|
||||||
|
|
||||||
|
it = self.status_trackers.first;
|
||||||
|
while (it) |node| : (it = node.next) node.data.sendOutput(.focused);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle the unmapping of a view, removing it from the focus stack and
|
/// Handle the unmapping of a view, removing it from the focus stack and
|
||||||
|
81
river/SeatStatus.zig
Normal file
81
river/SeatStatus.zig
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// This file is part of river, a dynamic tiling wayland compositor.
|
||||||
|
//
|
||||||
|
// Copyright 2020 Isaac Freund
|
||||||
|
//
|
||||||
|
// 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, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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 std = @import("std");
|
||||||
|
|
||||||
|
const c = @import("c.zig");
|
||||||
|
|
||||||
|
const Log = @import("log.zig").Log;
|
||||||
|
const Seat = @import("Seat.zig");
|
||||||
|
const Output = @import("Output.zig");
|
||||||
|
const View = @import("View.zig");
|
||||||
|
|
||||||
|
const FocusState = enum {
|
||||||
|
focused,
|
||||||
|
unfocused,
|
||||||
|
};
|
||||||
|
|
||||||
|
const implementation = c.struct_zriver_seat_status_v1_interface{
|
||||||
|
.destroy = destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
seat: *Seat,
|
||||||
|
wl_resource: *c.wl_resource,
|
||||||
|
|
||||||
|
pub fn init(self: *Self, seat: *Seat, wl_resource: *c.wl_resource) void {
|
||||||
|
self.seat = seat;
|
||||||
|
self.wl_resource = wl_resource;
|
||||||
|
|
||||||
|
c.wl_resource_set_implementation(wl_resource, &implementation, self, handleResourceDestroy);
|
||||||
|
|
||||||
|
// Send focused output/view once on bind
|
||||||
|
self.sendOutput(.focused);
|
||||||
|
self.sendFocusedView();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleResourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
|
||||||
|
const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
|
||||||
|
const node = @fieldParentPtr(std.SinglyLinkedList(Self).Node, "data", self);
|
||||||
|
self.seat.status_trackers.remove(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource) callconv(.C) void {
|
||||||
|
c.wl_resource_destroy(wl_resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sendOutput(self: Self, state: FocusState) void {
|
||||||
|
const wl_client = c.wl_resource_get_client(self.wl_resource);
|
||||||
|
const output_resources = &self.seat.focused_output.wlr_output.resources;
|
||||||
|
var output_resource = c.wl_resource_from_link(output_resources.next);
|
||||||
|
while (c.wl_resource_get_link(output_resource) != output_resources) : (output_resource =
|
||||||
|
c.wl_resource_from_link(c.wl_resource_get_link(output_resource).*.next))
|
||||||
|
{
|
||||||
|
if (c.wl_resource_get_client(output_resource) == wl_client) switch (state) {
|
||||||
|
.focused => c.zriver_seat_status_v1_send_focused_output(self.wl_resource, output_resource),
|
||||||
|
.unfocused => c.zriver_seat_status_v1_send_unfocused_output(self.wl_resource, output_resource),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sendFocusedView(self: Self) void {
|
||||||
|
c.zriver_seat_status_v1_send_focused_view(self.wl_resource, if (self.seat.focused_view) |v|
|
||||||
|
v.getTitle()
|
||||||
|
else
|
||||||
|
"");
|
||||||
|
}
|
@ -23,15 +23,16 @@ const std = @import("std");
|
|||||||
const c = @import("c.zig");
|
const c = @import("c.zig");
|
||||||
|
|
||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
|
const Control = @import("Control.zig");
|
||||||
const DecorationManager = @import("DecorationManager.zig");
|
const DecorationManager = @import("DecorationManager.zig");
|
||||||
const InputManager = @import("InputManager.zig");
|
const InputManager = @import("InputManager.zig");
|
||||||
const LayerSurface = @import("LayerSurface.zig");
|
const LayerSurface = @import("LayerSurface.zig");
|
||||||
const Log = @import("log.zig").Log;
|
const Log = @import("log.zig").Log;
|
||||||
const Output = @import("Output.zig");
|
const Output = @import("Output.zig");
|
||||||
const Root = @import("Root.zig");
|
const Root = @import("Root.zig");
|
||||||
|
const StatusManager = @import("StatusManager.zig");
|
||||||
const View = @import("View.zig");
|
const View = @import("View.zig");
|
||||||
const ViewStack = @import("view_stack.zig").ViewStack;
|
const ViewStack = @import("view_stack.zig").ViewStack;
|
||||||
const Control = @import("Control.zig");
|
|
||||||
const XwaylandUnmanaged = @import("XwaylandUnmanaged.zig");
|
const XwaylandUnmanaged = @import("XwaylandUnmanaged.zig");
|
||||||
|
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
@ -57,6 +58,7 @@ input_manager: InputManager,
|
|||||||
root: Root,
|
root: Root,
|
||||||
config: Config,
|
config: Config,
|
||||||
control: Control,
|
control: Control,
|
||||||
|
status_manager: StatusManager,
|
||||||
|
|
||||||
pub fn init(self: *Self, allocator: *std.mem.Allocator) !void {
|
pub fn init(self: *Self, allocator: *std.mem.Allocator) !void {
|
||||||
self.allocator = allocator;
|
self.allocator = allocator;
|
||||||
@ -123,6 +125,7 @@ pub fn init(self: *Self, allocator: *std.mem.Allocator) !void {
|
|||||||
// Must be called after root is initialized
|
// Must be called after root is initialized
|
||||||
try self.input_manager.init(self);
|
try self.input_manager.init(self);
|
||||||
try self.control.init(self);
|
try self.control.init(self);
|
||||||
|
try self.status_manager.init(self);
|
||||||
|
|
||||||
// These all free themselves when the wl_display is destroyed
|
// These all free themselves when the wl_display is destroyed
|
||||||
_ = c.wlr_data_device_manager_create(self.wl_display) orelse
|
_ = c.wlr_data_device_manager_create(self.wl_display) orelse
|
||||||
|
146
river/StatusManager.zig
Normal file
146
river/StatusManager.zig
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// This file is part of river, a dynamic tiling wayland compositor.
|
||||||
|
//
|
||||||
|
// Copyright 2020 Isaac Freund
|
||||||
|
//
|
||||||
|
// 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, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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 std = @import("std");
|
||||||
|
|
||||||
|
const c = @import("c.zig");
|
||||||
|
|
||||||
|
const Log = @import("log.zig").Log;
|
||||||
|
const Output = @import("Output.zig");
|
||||||
|
const OutputStatus = @import("OutputStatus.zig");
|
||||||
|
const Seat = @import("Seat.zig");
|
||||||
|
const SeatStatus = @import("SeatStatus.zig");
|
||||||
|
const Server = @import("Server.zig");
|
||||||
|
|
||||||
|
const protocol_version = 1;
|
||||||
|
|
||||||
|
const implementation = c.struct_zriver_status_manager_v1_interface{
|
||||||
|
.destroy = destroy,
|
||||||
|
.get_river_output_status = getRiverOutputStatus,
|
||||||
|
.get_river_seat_status = getRiverSeatStatus,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: remove this field, move allocator to util or something
|
||||||
|
server: *Server,
|
||||||
|
wl_global: *c.wl_global,
|
||||||
|
|
||||||
|
listen_display_destroy: c.wl_listener,
|
||||||
|
|
||||||
|
pub fn init(self: *Self, server: *Server) !void {
|
||||||
|
self.server = server;
|
||||||
|
self.wl_global = c.wl_global_create(
|
||||||
|
server.wl_display,
|
||||||
|
&c.zriver_status_manager_v1_interface,
|
||||||
|
protocol_version,
|
||||||
|
self,
|
||||||
|
bind,
|
||||||
|
) orelse return error.CantCreateWlGlobal;
|
||||||
|
|
||||||
|
self.listen_display_destroy.notify = handleDisplayDestroy;
|
||||||
|
c.wl_display_add_destroy_listener(server.wl_display, &self.listen_display_destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleDisplayDestroy(wl_listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
|
const self = @fieldParentPtr(Self, "listen_display_destroy", wl_listener.?);
|
||||||
|
c.wl_global_destroy(self.wl_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when a client binds our global
|
||||||
|
fn bind(wl_client: ?*c.wl_client, data: ?*c_void, version: u32, id: u32) callconv(.C) void {
|
||||||
|
const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), data));
|
||||||
|
const wl_resource = c.wl_resource_create(
|
||||||
|
wl_client,
|
||||||
|
&c.zriver_status_manager_v1_interface,
|
||||||
|
@intCast(c_int, version),
|
||||||
|
id,
|
||||||
|
) orelse {
|
||||||
|
c.wl_client_post_no_memory(wl_client);
|
||||||
|
Log.Error.log("out of memory\n", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
c.wl_resource_set_implementation(wl_resource, &implementation, self, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource) callconv(.C) void {}
|
||||||
|
|
||||||
|
fn getRiverOutputStatus(
|
||||||
|
wl_client: ?*c.wl_client,
|
||||||
|
wl_resource: ?*c.wl_resource,
|
||||||
|
new_id: u32,
|
||||||
|
output_wl_resource: ?*c.wl_resource,
|
||||||
|
) callconv(.C) void {
|
||||||
|
const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
|
||||||
|
// This can be null if the output is inert, in which case we ignore the request
|
||||||
|
const wlr_output = c.wlr_output_from_resource(output_wl_resource) orelse return;
|
||||||
|
const output = @ptrCast(*Output, @alignCast(@alignOf(*Output), wlr_output.*.data));
|
||||||
|
const allocator = self.server.allocator;
|
||||||
|
|
||||||
|
const node = allocator.create(std.SinglyLinkedList(OutputStatus).Node) catch {
|
||||||
|
c.wl_client_post_no_memory(wl_client);
|
||||||
|
Log.Error.log("out of memory\n", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const output_status_resource = c.wl_resource_create(
|
||||||
|
wl_client,
|
||||||
|
&c.zriver_output_status_v1_interface,
|
||||||
|
protocol_version,
|
||||||
|
new_id,
|
||||||
|
) orelse {
|
||||||
|
c.wl_client_post_no_memory(wl_client);
|
||||||
|
Log.Error.log("out of memory\n", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
node.data.init(output, output_status_resource);
|
||||||
|
output.status_trackers.prepend(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getRiverSeatStatus(
|
||||||
|
wl_client: ?*c.wl_client,
|
||||||
|
wl_resource: ?*c.wl_resource,
|
||||||
|
new_id: u32,
|
||||||
|
seat_wl_resource: ?*c.wl_resource,
|
||||||
|
) callconv(.C) void {
|
||||||
|
const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
|
||||||
|
// This can be null if the seat is inert, in which case we ignore the request
|
||||||
|
const wlr_seat_client = c.wlr_seat_client_from_resource(wl_resource) orelse return;
|
||||||
|
const seat = @ptrCast(*Seat, @alignCast(@alignOf(*Seat), wlr_seat_client.*.seat.*.data));
|
||||||
|
const allocator = self.server.allocator;
|
||||||
|
|
||||||
|
const node = allocator.create(std.SinglyLinkedList(SeatStatus).Node) catch {
|
||||||
|
c.wl_client_post_no_memory(wl_client);
|
||||||
|
Log.Error.log("out of memory\n", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const seat_status_resource = c.wl_resource_create(
|
||||||
|
wl_client,
|
||||||
|
&c.zriver_seat_status_v1_interface,
|
||||||
|
protocol_version,
|
||||||
|
new_id,
|
||||||
|
) orelse {
|
||||||
|
c.wl_client_post_no_memory(wl_client);
|
||||||
|
Log.Error.log("out of memory\n", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
node.data.init(seat, seat_status_resource);
|
||||||
|
seat.status_trackers.prepend(node);
|
||||||
|
}
|
@ -223,6 +223,14 @@ pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surfa
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the current title of the view. May be an empty string.
|
||||||
|
pub fn getTitle(self: Self) [*:0]const u8 {
|
||||||
|
return switch (self.impl) {
|
||||||
|
.xdg_toplevel => |xdg_toplevel| xdg_toplevel.getTitle(),
|
||||||
|
.xwayland_view => |xwayland_view| xwayland_view.getTitle(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Called by the impl when the surface is ready to be displayed
|
/// Called by the impl when the surface is ready to be displayed
|
||||||
pub fn map(self: *Self) void {
|
pub fn map(self: *Self) void {
|
||||||
const root = self.output.root;
|
const root = self.output.root;
|
||||||
|
@ -46,3 +46,7 @@ pub fn forEachSurface(
|
|||||||
pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
|
pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getTitle(self: Self) [*:0]const u8 {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
@ -100,6 +100,15 @@ pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surfa
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the current title of the toplevel. May be an empty string.
|
||||||
|
pub fn getTitle(self: Self) [*:0]const u8 {
|
||||||
|
const wlr_xdg_toplevel: *c.wlr_xdg_toplevel = @field(
|
||||||
|
self.wlr_xdg_surface,
|
||||||
|
c.wlr_xdg_surface_union,
|
||||||
|
).toplevel;
|
||||||
|
return wlr_xdg_toplevel.title;
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when the xdg surface is destroyed
|
/// Called when the xdg surface is destroyed
|
||||||
fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
|
const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
|
||||||
|
@ -105,6 +105,11 @@ pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surfa
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current title of the xwayland surface. May be an empty string
|
||||||
|
pub fn getTitle(self: Self) [*:0]const u8 {
|
||||||
|
return self.wlr_xwayland_surface.title;
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when the xwayland surface is destroyed
|
/// Called when the xwayland surface is destroyed
|
||||||
fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
|
const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
|
||||||
|
@ -51,6 +51,7 @@ pub usingnamespace @cImport({
|
|||||||
@cInclude("include/bindings.h");
|
@cInclude("include/bindings.h");
|
||||||
|
|
||||||
@cInclude("river-control-unstable-v1-protocol.h");
|
@cInclude("river-control-unstable-v1-protocol.h");
|
||||||
|
@cInclude("river-status-unstable-v1-protocol.h");
|
||||||
});
|
});
|
||||||
|
|
||||||
// These are needed because zig currently names translated anonymous unions
|
// These are needed because zig currently names translated anonymous unions
|
||||||
|
@ -45,10 +45,10 @@ pub fn focusOutput(
|
|||||||
|
|
||||||
// Focus the next/prev output in the list if there is one, else wrap
|
// Focus the next/prev output in the list if there is one, else wrap
|
||||||
const focused_node = @fieldParentPtr(std.TailQueue(Output).Node, "data", seat.focused_output);
|
const focused_node = @fieldParentPtr(std.TailQueue(Output).Node, "data", seat.focused_output);
|
||||||
seat.focused_output = switch (direction) {
|
seat.focusOutput(switch (direction) {
|
||||||
.Next => if (focused_node.next) |node| &node.data else &root.outputs.first.?.data,
|
.Next => if (focused_node.next) |node| &node.data else &root.outputs.first.?.data,
|
||||||
.Prev => if (focused_node.prev) |node| &node.data else &root.outputs.last.?.data,
|
.Prev => if (focused_node.prev) |node| &node.data else &root.outputs.last.?.data,
|
||||||
};
|
});
|
||||||
|
|
||||||
seat.focus(null);
|
seat.focus(null);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user