2020-05-02 10:21:10 -07:00
|
|
|
// This file is part of river, a dynamic tiling wayland compositor.
|
|
|
|
//
|
2020-11-11 11:30:21 -08:00
|
|
|
// Copyright 2020 The River Developers
|
2020-05-02 10:21:10 -07:00
|
|
|
//
|
|
|
|
// 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/>.
|
|
|
|
|
2020-05-02 07:20:32 -07:00
|
|
|
const Self = @This();
|
|
|
|
|
2020-05-08 05:51:10 -07:00
|
|
|
const build_options = @import("build_options");
|
2020-03-22 14:42:55 -07:00
|
|
|
const std = @import("std");
|
2021-05-23 04:35:37 -07:00
|
|
|
const mem = std.mem;
|
2020-11-03 15:23:21 -08:00
|
|
|
const wlr = @import("wlroots");
|
|
|
|
const wl = @import("wayland").server.wl;
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-03-29 10:36:15 -07:00
|
|
|
const c = @import("c.zig");
|
2020-06-16 11:54:05 -07:00
|
|
|
const util = @import("util.zig");
|
2020-03-22 14:42:55 -07:00
|
|
|
|
2020-05-02 14:11:56 -07:00
|
|
|
const Config = @import("Config.zig");
|
2020-06-04 07:56:58 -07:00
|
|
|
const Control = @import("Control.zig");
|
2020-05-02 14:11:56 -07:00
|
|
|
const DecorationManager = @import("DecorationManager.zig");
|
|
|
|
const InputManager = @import("InputManager.zig");
|
2020-05-11 15:11:11 -07:00
|
|
|
const LayerSurface = @import("LayerSurface.zig");
|
2020-10-02 06:53:08 -07:00
|
|
|
const LayoutManager = @import("LayoutManager.zig");
|
2020-05-02 14:11:56 -07:00
|
|
|
const Output = @import("Output.zig");
|
|
|
|
const Root = @import("Root.zig");
|
2020-06-04 07:56:58 -07:00
|
|
|
const StatusManager = @import("StatusManager.zig");
|
2020-05-02 14:11:56 -07:00
|
|
|
const View = @import("View.zig");
|
2020-04-03 09:53:36 -07:00
|
|
|
const ViewStack = @import("view_stack.zig").ViewStack;
|
2020-05-11 04:46:29 -07:00
|
|
|
const XwaylandUnmanaged = @import("XwaylandUnmanaged.zig");
|
2020-03-23 08:50:20 -07:00
|
|
|
|
2021-02-05 00:46:18 -08:00
|
|
|
const log = std.log.scoped(.server);
|
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
wl_server: *wl.Server,
|
2020-08-01 08:27:49 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
sigint_source: *wl.EventSource,
|
|
|
|
sigterm_source: *wl.EventSource,
|
2020-05-08 06:23:02 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
backend: *wlr.Backend,
|
|
|
|
noop_backend: *wlr.Backend,
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
xdg_shell: *wlr.XdgShell,
|
|
|
|
new_xdg_surface: wl.Listener(*wlr.XdgSurface),
|
2020-05-08 06:23:02 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
layer_shell: *wlr.LayerShellV1,
|
|
|
|
new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1),
|
2020-05-08 06:23:02 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
xwayland: if (build_options.xwayland) *wlr.Xwayland else void,
|
|
|
|
new_xwayland_surface: if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface) else void,
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-12-23 16:59:30 -08:00
|
|
|
foreign_toplevel_manager: *wlr.ForeignToplevelManagerV1,
|
|
|
|
|
2020-05-02 07:20:32 -07:00
|
|
|
decoration_manager: DecorationManager,
|
|
|
|
input_manager: InputManager,
|
|
|
|
root: Root,
|
|
|
|
config: Config,
|
2020-05-24 11:58:39 -07:00
|
|
|
control: Control,
|
2020-06-04 07:56:58 -07:00
|
|
|
status_manager: StatusManager,
|
2020-10-02 06:53:08 -07:00
|
|
|
layout_manager: LayoutManager,
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-06-16 13:08:36 -07:00
|
|
|
pub fn init(self: *Self) !void {
|
2020-11-03 15:23:21 -08:00
|
|
|
self.wl_server = try wl.Server.create();
|
|
|
|
errdefer self.wl_server.destroy();
|
|
|
|
|
|
|
|
const loop = self.wl_server.getEventLoop();
|
|
|
|
self.sigint_source = try loop.addSignal(*wl.Server, std.os.SIGINT, terminate, self.wl_server);
|
|
|
|
errdefer self.sigint_source.remove();
|
|
|
|
self.sigterm_source = try loop.addSignal(*wl.Server, std.os.SIGTERM, terminate, self.wl_server);
|
|
|
|
errdefer self.sigterm_source.remove();
|
|
|
|
|
|
|
|
// This frees itself when the wl.Server is destroyed
|
2021-04-07 15:21:17 -07:00
|
|
|
self.backend = try wlr.Backend.autocreate(self.wl_server);
|
2020-05-02 07:20:32 -07:00
|
|
|
|
|
|
|
// This backend is used to create a noop output for use when no actual
|
2020-11-03 15:23:21 -08:00
|
|
|
// outputs are available. This frees itself when the wl.Server is destroyed.
|
|
|
|
self.noop_backend = try wlr.Backend.createNoop(self.wl_server);
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
// This will never be null for the non-custom backends in wlroots
|
|
|
|
const renderer = self.backend.getRenderer().?;
|
|
|
|
try renderer.initServer(self.wl_server);
|
2020-07-16 12:20:43 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
const compositor = try wlr.Compositor.create(self.wl_server, renderer);
|
2020-05-08 05:51:10 -07:00
|
|
|
|
2020-05-08 06:23:02 -07:00
|
|
|
// Set up xdg shell
|
2020-11-03 15:23:21 -08:00
|
|
|
self.xdg_shell = try wlr.XdgShell.create(self.wl_server);
|
|
|
|
self.new_xdg_surface.setNotify(handleNewXdgSurface);
|
|
|
|
self.xdg_shell.events.new_surface.add(&self.new_xdg_surface);
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-05-08 06:23:02 -07:00
|
|
|
// Set up layer shell
|
2020-11-03 15:23:21 -08:00
|
|
|
self.layer_shell = try wlr.LayerShellV1.create(self.wl_server);
|
|
|
|
self.new_layer_surface.setNotify(handleNewLayerSurface);
|
|
|
|
self.layer_shell.events.new_surface.add(&self.new_layer_surface);
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-05-11 04:46:29 -07:00
|
|
|
// Set up xwayland if built with support
|
2020-05-08 05:51:10 -07:00
|
|
|
if (build_options.xwayland) {
|
2020-11-03 15:23:21 -08:00
|
|
|
self.xwayland = try wlr.Xwayland.create(self.wl_server, compositor, false);
|
|
|
|
self.new_xwayland_surface.setNotify(handleNewXwaylandSurface);
|
|
|
|
self.xwayland.events.new_surface.add(&self.new_xwayland_surface);
|
2020-05-08 05:51:10 -07:00
|
|
|
}
|
|
|
|
|
2020-12-23 16:59:30 -08:00
|
|
|
self.foreign_toplevel_manager = try wlr.ForeignToplevelManagerV1.create(self.wl_server);
|
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
_ = try wlr.PrimarySelectionDeviceManagerV1.create(self.wl_server);
|
2020-10-06 08:02:07 -07:00
|
|
|
|
2020-08-21 10:32:21 -07:00
|
|
|
self.config = try Config.init();
|
2021-05-13 06:06:00 -07:00
|
|
|
try self.decoration_manager.init();
|
2021-05-13 05:26:27 -07:00
|
|
|
try self.root.init();
|
2020-05-02 07:20:32 -07:00
|
|
|
// Must be called after root is initialized
|
2021-05-13 05:53:08 -07:00
|
|
|
try self.input_manager.init();
|
2021-05-13 06:06:00 -07:00
|
|
|
try self.control.init();
|
|
|
|
try self.status_manager.init();
|
|
|
|
try self.layout_manager.init();
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
// These all free themselves when the wl_server is destroyed
|
|
|
|
_ = try wlr.DataDeviceManager.create(self.wl_server);
|
|
|
|
_ = try wlr.DataControlManagerV1.create(self.wl_server);
|
|
|
|
_ = try wlr.ExportDmabufManagerV1.create(self.wl_server);
|
|
|
|
_ = try wlr.GammaControlManagerV1.create(self.wl_server);
|
|
|
|
_ = try wlr.ScreencopyManagerV1.create(self.wl_server);
|
|
|
|
_ = try wlr.Viewporter.create(self.wl_server);
|
2020-05-02 07:20:32 -07:00
|
|
|
}
|
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
/// Free allocated memory and clean up. Note: order is important here
|
2020-05-02 07:20:32 -07:00
|
|
|
pub fn deinit(self: *Self) void {
|
2020-11-03 15:23:21 -08:00
|
|
|
self.sigint_source.remove();
|
|
|
|
self.sigterm_source.remove();
|
2020-08-01 08:27:49 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
if (build_options.xwayland) self.xwayland.destroy();
|
2020-06-02 04:45:56 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
self.wl_server.destroyClients();
|
2020-06-02 04:45:56 -07:00
|
|
|
|
|
|
|
self.root.deinit();
|
|
|
|
|
2020-12-23 16:59:30 -08:00
|
|
|
self.wl_server.destroy();
|
2020-06-02 04:45:56 -07:00
|
|
|
|
2020-05-02 07:20:32 -07:00
|
|
|
self.input_manager.deinit();
|
2020-06-16 13:08:36 -07:00
|
|
|
self.config.deinit();
|
2020-05-02 07:20:32 -07:00
|
|
|
}
|
|
|
|
|
2020-06-17 13:17:51 -07:00
|
|
|
/// Create the socket, start the backend, and setup the environment
|
2020-05-02 07:20:32 -07:00
|
|
|
pub fn start(self: Self) !void {
|
2020-11-03 15:23:21 -08:00
|
|
|
var buf: [11]u8 = undefined;
|
|
|
|
const socket = try self.wl_server.addSocketAuto(&buf);
|
|
|
|
try self.backend.start();
|
|
|
|
// TODO: don't use libc's setenv
|
2020-06-25 15:59:31 -07:00
|
|
|
if (c.setenv("WAYLAND_DISPLAY", socket, 1) < 0) return error.SetenvError;
|
2020-05-08 05:51:10 -07:00
|
|
|
if (build_options.xwayland) {
|
2020-11-03 15:23:21 -08:00
|
|
|
if (c.setenv("DISPLAY", self.xwayland.display_name, 1) < 0) return error.SetenvError;
|
2020-05-08 05:51:10 -07:00
|
|
|
}
|
2020-05-02 07:20:32 -07:00
|
|
|
}
|
|
|
|
|
2020-08-01 08:27:49 -07:00
|
|
|
/// Handle SIGINT and SIGTERM by gracefully stopping the server
|
2020-11-03 15:23:21 -08:00
|
|
|
fn terminate(signal: c_int, wl_server: *wl.Server) callconv(.C) c_int {
|
|
|
|
wl_server.terminate();
|
2020-08-01 08:27:49 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
fn handleNewXdgSurface(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
|
|
|
|
const self = @fieldParentPtr(Self, "new_xdg_surface", listener);
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
if (xdg_surface.role == .popup) {
|
2021-02-05 00:46:18 -08:00
|
|
|
log.debug("new xdg_popup", .{});
|
2020-05-02 07:20:32 -07:00
|
|
|
return;
|
2020-03-22 14:42:55 -07:00
|
|
|
}
|
|
|
|
|
2021-02-05 00:46:18 -08:00
|
|
|
log.debug("new xdg_toplevel", .{});
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-05-11 14:43:04 -07:00
|
|
|
// The View will add itself to the output's view stack on map
|
2020-09-29 08:27:33 -07:00
|
|
|
const output = self.input_manager.defaultSeat().focused_output;
|
2020-07-05 13:49:17 -07:00
|
|
|
const node = util.gpa.create(ViewStack(View).Node) catch {
|
2020-11-03 15:23:21 -08:00
|
|
|
xdg_surface.resource.postNoMemory();
|
2020-07-05 13:49:17 -07:00
|
|
|
return;
|
|
|
|
};
|
2021-01-02 00:43:53 -08:00
|
|
|
node.view.init(output, getNewViewTags(output), xdg_surface);
|
2020-05-02 07:20:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// This event is raised when the layer_shell recieves a new surface from a client.
|
2020-11-03 15:23:21 -08:00
|
|
|
fn handleNewLayerSurface(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wlr.LayerSurfaceV1) void {
|
|
|
|
const self = @fieldParentPtr(Self, "new_layer_surface", listener);
|
2020-05-02 07:20:32 -07:00
|
|
|
|
2020-06-16 16:25:11 -07:00
|
|
|
log.debug(
|
2021-05-23 04:35:37 -07:00
|
|
|
"New layer surface: namespace {s}, layer {}, anchor {}, size {}x{}, margin ({},{},{},{}), exclusive_zone {}",
|
2020-05-02 07:20:32 -07:00
|
|
|
.{
|
|
|
|
wlr_layer_surface.namespace,
|
|
|
|
wlr_layer_surface.client_pending.layer,
|
|
|
|
wlr_layer_surface.client_pending.anchor,
|
|
|
|
wlr_layer_surface.client_pending.desired_width,
|
|
|
|
wlr_layer_surface.client_pending.desired_height,
|
|
|
|
wlr_layer_surface.client_pending.margin.top,
|
|
|
|
wlr_layer_surface.client_pending.margin.right,
|
|
|
|
wlr_layer_surface.client_pending.margin.bottom,
|
|
|
|
wlr_layer_surface.client_pending.margin.left,
|
|
|
|
wlr_layer_surface.client_pending.exclusive_zone,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
// If the new layer surface does not have an output assigned to it, use the
|
|
|
|
// first output or close the surface if none are available.
|
|
|
|
if (wlr_layer_surface.output == null) {
|
2020-09-29 08:27:33 -07:00
|
|
|
const output = self.input_manager.defaultSeat().focused_output;
|
|
|
|
if (output == &self.root.noop_output) {
|
2021-05-23 04:35:37 -07:00
|
|
|
log.err("no output available for layer surface '{s}'", .{wlr_layer_surface.namespace});
|
2020-11-03 15:23:21 -08:00
|
|
|
wlr_layer_surface.close();
|
2020-03-23 06:04:54 -07:00
|
|
|
return;
|
|
|
|
}
|
2020-09-29 08:27:33 -07:00
|
|
|
|
2021-05-23 04:35:37 -07:00
|
|
|
log.debug("new layer surface had null output, assigning it to output '{s}'", .{
|
|
|
|
mem.sliceTo(&output.wlr_output.name, 0),
|
2020-11-03 15:23:21 -08:00
|
|
|
});
|
2020-09-29 08:27:33 -07:00
|
|
|
wlr_layer_surface.output = output.wlr_output;
|
2020-03-23 04:22:48 -07:00
|
|
|
}
|
2020-04-10 07:49:52 -07:00
|
|
|
|
2020-05-11 15:11:11 -07:00
|
|
|
// The layer surface will add itself to the proper list of the output on map
|
2020-11-03 15:23:21 -08:00
|
|
|
const output = @intToPtr(*Output, wlr_layer_surface.output.?.data);
|
2020-07-05 13:49:17 -07:00
|
|
|
const node = util.gpa.create(std.TailQueue(LayerSurface).Node) catch {
|
2020-11-03 15:23:21 -08:00
|
|
|
wlr_layer_surface.resource.postNoMemory();
|
2020-07-05 13:49:17 -07:00
|
|
|
return;
|
|
|
|
};
|
2020-05-11 15:11:11 -07:00
|
|
|
node.data.init(output, wlr_layer_surface);
|
2020-05-02 07:20:32 -07:00
|
|
|
}
|
2020-05-08 05:51:10 -07:00
|
|
|
|
2020-11-03 15:23:21 -08:00
|
|
|
fn handleNewXwaylandSurface(listener: *wl.Listener(*wlr.XwaylandSurface), wlr_xwayland_surface: *wlr.XwaylandSurface) void {
|
|
|
|
const self = @fieldParentPtr(Self, "new_xwayland_surface", listener);
|
2020-05-08 05:51:10 -07:00
|
|
|
|
2020-05-11 04:46:29 -07:00
|
|
|
if (wlr_xwayland_surface.override_redirect) {
|
2021-02-05 00:46:18 -08:00
|
|
|
log.debug("new unmanaged xwayland surface", .{});
|
2020-05-11 04:46:29 -07:00
|
|
|
// The unmanged surface will add itself to the list of unmanaged views
|
|
|
|
// in Root when it is mapped.
|
2020-07-05 13:49:17 -07:00
|
|
|
const node = util.gpa.create(std.TailQueue(XwaylandUnmanaged).Node) catch return;
|
2021-05-13 06:08:53 -07:00
|
|
|
node.data.init(wlr_xwayland_surface);
|
2020-05-11 04:46:29 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-16 16:25:11 -07:00
|
|
|
log.debug(
|
2021-05-23 04:35:37 -07:00
|
|
|
"new xwayland surface: title '{s}', class '{s}'",
|
2020-05-08 05:51:10 -07:00
|
|
|
.{ wlr_xwayland_surface.title, wlr_xwayland_surface.class },
|
|
|
|
);
|
2020-05-11 14:43:04 -07:00
|
|
|
|
|
|
|
// The View will add itself to the output's view stack on map
|
2020-09-29 08:27:33 -07:00
|
|
|
const output = self.input_manager.defaultSeat().focused_output;
|
2020-07-05 13:49:17 -07:00
|
|
|
const node = util.gpa.create(ViewStack(View).Node) catch return;
|
2021-01-02 00:43:53 -08:00
|
|
|
node.view.init(output, getNewViewTags(output), wlr_xwayland_surface);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn getNewViewTags(output: *Output) u32 {
|
|
|
|
const tags = output.current.tags & output.spawn_tagmask;
|
|
|
|
return if (tags != 0) tags else output.current.tags;
|
2020-05-08 05:51:10 -07:00
|
|
|
}
|