1const std = @import("std");2const log = std.log;3const Mutex = std.Io.Mutex;45const wl = @import("wayland").client.wl;67const Bar = @import("Bar.zig");8const Monitor = @import("Monitor.zig");9const render = @import("render.zig");10const zriver = @import("wayland").client.zriver;11const state = &@import("root").state;1213pub const Seat = @This();1415seat_status: *zriver.SeatStatusV1,16current_output: ?*wl.Output,17window_title: ?[:0]u8,18status_buffer: [4096]u8 = undefined,19status_text: std.Io.Writer,20mtx: Mutex,2122pub fn create() !*Seat {23 const self = try state.gpa.create(Seat);24 const manager = state.wayland.status_manager.?;25 const seat = state.wayland.seat.?;2627 self.mtx = .init;28 self.current_output = null;29 self.window_title = null;30 self.seat_status = try manager.getRiverSeatStatus(seat);31 self.seat_status.setListener(*Seat, seatListener, self);3233 self.status_text = .fixed(&self.status_buffer);34 return self;35}3637pub fn destroy(self: *Seat) void {38 self.mtx.lock(state.io) catch unreachable;39 if (self.window_title) |w| {40 state.gpa.free(w);41 }42 self.mtx.unlock(state.io);4344 self.seat_status.destroy();45 state.gpa.destroy(self);46}4748pub fn focusedMonitor(self: *Seat) ?*Monitor {49 // If there is no current monitor, e.g. on startup use the first one.50 //51 // TODO: Find a better way to do this.52 if (self.current_output == null) {53 const items = state.wayland.monitors.items;54 if (items.len > 0) {55 return items[0];56 }57 }5859 for (state.wayland.monitors.items) |monitor| {60 if (monitor.output == self.current_output) {61 return monitor;62 }63 }6465 return null;66}6768pub fn focusedBar(self: *Seat) ?*Bar {69 if (self.focusedMonitor()) |m| {70 return m.confBar();71 }7273 return null;74}7576fn updateTitle(self: *Seat, data: [*:0]const u8) void {77 const title = std.mem.sliceTo(data, 0);7879 self.mtx.lock(state.io) catch unreachable;80 defer self.mtx.unlock(state.io);8182 if (self.window_title) |t| {83 state.gpa.free(t);84 }85 if (title.len == 0) {86 self.window_title = null;87 } else {88 const vz = state.gpa.allocSentinel(u8, title.len, 0) catch |err| {89 log.err("allocSentinel failed for window title: {s}\n", .{@errorName(err)});90 return;91 };92 @memcpy(vz[0..vz.len], title);93 self.window_title = vz;94 }95}9697fn focusedOutput(self: *Seat, output: *wl.Output) void {98 var monitor: ?*Monitor = null;99 for (state.wayland.monitors.items) |m| {100 if (m.output == output) {101 monitor = m;102 break;103 }104 }105106 if (monitor) |m| {107 if (m.confBar()) |bar| {108 self.current_output = m.output;109 defer self.status_text.flush() catch unreachable;110 render.renderText(bar, self.status_text.buffered()) catch |err| {111 log.err("renderText failed on focus for monitor {}: {s}", .{ m.globalName, @errorName(err) });112 return;113 };114115 bar.text.surface.commit();116 bar.background.surface.commit();117 }118 } else {119 log.err("seatListener: couldn't find focused output", .{});120 }121}122123fn unfocusedOutput(self: *Seat, output: *wl.Output) void {124 var monitor: ?*Monitor = null;125 for (state.wayland.monitors.items) |m| {126 if (m.output == output) {127 monitor = m;128 break;129 }130 }131132 if (monitor) |m| {133 if (m.confBar()) |bar| {134 render.resetText(bar) catch |err| {135 log.err("resetText failed for monitor {}: {s}", .{ bar.monitor.globalName, @errorName(err) });136 };137 bar.text.surface.commit();138139 render.renderTitle(bar, null) catch |err| {140 log.err("renderTitle failed on unfocus for monitor {}: {s}", .{ bar.monitor.globalName, @errorName(err) });141 return;142 };143144 bar.title.surface.commit();145 bar.background.surface.commit();146 }147 } else {148 log.err("seatListener: couldn't find unfocused output", .{});149 }150151 self.current_output = null;152}153154fn focusedView(self: *Seat, title: [*:0]const u8) void {155 self.updateTitle(title);156 if (self.focusedBar()) |bar| {157 render.renderTitle(bar, self.window_title) catch |err| {158 log.err("renderTitle failed on focused view for monitor {}: {s}", .{ bar.monitor.globalName, @errorName(err) });159 return;160 };161162 bar.title.surface.commit();163 bar.background.surface.commit();164 }165}166167fn seatListener(168 _: *zriver.SeatStatusV1,169 event: zriver.SeatStatusV1.Event,170 seat: *Seat,171) void {172 switch (event) {173 .focused_output => |data| seat.focusedOutput(data.output.?),174 .unfocused_output => |data| seat.unfocusedOutput(data.output.?),175 .focused_view => |data| seat.focusedView(data.title),176 }177}