aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command.zig140
-rw-r--r--src/cursor.zig2
-rw-r--r--src/input_manager.zig9
-rw-r--r--src/render.zig2
-rw-r--r--src/root.zig29
-rw-r--r--src/seat.zig96
-rw-r--r--src/view.zig55
7 files changed, 186 insertions, 147 deletions
diff --git a/src/command.zig b/src/command.zig
index 28023ce..347a1d7 100644
--- a/src/command.zig
+++ b/src/command.zig
@@ -21,62 +21,46 @@ pub fn exitCompositor(seat: *Seat, arg: Arg) void {
c.wl_display_terminate(seat.input_manager.server.wl_display);
}
+/// Focus either the next or the previous visible view, depending on the bool
+/// passed.
+fn focusNextPrevView(seat: *Seat, next: bool) void {
+ const output = seat.input_manager.server.root.focusedOutput();
+ if (seat.focused_view) |current_focus| {
+ // If there is a currently focused view, focus the next visible view in the stack.
+ const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus);
+ var it = if (next)
+ ViewStack(View).iterator(focused_node, output.current_focused_tags)
+ else
+ ViewStack(View).reverseIterator(focused_node, output.current_focused_tags);
+
+ // Skip past the focused node
+ _ = it.next();
+ // Focus the next visible node if there is one
+ if (it.next()) |node| {
+ seat.focus(&node.view);
+ return;
+ }
+ }
+
+ // There is either no currently focused view or the last visible view in the
+ // stack is focused and we need to wrap.
+ var it = if (next)
+ ViewStack(View).iterator(output.views.first, output.current_focused_tags)
+ else
+ ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags);
+ seat.focus(if (it.next()) |node| &node.view else null);
+}
+
/// Focus the next visible view in the stack, wrapping if needed. Does
/// nothing if there is only one view in the stack.
pub fn focusNextView(seat: *Seat, arg: Arg) void {
- // FIXME: this need to be rewritten the next commit adding a focus stack
- //const output = self.focusedOutput();
- //if (self.focused_view) |current_focus| {
- // // If there is a currently focused view, focus the next visible view in the stack.
- // const current_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus);
- // var it = ViewStack(View).iterator(current_node, output.current_focused_tags);
- // // Skip past the current node
- // _ = it.next();
- // // Focus the next visible node if there is one
- // if (it.next()) |node| {
- // node.view.focus(node.view.wlr_xdg_surface.surface);
- // return;
- // }
- //}
-
- //// There is either no currently focused view or the last visible view in the
- //// stack is focused and we need to wrap.
- //var it = ViewStack(View).iterator(output.views.first, output.current_focused_tags);
- //if (it.next()) |node| {
- // node.view.focus(node.view.wlr_xdg_surface.surface);
- //} else {
- // // Otherwise clear the focus since there are no visible views
- // self.clearFocus();
- //}
+ focusNextPrevView(seat, true);
}
/// Focus the previous view in the stack, wrapping if needed. Does nothing
/// if there is only one view in the stack.
pub fn focusPrevView(seat: *Seat, arg: Arg) void {
- // FIXME: this need to be rewritten the next commit adding a focus stack
- //const output = self.focusedOutput();
- //if (self.focused_view) |current_focus| {
- // // If there is a currently focused view, focus the previous visible view in the stack.
- // const current_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus);
- // var it = ViewStack(View).reverseIterator(current_node, output.current_focused_tags);
- // // Skip past the current node
- // _ = it.next();
- // // Focus the previous visible node if there is one
- // if (it.next()) |node| {
- // node.view.focus(node.view.wlr_xdg_surface.surface);
- // return;
- // }
- //}
-
- //// There is either no currently focused view or the first visible view in the
- //// stack is focused and we need to wrap.
- //var it = ViewStack(View).reverseIterator(output.views.last, output.current_focused_tags);
- //if (it.next()) |node| {
- // node.view.focus(node.view.wlr_xdg_surface.surface);
- //} else {
- // // Otherwise clear the focus since there are no visible views
- // self.clearFocus();
- //}
+ focusNextPrevView(seat, false);
}
/// Modify the number of master views
@@ -109,16 +93,16 @@ pub fn modifyMasterFactor(seat: *Seat, arg: Arg) void {
/// Bump the focused view to the top of the stack.
/// TODO: if the top of the stack is focused, bump the next visible view.
pub fn zoom(seat: *Seat, arg: Arg) void {
- // FIXME rewrite after next commit adding focus stack
- //if (server.root.focused_view) |current_focus| {
- // const output = server.root.focusedOutput();
- // const node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus);
- // if (node != output.views.first) {
- // output.views.remove(node);
- // output.views.push(node);
- // server.root.arrange();
- // }
- //}
+ if (seat.focused_view) |current_focus| {
+ const root = &seat.input_manager.server.root;
+ const output = root.focusedOutput();
+ const node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus);
+ if (node != output.views.first) {
+ output.views.remove(node);
+ output.views.push(node);
+ root.arrange();
+ }
+ }
}
/// Switch focus to the passed tags.
@@ -144,31 +128,28 @@ pub fn toggleTags(seat: *Seat, arg: Arg) void {
/// Set the tags of the focused view.
pub fn setFocusedViewTags(seat: *Seat, arg: Arg) void {
- // FIXME
- //const tags = arg.uint;
- //if (server.root.focused_view) |view| {
- // if (view.current_tags != tags) {
- // view.pending_tags = tags;
- // server.root.arrange();
- // }
- //}
+ const tags = arg.uint;
+ if (seat.focused_view) |view| {
+ if (view.current_tags != tags) {
+ view.pending_tags = tags;
+ seat.input_manager.server.root.arrange();
+ }
+ }
}
/// Toggle the passed tags of the focused view
pub fn toggleFocusedViewTags(seat: *Seat, arg: Arg) void {
- // FIXME: rewrite afet next commit adding focus stack
- //const tags = arg.uint;
- //if (server.root.focused_view) |view| {
- // const new_tags = view.current_tags ^ tags;
- // if (new_tags != 0) {
- // view.pending_tags = new_tags;
- // server.root.arrange();
- // }
- //}
+ const tags = arg.uint;
+ if (seat.focused_view) |view| {
+ const new_tags = view.current_tags ^ tags;
+ if (new_tags != 0) {
+ view.pending_tags = new_tags;
+ seat.input_manager.server.root.arrange();
+ }
+ }
}
/// Spawn a program.
-/// TODO: make this take a program as a paramter and spawn that
pub fn spawn(seat: *Seat, arg: Arg) void {
const cmd = arg.str;
@@ -185,8 +166,7 @@ pub fn spawn(seat: *Seat, arg: Arg) void {
/// Close the focused view, if any.
pub fn close(seat: *Seat, arg: Arg) void {
- // FIXME: see above
- //if (server.root.focused_view) |view| {
- // view.close();
- //}
+ if (seat.focused_view) |view| {
+ view.close();
+ }
}
diff --git a/src/cursor.zig b/src/cursor.zig
index af41c77..839276a 100644
--- a/src/cursor.zig
+++ b/src/cursor.zig
@@ -273,7 +273,7 @@ pub const Cursor = struct {
} else {
// Focus that client if the button was _pressed_
if (view) |v| {
- v.focus(surface.?);
+ cursor.seat.focus(v);
}
}
}
diff --git a/src/input_manager.zig b/src/input_manager.zig
index 885680c..30c06bc 100644
--- a/src/input_manager.zig
+++ b/src/input_manager.zig
@@ -30,6 +30,15 @@ pub const InputManager = struct {
c.wl_signal_add(&self.server.wlr_backend.events.new_input, &self.listen_new_input);
}
+ /// Must be called whenever a view is unmapped.
+ pub fn handleViewUnmap(self: Self, view: *View) void {
+ var it = self.seats.first;
+ while (it) |node| : (it = node.next) {
+ const seat = &node.data;
+ seat.handleViewUnmap(view);
+ }
+ }
+
/// This event is raised by the backend when a new input device becomes available.
fn handleNewInput(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
const input_manager = @fieldParentPtr(InputManager, "listen_new_input", listener.?);
diff --git a/src/render.zig b/src/render.zig
index 074228c..bd08400 100644
--- a/src/render.zig
+++ b/src/render.zig
@@ -262,7 +262,7 @@ fn renderSurface(_surface: ?*c.wlr_surface, sx: c_int, sy: c_int, data: ?*c_void
fn renderBorders(output: Output, view: *View, now: *c.struct_timespec, ox: f64, oy: f64) void {
var border: c.wlr_box = undefined;
- const color = if (output.root.focused_view == view)
+ const color = if (view.wlr_xdg_surface.unnamed_163.toplevel.*.current.activated)
[_]f32{ 0.57647059, 0.63137255, 0.63137255, 1.0 } // Solarized base1
else
[_]f32{ 0.34509804, 0.43137255, 0.45882353, 1.0 }; // Solarized base01
diff --git a/src/root.zig b/src/root.zig
index 38c09f6..0c470d2 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -19,10 +19,6 @@ pub const Root = struct {
wlr_output_layout: *c.wlr_output_layout,
outputs: std.TailQueue(Output),
- /// The view that has seat focus, if any.
- /// TODO: move this to Seat
- focused_view: ?*View,
-
/// Number of pending configures sent in the current transaction.
/// A value of 0 means there is no current transaction.
pending_configures: u32,
@@ -41,8 +37,6 @@ pub const Root = struct {
self.outputs = std.TailQueue(Output).init();
- self.focused_view = null;
-
self.pending_configures = 0;
self.transaction_timer = null;
@@ -188,8 +182,8 @@ pub const Root = struct {
// Iterate over all views of all outputs
var output_it = self.outputs.first;
- while (output_it) |node| : (output_it = node.next) {
- const output = &node.data;
+ while (output_it) |output_node| : (output_it = output_node.next) {
+ const output = &output_node.data;
// If there were pending focused tags, make them the current focus
if (output.pending_focused_tags) |tags| {
@@ -199,9 +193,6 @@ pub const Root = struct {
);
output.current_focused_tags = tags;
output.pending_focused_tags = null;
-
- self.focused_view = null;
- Log.Error.log("FIXME: this needs to iterate over all seats and focus(null)", .{});
}
var view_it = ViewStack(View).iterator(output.views.first, 0xFFFFFFFF);
@@ -218,20 +209,16 @@ pub const Root = struct {
if (view.pending_tags) |tags| {
view.current_tags = tags;
view.pending_tags = null;
-
- // If the pending tags caused the currently focused view to no
- // longer be visible, focus the next view.
- if (self.focused_view) |focus| {
- if (focus == view and
- view.current_tags & output.current_focused_tags == 0)
- {
- Log.Error.log("FIXME: this needs to iterate over all seats and focus(null)", .{});
- }
- }
}
view.dropStashedBuffer();
}
}
+
+ // Iterate over all seats and update focus
+ var it = self.server.input_manager.seats.first;
+ while (it) |seat_node| : (it = seat_node.next) {
+ seat_node.data.focus(null);
+ }
}
};
diff --git a/src/seat.zig b/src/seat.zig
index 646945c..34cdbfa 100644
--- a/src/seat.zig
+++ b/src/seat.zig
@@ -2,8 +2,11 @@ const std = @import("std");
const c = @import("c.zig");
const Cursor = @import("cursor.zig").Cursor;
+const Log = @import("log.zig").Log;
const InputManager = @import("input_manager.zig").InputManager;
const Keyboard = @import("keyboard.zig").Keyboard;
+const View = @import("view.zig").View;
+const ViewStack = @import("view_stack.zig").ViewStack;
pub const Seat = struct {
const Self = @This();
@@ -17,6 +20,13 @@ pub const Seat = struct {
/// Mulitple keyboards are handled separately
keyboards: std.TailQueue(Keyboard),
+ /// Currently focused view if any
+ focused_view: ?*View,
+
+ /// Stack of views in most recently focused order
+ /// If there is a currently focused view, it is on top.
+ focus_stack: ViewStack(*View),
+
pub fn init(self: *Self, input_manager: *InputManager, name: []const u8) !void {
self.input_manager = input_manager;
@@ -28,12 +38,98 @@ pub const Seat = struct {
errdefer self.cursor.destroy();
self.keyboards = std.TailQueue(Keyboard).init();
+
+ self.focused_view = null;
+
+ self.focus_stack.init();
}
pub fn destroy(self: Self) void {
self.cursor.destroy();
}
+ /// Set the current focus. If a visible view is passed it will be focused.
+ /// If null is passed, the first visible view in the focus stack will be focused.
+ pub fn focus(self: *Self, _view: ?*View) void {
+ var view = _view;
+
+ // If view is null or not currently visible
+ if (if (view) |v| v.current_tags & v.output.current_focused_tags == 0 else true) {
+ // Set view to the first currently visible view in the focus stack if any
+ view = if (ViewStack(*View).iterator(
+ self.focus_stack.first,
+ self.input_manager.server.root.focusedOutput().current_focused_tags,
+ ).next()) |node| node.view else null;
+ }
+
+ if (self.focused_view) |current_focus| {
+ // Don't refocus the currently focused view
+ if (if (view) |v| current_focus == v else false) {
+ return;
+ }
+ // Deactivate the currently focused view
+ current_focus.setActivated(false);
+ }
+
+ if (view) |to_focus| {
+ // Find or allocate a new node in the focus stack for the target view
+ var it = self.focus_stack.first;
+ while (it) |node| : (it = node.next) {
+ // If the view is found, move it to the top of the stack
+ if (node.view == to_focus) {
+ const new_focus_node = self.focus_stack.remove(node);
+ self.focus_stack.push(node);
+ break;
+ }
+ } else {
+ // The view is not in the stack, so allocate a new node and prepend it
+ const new_focus_node = self.input_manager.server.allocator.create(
+ ViewStack(*View).Node,
+ ) catch unreachable;
+ new_focus_node.view = to_focus;
+ self.focus_stack.push(new_focus_node);
+ }
+
+ // The target view is now at the top of the focus stack, so activate it
+ to_focus.setActivated(true);
+
+ // Tell the seat to have the keyboard enter this surface. wlroots will keep
+ // track of this and automatically send key events to the appropriate
+ // clients without additional work on your part.
+ const keyboard: *c.wlr_keyboard = c.wlr_seat_get_keyboard(self.wlr_seat);
+ c.wlr_seat_keyboard_notify_enter(
+ self.wlr_seat,
+ to_focus.wlr_xdg_surface.surface,
+ &keyboard.keycodes,
+ keyboard.num_keycodes,
+ &keyboard.modifiers,
+ );
+ }
+
+ self.focused_view = view;
+ }
+
+ /// Handle the unmapping of a view, removing it from the focus stack and
+ /// setting the focus if needed.
+ pub fn handleViewUnmap(self: *Self, view: *View) void {
+ // Remove the node from the focus stack and destroy it.
+ var it = self.focus_stack.first;
+ while (it) |node| : (it = node.next) {
+ if (node.view == view) {
+ self.focus_stack.remove(node);
+ self.input_manager.server.allocator.destroy(node);
+ break;
+ }
+ }
+
+ // If the unmapped view is focused, choose a new focus
+ if (self.focused_view) |current_focus| {
+ if (current_focus == view) {
+ self.focus(null);
+ }
+ }
+ }
+
/// Handle any user-defined keybinding for the passed keysym and modifiers
/// Returns true if the key was handled
pub fn handleKeybinding(self: *Self, keysym: c.xkb_keysym_t, modifiers: u32) bool {
diff --git a/src/view.zig b/src/view.zig
index aa35887..70c81d5 100644
--- a/src/view.zig
+++ b/src/view.zig
@@ -131,8 +131,10 @@ pub const View = struct {
fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
// Called when the surface is mapped, or ready to display on-screen.
const view = @fieldParentPtr(View, "listen_map", listener.?);
+ const root = view.output.root;
view.mapped = true;
- view.focus(view.wlr_xdg_surface.surface);
+ // TODO: remove this hack
+ root.server.input_manager.seats.first.?.data.focus(view);
view.output.root.arrange();
}
@@ -141,13 +143,11 @@ pub const View = struct {
const root = view.output.root;
view.mapped = false;
- if (root.focused_view) |current_focus| {
- // If the view being unmapped is focused
- if (current_focus == view) {
- // Focus the previous view. This clears the focus if there are no visible views.
- // FIXME: must be fixed in next commit adding focus stack
- //root.focusPrevView();
- }
+ // Inform all seats that the view has been unmapped so they can handle focus
+ var it = root.server.input_manager.seats.first;
+ while (it) |node| : (it = node.next) {
+ const seat = &node.data;
+ seat.handleViewUnmap(view);
}
root.arrange();
@@ -181,42 +181,9 @@ pub const View = struct {
// // ignore for now
// }
- fn focus(self: *Self, surface: *c.wlr_surface) void {
- const root = self.output.root;
- // TODO: remove this hack
- const wlr_seat = root.server.input_manager.seats.first.?.data.wlr_seat;
- const prev_surface = wlr_seat.keyboard_state.focused_surface;
-
- if (prev_surface == surface) {
- // Don't re-focus an already focused surface.
- // TODO: debug message?
- return;
- }
-
- root.focused_view = self;
-
- if (prev_surface != null) {
- // Deactivate the previously focused surface. This lets the client know
- // it no longer has focus and the client will repaint accordingly, e.g.
- // stop displaying a caret.
- const prev_xdg_surface = c.wlr_xdg_surface_from_wlr_surface(prev_surface);
- _ = c.wlr_xdg_toplevel_set_activated(prev_xdg_surface, false);
- }
-
- // Activate the new surface
- _ = c.wlr_xdg_toplevel_set_activated(self.wlr_xdg_surface, true);
-
- // Tell the seat to have the keyboard enter this surface. wlroots will keep
- // track of this and automatically send key events to the appropriate
- // clients without additional work on your part.
- const keyboard: *c.wlr_keyboard = c.wlr_seat_get_keyboard(wlr_seat);
- c.wlr_seat_keyboard_notify_enter(
- wlr_seat,
- self.wlr_xdg_surface.surface,
- &keyboard.keycodes,
- keyboard.num_keycodes,
- &keyboard.modifiers,
- );
+ /// Set the active state of the view to the passed bool
+ pub fn setActivated(self: Self, activated: bool) void {
+ _ = c.wlr_xdg_toplevel_set_activated(self.wlr_xdg_surface, activated);
}
fn isAt(self: Self, lx: f64, ly: f64, surface: *?*c.wlr_surface, sx: *f64, sy: *f64) bool {