aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsaac Freund <ifreund@ifreund.xyz>2020-07-14 17:34:29 +0200
committerIsaac Freund <ifreund@ifreund.xyz>2020-07-14 17:34:29 +0200
commit6bdb152808c39bc3776c34e13c07b25d18272824 (patch)
treef53078159bdd7a663043f413cf0687edc9584c13
parentf3d4e5ac5311f27d8b17f2797a861aab4bae5673 (diff)
downloadriver-6bdb152808c39bc3776c34e13c07b25d18272824.tar.gz
river-6bdb152808c39bc3776c34e13c07b25d18272824.tar.xz
cursor: make xcursor theme configurable
- add a new command to set the theme - export the theme of the default seat through environment variables
-rw-r--r--doc/riverctl.1.scd6
-rw-r--r--river/Cursor.zig80
-rw-r--r--river/command.zig4
-rw-r--r--river/command/xcursor_theme.zig38
4 files changed, 99 insertions, 29 deletions
diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd
index 3c03adf..eef17cb 100644
--- a/doc/riverctl.1.scd
+++ b/doc/riverctl.1.scd
@@ -124,6 +124,12 @@ that tag 1 through 9 are visible.
- *outer_padding* _non-negative_integer_
- *view_padding* _non-negative_integer_
+*xcursor-theme* _theme_name_ [_size_]
+ Set the xcursor theme to _theme_name_ and optionally set the
+ _size_. The theme of the default seat determines the default
+ for XWayland and made available through the _XCURSOR_THEME_ and
+ _XCURSOR_SIZE_ environment variables.
+
# EXAMPLES
Bind bemenu-run to Super+P:
diff --git a/river/Cursor.zig b/river/Cursor.zig
index 7b72729..5c3e1b1 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -39,6 +39,8 @@ const CursorMode = enum {
Resize,
};
+const default_size = 24;
+
seat: *Seat,
wlr_cursor: *c.wlr_cursor,
wlr_xcursor_manager: *c.wlr_xcursor_manager,
@@ -71,35 +73,14 @@ pub fn init(self: *Self, seat: *Seat) !void {
// Creates a wlroots utility for tracking the cursor image shown on screen.
self.wlr_cursor = c.wlr_cursor_create() orelse return error.OutOfMemory;
-
- // Creates an xcursor manager, another wlroots utility which loads up
- // Xcursor themes to source cursor images from and makes sure that cursor
- // images are available at all scale factors on the screen (necessary for
- // HiDPI support). We add a cursor theme at scale factor 1 to begin with.
- self.wlr_xcursor_manager = c.wlr_xcursor_manager_create(null, 24) orelse return error.OutOfMemory;
c.wlr_cursor_attach_output_layout(self.wlr_cursor, seat.input_manager.server.root.wlr_output_layout);
- if (c.wlr_xcursor_manager_load(self.wlr_xcursor_manager, 1) == 0) {
- if (build_options.xwayland) {
- if (c.wlr_xcursor_manager_get_xcursor(
- self.wlr_xcursor_manager,
- "left_ptr",
- 1,
- )) |wlr_xcursor| {
- const image: *c.wlr_xcursor_image = wlr_xcursor.*.images[0];
- c.wlr_xwayland_set_cursor(
- seat.input_manager.server.wlr_xwayland,
- image.buffer,
- image.width * 4,
- image.width,
- image.height,
- @intCast(i32, image.hotspot_x),
- @intCast(i32, image.hotspot_y),
- );
- }
- }
- } else {
- log.err(.cursor, "failed to load an xcursor theme", .{});
- }
+
+ // This is here so that self.wlr_xcursor_manager doesn't need to be an
+ // optional pointer. This isn't optimal as it does a needless allocation,
+ // but this is not a hot path.
+ self.wlr_xcursor_manager = c.wlr_xcursor_manager_create(null, default_size) orelse
+ return error.OutOfMemory;
+ try self.setTheme(null, null);
self.mode = CursorMode.Passthrough;
self.grabbed_view = undefined;
@@ -136,6 +117,49 @@ pub fn deinit(self: *Self) void {
c.wlr_cursor_destroy(self.wlr_cursor);
}
+/// Set the cursor theme for the given seat, as well as the xwayland theme if
+/// this is the default seat.
+pub fn setTheme(self: *Self, theme: ?[*:0]const u8, size: ?u32) !void {
+ const server = self.seat.input_manager.server;
+
+ c.wlr_xcursor_manager_destroy(self.wlr_xcursor_manager);
+ self.wlr_xcursor_manager = c.wlr_xcursor_manager_create(theme, size orelse default_size) orelse
+ return error.OutOfMemory;
+
+ // For each output, ensure a theme of the proper scale is loaded
+ var it = server.root.outputs.first;
+ while (it) |node| : (it = node.next) {
+ const wlr_output = node.data.wlr_output;
+ if (c.wlr_xcursor_manager_load(self.wlr_xcursor_manager, wlr_output.scale) != 0)
+ log.err(.cursor, "failed to load xcursor theme '{}' at scale {}", .{ theme, wlr_output.scale });
+ }
+
+ // If this cursor belongs to the default seat, set the xcursor environment
+ // variables and the xwayland cursor theme.
+ if (self.seat == self.seat.input_manager.default_seat) {
+ const size_str = try std.fmt.allocPrint0(util.gpa, "{}", .{size});
+ defer util.gpa.free(size_str);
+ if (c.setenv("XCURSOR_SIZE", size_str, 1) < 0) return error.OutOfMemory;
+ if (theme) |t| if (c.setenv("XCURSOR_THEME", t, 1) < 0) return error.OutOfMemory;
+
+ if (build_options.xwayland) {
+ if (c.wlr_xcursor_manager_load(self.wlr_xcursor_manager, 1) == 0) {
+ const wlr_xcursor = c.wlr_xcursor_manager_get_xcursor(self.wlr_xcursor_manager, "left_ptr", 1).?;
+ const image: *c.wlr_xcursor_image = wlr_xcursor.*.images[0];
+ c.wlr_xwayland_set_cursor(
+ server.wlr_xwayland,
+ image.buffer,
+ image.width * 4,
+ image.width,
+ image.height,
+ @intCast(i32, image.hotspot_x),
+ @intCast(i32, image.hotspot_y),
+ );
+ } else log.err(.cursor, "failed to load xcursor theme '{}' at scale 1", .{theme});
+ }
+ }
+}
+
fn handleAxis(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
// This event is forwarded by the cursor when a pointer emits an axis event,
// for example when you move the scroll wheel.
diff --git a/river/command.zig b/river/command.zig
index f71f18f..8974f94 100644
--- a/river/command.zig
+++ b/river/command.zig
@@ -39,6 +39,7 @@ const impl = struct {
const toggleFocusedTags = @import("command/tags.zig").toggleFocusedTags;
const toggleFullscreen = @import("command/toggle_fullscreen.zig").toggleFullscreen;
const toggleViewTags = @import("command/tags.zig").toggleViewTags;
+ const xcursorTheme = @import("command/xcursor_theme.zig").xcursorTheme;
const zoom = @import("command/zoom.zig").zoom;
};
@@ -79,9 +80,10 @@ const str_to_impl_fn = [_]struct {
.{ .name = "spawn", .impl = impl.spawn },
.{ .name = "toggle-float", .impl = impl.toggleFloat },
.{ .name = "toggle-focused-tags", .impl = impl.toggleFocusedTags },
+ .{ .name = "toggle-fullscreen", .impl = impl.toggleFullscreen },
.{ .name = "toggle-view-tags", .impl = impl.toggleViewTags },
+ .{ .name = "xcursor-theme", .impl = impl.xcursorTheme },
.{ .name = "zoom", .impl = impl.zoom },
- .{ .name = "toggle-fullscreen", .impl = impl.toggleFullscreen },
};
// zig fmt: on
diff --git a/river/command/xcursor_theme.zig b/river/command/xcursor_theme.zig
new file mode 100644
index 0000000..f7a94ed
--- /dev/null
+++ b/river/command/xcursor_theme.zig
@@ -0,0 +1,38 @@
+// This file is part of river, a dynamic tiling wayland compositor.
+//
+// Copyright 2020 Isaac Freund
+//
+// 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, either version 3 of the License, or
+// (at your option) any later version.
+//
+// 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 std = @import("std");
+
+const Error = @import("../command.zig").Error;
+const Seat = @import("../Seat.zig");
+
+pub fn xcursorTheme(
+ allocator: *std.mem.Allocator,
+ seat: *Seat,
+ args: []const []const u8,
+ out: *?[]const u8,
+) Error!void {
+ if (args.len < 2) return Error.NotEnoughArguments;
+ if (args.len > 3) return Error.TooManyArguments;
+
+ // TODO: get rid of this allocation
+ const name = try std.cstr.addNullByte(allocator, args[1]);
+ defer allocator.free(name);
+ const size = if (args.len == 3) try std.fmt.parseInt(u32, args[2], 10) else null;
+
+ try seat.cursor.setTheme(name, size);
+}