cursor: make xcursor theme configurable
- add a new command to set the theme - export the theme of the default seat through environment variables
This commit is contained in:
parent
f3d4e5ac53
commit
6bdb152808
@ -124,6 +124,12 @@ that tag 1 through 9 are visible.
|
|||||||
- *outer_padding* _non-negative_integer_
|
- *outer_padding* _non-negative_integer_
|
||||||
- *view_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
|
# EXAMPLES
|
||||||
|
|
||||||
Bind bemenu-run to Super+P:
|
Bind bemenu-run to Super+P:
|
||||||
|
@ -39,6 +39,8 @@ const CursorMode = enum {
|
|||||||
Resize,
|
Resize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const default_size = 24;
|
||||||
|
|
||||||
seat: *Seat,
|
seat: *Seat,
|
||||||
wlr_cursor: *c.wlr_cursor,
|
wlr_cursor: *c.wlr_cursor,
|
||||||
wlr_xcursor_manager: *c.wlr_xcursor_manager,
|
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.
|
// Creates a wlroots utility for tracking the cursor image shown on screen.
|
||||||
self.wlr_cursor = c.wlr_cursor_create() orelse return error.OutOfMemory;
|
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);
|
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) {
|
// This is here so that self.wlr_xcursor_manager doesn't need to be an
|
||||||
if (c.wlr_xcursor_manager_get_xcursor(
|
// optional pointer. This isn't optimal as it does a needless allocation,
|
||||||
self.wlr_xcursor_manager,
|
// but this is not a hot path.
|
||||||
"left_ptr",
|
self.wlr_xcursor_manager = c.wlr_xcursor_manager_create(null, default_size) orelse
|
||||||
1,
|
return error.OutOfMemory;
|
||||||
)) |wlr_xcursor| {
|
try self.setTheme(null, null);
|
||||||
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", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.mode = CursorMode.Passthrough;
|
self.mode = CursorMode.Passthrough;
|
||||||
self.grabbed_view = undefined;
|
self.grabbed_view = undefined;
|
||||||
@ -136,6 +117,49 @@ pub fn deinit(self: *Self) void {
|
|||||||
c.wlr_cursor_destroy(self.wlr_cursor);
|
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 {
|
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,
|
// This event is forwarded by the cursor when a pointer emits an axis event,
|
||||||
// for example when you move the scroll wheel.
|
// for example when you move the scroll wheel.
|
||||||
|
@ -39,6 +39,7 @@ const impl = struct {
|
|||||||
const toggleFocusedTags = @import("command/tags.zig").toggleFocusedTags;
|
const toggleFocusedTags = @import("command/tags.zig").toggleFocusedTags;
|
||||||
const toggleFullscreen = @import("command/toggle_fullscreen.zig").toggleFullscreen;
|
const toggleFullscreen = @import("command/toggle_fullscreen.zig").toggleFullscreen;
|
||||||
const toggleViewTags = @import("command/tags.zig").toggleViewTags;
|
const toggleViewTags = @import("command/tags.zig").toggleViewTags;
|
||||||
|
const xcursorTheme = @import("command/xcursor_theme.zig").xcursorTheme;
|
||||||
const zoom = @import("command/zoom.zig").zoom;
|
const zoom = @import("command/zoom.zig").zoom;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,9 +80,10 @@ const str_to_impl_fn = [_]struct {
|
|||||||
.{ .name = "spawn", .impl = impl.spawn },
|
.{ .name = "spawn", .impl = impl.spawn },
|
||||||
.{ .name = "toggle-float", .impl = impl.toggleFloat },
|
.{ .name = "toggle-float", .impl = impl.toggleFloat },
|
||||||
.{ .name = "toggle-focused-tags", .impl = impl.toggleFocusedTags },
|
.{ .name = "toggle-focused-tags", .impl = impl.toggleFocusedTags },
|
||||||
.{ .name = "toggle-view-tags", .impl = impl.toggleViewTags },
|
|
||||||
.{ .name = "zoom", .impl = impl.zoom },
|
|
||||||
.{ .name = "toggle-fullscreen", .impl = impl.toggleFullscreen },
|
.{ .name = "toggle-fullscreen", .impl = impl.toggleFullscreen },
|
||||||
|
.{ .name = "toggle-view-tags", .impl = impl.toggleViewTags },
|
||||||
|
.{ .name = "xcursor-theme", .impl = impl.xcursorTheme },
|
||||||
|
.{ .name = "zoom", .impl = impl.zoom },
|
||||||
};
|
};
|
||||||
// zig fmt: on
|
// zig fmt: on
|
||||||
|
|
||||||
|
38
river/command/xcursor_theme.zig
Normal file
38
river/command/xcursor_theme.zig
Normal file
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user