1const std = @import("std");2const log = std.log;3const Mutex = std.Thread.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.FixedBufferStream([]u8),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 = Mutex{};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 = std.io.fixedBufferStream(&self.status_buffer);34 return self;35}3637pub fn destroy(self: *Seat) void {38 self.mtx.lock();39 if (self.window_title) |w| {40 state.gpa.free(w);41 }42 self.mtx.unlock();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();80 defer self.mtx.unlock();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 render.renderText(bar, self.status_text.getWritten()) catch |err| {110 log.err("renderText failed on focus for monitor {}: {s}",111 .{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}",136 .{bar.monitor.globalName, @errorName(err)});137 };138 bar.text.surface.commit();139140 render.renderTitle(bar, null) catch |err| {141 log.err("renderTitle failed on unfocus for monitor {}: {s}",142 .{bar.monitor.globalName, @errorName(err)});143 return;144 };145146 bar.title.surface.commit();147 bar.background.surface.commit();148 }149 } else {150 log.err("seatListener: couldn't find unfocused output", .{});151 }152153 self.current_output = null;154}155156fn focusedView(self: *Seat, title: [*:0]const u8) void {157 self.updateTitle(title);158 if (self.focusedBar()) |bar| {159 render.renderTitle(bar, self.window_title) catch |err| {160 log.err("renderTitle failed on focused view for monitor {}: {s}",161 .{bar.monitor.globalName, @errorName(err)});162 return;163 };164165 bar.title.surface.commit();166 bar.background.surface.commit();167 }168}169170fn seatListener(171 _: *zriver.SeatStatusV1,172 event: zriver.SeatStatusV1.Event,173 seat: *Seat,174) void {175 switch (event) {176 .focused_output => |data| seat.focusedOutput(data.output.?),177 .unfocused_output => |data| seat.unfocusedOutput(data.output.?),178 .focused_view => |data| seat.focusedView(data.title),179 }180}