git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commitbb9dcb65e4ff5ccdfa657b7ebc4c75148237f8c8
parentdcd8c66f50
authorIsaac Freund <[email protected]>
date2025-07-21 12:11
InputPopup: fix a TODO panic

 river/InputPopup.zig    | 18 ++++++++----------
 river/OutputManager.zig |  8 ++++++++
 river/XdgPopup.zig      | 19 ++++---------------
 3 files changed, 20 insertions(+), 25 deletions(-)

diff --git a/river/InputPopup.zig b/river/InputPopup.zig
index 0d7a775..a7d54c5 100644
--- a/river/InputPopup.zig
+++ b/river/InputPopup.zig
@@ -107,13 +107,6 @@ pub fn update(input_popup: *InputPopup) void {
 
     const focused = SceneNodeData.fromSurface(focused_surface) orelse return;
 
-    const output = switch (focused.data) {
-        .window, .shell_surface => @panic("TODO"),
-        .lock_surface => |lock_surface| lock_surface.getOutput(),
-        // Xwayland doesn't use the text-input protocol
-        .override_redirect => unreachable,
-    };
-
     const popup_tree = switch (focused.data) {
         .window => |window| window.popup_tree,
         .shell_surface => @panic("TODO"),
@@ -137,12 +130,17 @@ pub fn update(input_popup: *InputPopup) void {
     var focused_y: c_int = undefined;
     _ = focused.node.coords(&focused_x, &focused_y);
 
-    var output_box: wlr.Box = undefined;
-    server.om.output_layout.getBox(output.wlr_output, &output_box);
-
     // Relative to the surface with the active text input
     var cursor_box = text_input.wlr_text_input.current.cursor_rectangle;
 
+    const wlr_output = server.om.outputAt(
+        @floatFromInt(focused_x + cursor_box.x),
+        @floatFromInt(focused_y + cursor_box.y),
+    ) orelse return;
+
+    var output_box: wlr.Box = undefined;
+    server.om.output_layout.getBox(wlr_output, &output_box);
+
     // Adjust to be relative to the output
     cursor_box.x += focused_x - output_box.x;
     cursor_box.y += focused_y - output_box.y;
diff --git a/river/OutputManager.zig b/river/OutputManager.zig
index 1426260..7cbb11c 100644
--- a/river/OutputManager.zig
+++ b/river/OutputManager.zig
@@ -102,6 +102,14 @@ fn handleNewOutput(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
     };
 }
 
+/// Returns null if there are no outputs in the output layout
+pub fn outputAt(om: *OutputManager, lx: f64, ly: f64) ?*wlr.Output {
+    var output_lx: f64 = undefined;
+    var output_ly: f64 = undefined;
+    om.output_layout.closestPoint(null, lx, ly, &output_lx, &output_ly);
+    return om.output_layout.outputAt(output_lx, output_ly);
+}
+
 fn handleManagerTest(_: *wl.Listener(*wlr.OutputConfigurationV1), config: *wlr.OutputConfigurationV1) void {
     defer config.destroy();
 
diff --git a/river/XdgPopup.zig b/river/XdgPopup.zig
index 3026347..f0689bb 100644
--- a/river/XdgPopup.zig
+++ b/river/XdgPopup.zig
@@ -99,21 +99,10 @@ fn handleReposition(listener: *wl.Listener(void)) void {
     var root_ly: c_int = undefined;
     _ = xdg_popup.root.node.coords(&root_lx, &root_ly);
 
-    const wlr_output = switch (SceneNodeData.fromNode(&xdg_popup.root.node).?.data) {
-        .window => |_| blk: {
-            var lx: f64 = undefined;
-            var ly: f64 = undefined;
-            server.om.output_layout.closestPoint(
-                null,
-                @as(f64, @floatFromInt(root_lx + xdg_popup.wlr_popup.scheduled.geometry.x)),
-                @as(f64, @floatFromInt(root_ly + xdg_popup.wlr_popup.scheduled.geometry.y)),
-                &lx,
-                &ly,
-            );
-            break :blk server.om.output_layout.outputAt(lx, ly).?;
-        },
-        .shell_surface, .lock_surface, .override_redirect => unreachable,
-    };
+    const wlr_output = server.om.outputAt(
+        @floatFromInt(root_lx + xdg_popup.wlr_popup.scheduled.geometry.x),
+        @floatFromInt(root_ly + xdg_popup.wlr_popup.scheduled.geometry.y),
+    ) orelse return;
 
     var box: wlr.Box = undefined;
     server.om.output_layout.getBox(wlr_output, &box);