wlr-foreign-toplevel-management: implement protocol
Well, at least as much of it as was implemented before the scene graph refactor.
This commit is contained in:
parent
f21eb4d05b
commit
05e8fbc8b9
117
river/ForeignToplevelHandle.zig
Normal file
117
river/ForeignToplevelHandle.zig
Normal file
@ -0,0 +1,117 @@
|
||||
// This file is part of river, a dynamic tiling wayland compositor.
|
||||
//
|
||||
// Copyright 2023 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 ForeignToplevelHandle = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const wlr = @import("wlroots");
|
||||
const wl = @import("wayland").server.wl;
|
||||
|
||||
const server = &@import("main.zig").server;
|
||||
|
||||
const View = @import("View.zig");
|
||||
const Seat = @import("Seat.zig");
|
||||
|
||||
wlr_handle: ?*wlr.ForeignToplevelHandleV1 = null,
|
||||
|
||||
foreign_activate: wl.Listener(*wlr.ForeignToplevelHandleV1.event.Activated) =
|
||||
wl.Listener(*wlr.ForeignToplevelHandleV1.event.Activated).init(handleForeignActivate),
|
||||
foreign_fullscreen: wl.Listener(*wlr.ForeignToplevelHandleV1.event.Fullscreen) =
|
||||
wl.Listener(*wlr.ForeignToplevelHandleV1.event.Fullscreen).init(handleForeignFullscreen),
|
||||
foreign_close: wl.Listener(*wlr.ForeignToplevelHandleV1) =
|
||||
wl.Listener(*wlr.ForeignToplevelHandleV1).init(handleForeignClose),
|
||||
|
||||
pub fn map(handle: *ForeignToplevelHandle) void {
|
||||
const view = @fieldParentPtr(View, "foreign_toplevel_handle", handle);
|
||||
|
||||
assert(handle.wlr_handle == null);
|
||||
|
||||
handle.wlr_handle = wlr.ForeignToplevelHandleV1.create(server.foreign_toplevel_manager) catch {
|
||||
std.log.err("out of memory", .{});
|
||||
return;
|
||||
};
|
||||
|
||||
handle.wlr_handle.?.events.request_activate.add(&handle.foreign_activate);
|
||||
handle.wlr_handle.?.events.request_fullscreen.add(&handle.foreign_fullscreen);
|
||||
handle.wlr_handle.?.events.request_close.add(&handle.foreign_close);
|
||||
|
||||
if (view.getTitle()) |title| handle.wlr_handle.?.setTitle(title);
|
||||
if (view.getAppId()) |app_id| handle.wlr_handle.?.setAppId(app_id);
|
||||
}
|
||||
|
||||
pub fn unmap(handle: *ForeignToplevelHandle) void {
|
||||
const wlr_handle = handle.wlr_handle orelse return;
|
||||
|
||||
handle.foreign_activate.link.remove();
|
||||
handle.foreign_fullscreen.link.remove();
|
||||
handle.foreign_close.link.remove();
|
||||
|
||||
wlr_handle.destroy();
|
||||
|
||||
handle.wlr_handle = null;
|
||||
}
|
||||
|
||||
/// Must be called just before the view's inflight state is made current.
|
||||
pub fn update(handle: *ForeignToplevelHandle) void {
|
||||
const view = @fieldParentPtr(View, "foreign_toplevel_handle", handle);
|
||||
|
||||
const wlr_handle = handle.wlr_handle orelse return;
|
||||
|
||||
if (view.inflight.output != view.current.output) {
|
||||
if (view.current.output) |output| wlr_handle.outputLeave(output.wlr_output);
|
||||
if (view.inflight.output) |output| wlr_handle.outputEnter(output.wlr_output);
|
||||
}
|
||||
|
||||
wlr_handle.setActivated(view.inflight.focus != 0);
|
||||
wlr_handle.setFullscreen(view.inflight.output != null and
|
||||
view.inflight.output.?.inflight.fullscreen == view);
|
||||
}
|
||||
|
||||
/// Only honors the request if the view is already visible on the seat's
|
||||
/// currently focused output.
|
||||
fn handleForeignActivate(
|
||||
listener: *wl.Listener(*wlr.ForeignToplevelHandleV1.event.Activated),
|
||||
event: *wlr.ForeignToplevelHandleV1.event.Activated,
|
||||
) void {
|
||||
const handle = @fieldParentPtr(ForeignToplevelHandle, "foreign_activate", listener);
|
||||
const view = @fieldParentPtr(View, "foreign_toplevel_handle", handle);
|
||||
const seat = @intToPtr(*Seat, event.seat.data);
|
||||
|
||||
seat.focus(view);
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
fn handleForeignFullscreen(
|
||||
listener: *wl.Listener(*wlr.ForeignToplevelHandleV1.event.Fullscreen),
|
||||
event: *wlr.ForeignToplevelHandleV1.event.Fullscreen,
|
||||
) void {
|
||||
const handle = @fieldParentPtr(ForeignToplevelHandle, "foreign_fullscreen", listener);
|
||||
const view = @fieldParentPtr(View, "foreign_toplevel_handle", handle);
|
||||
|
||||
view.pending.fullscreen = event.fullscreen;
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
fn handleForeignClose(
|
||||
listener: *wl.Listener(*wlr.ForeignToplevelHandleV1),
|
||||
_: *wlr.ForeignToplevelHandleV1,
|
||||
) void {
|
||||
const handle = @fieldParentPtr(ForeignToplevelHandle, "foreign_close", listener);
|
||||
const view = @fieldParentPtr(View, "foreign_toplevel_handle", handle);
|
||||
|
||||
view.close();
|
||||
}
|
@ -61,6 +61,8 @@ new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1),
|
||||
xwayland: if (build_options.xwayland) *wlr.Xwayland else void,
|
||||
new_xwayland_surface: if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface) else void,
|
||||
|
||||
foreign_toplevel_manager: *wlr.ForeignToplevelManagerV1,
|
||||
|
||||
xdg_activation: *wlr.XdgActivationV1,
|
||||
request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate),
|
||||
|
||||
@ -111,6 +113,8 @@ pub fn init(self: *Self) !void {
|
||||
self.xwayland.events.new_surface.add(&self.new_xwayland_surface);
|
||||
}
|
||||
|
||||
self.foreign_toplevel_manager = try wlr.ForeignToplevelManagerV1.create(self.wl_server);
|
||||
|
||||
self.xdg_activation = try wlr.XdgActivationV1.create(self.wl_server);
|
||||
self.xdg_activation.events.request_activate.add(&self.request_activate);
|
||||
self.request_activate.setNotify(handleRequestActivate);
|
||||
|
@ -27,6 +27,7 @@ const wl = @import("wayland").server.wl;
|
||||
const server = &@import("main.zig").server;
|
||||
const util = @import("util.zig");
|
||||
|
||||
const ForeignToplevelHandle = @import("ForeignToplevelHandle.zig");
|
||||
const Output = @import("Output.zig");
|
||||
const SceneNodeData = @import("SceneNodeData.zig");
|
||||
const Seat = @import("Seat.zig");
|
||||
@ -147,6 +148,8 @@ float_box: wlr.Box = undefined,
|
||||
/// exiting fullscreen if there is no active layout.
|
||||
post_fullscreen_box: wlr.Box = undefined,
|
||||
|
||||
foreign_toplevel_handle: ForeignToplevelHandle = .{},
|
||||
|
||||
pub fn create(impl: Impl) error{OutOfMemory}!*Self {
|
||||
const view = try util.gpa.create(Self);
|
||||
errdefer util.gpa.destroy(view);
|
||||
@ -216,6 +219,8 @@ pub fn destroy(view: *Self) void {
|
||||
pub fn updateCurrent(view: *Self) void {
|
||||
const config = &server.config;
|
||||
|
||||
view.foreign_toplevel_handle.update();
|
||||
|
||||
view.current = view.inflight;
|
||||
view.dropSavedSurfaceTree();
|
||||
if (view.impl == .xdg_toplevel) {
|
||||
@ -387,6 +392,8 @@ pub fn map(view: *Self) !void {
|
||||
assert(!view.mapped and !view.destroying);
|
||||
view.mapped = true;
|
||||
|
||||
view.foreign_toplevel_handle.map();
|
||||
|
||||
view.pending.borders = !server.config.csdAllowed(view);
|
||||
|
||||
if (server.input_manager.defaultSeat().focused_output) |output| {
|
||||
@ -427,14 +434,19 @@ pub fn unmap(view: *Self) void {
|
||||
assert(view.mapped and !view.destroying);
|
||||
view.mapped = false;
|
||||
|
||||
view.foreign_toplevel_handle.unmap();
|
||||
|
||||
server.root.applyPending();
|
||||
}
|
||||
|
||||
pub fn notifyTitle(self: *const Self) void {
|
||||
pub fn notifyTitle(view: *const Self) void {
|
||||
if (view.foreign_toplevel_handle.wlr_handle) |wlr_handle| {
|
||||
if (view.getTitle()) |title| wlr_handle.setTitle(title);
|
||||
}
|
||||
// Send title to all status listeners attached to a seat which focuses this view
|
||||
var seat_it = server.input_manager.seats.first;
|
||||
while (seat_it) |seat_node| : (seat_it = seat_node.next) {
|
||||
if (seat_node.data.focused == .view and seat_node.data.focused.view == self) {
|
||||
if (seat_node.data.focused == .view and seat_node.data.focused.view == view) {
|
||||
var client_it = seat_node.data.status_trackers.first;
|
||||
while (client_it) |client_node| : (client_it = client_node.next) {
|
||||
client_node.data.sendFocusedView();
|
||||
@ -443,6 +455,8 @@ pub fn notifyTitle(self: *const Self) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notifyAppId(_: Self) void {
|
||||
// TODO reimplement foreign-toplevel-management I guess.
|
||||
pub fn notifyAppId(view: Self) void {
|
||||
if (view.foreign_toplevel_handle.wlr_handle) |wlr_handle| {
|
||||
if (view.getAppId()) |app_id| wlr_handle.setAppId(app_id);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user