diff --git a/docs/TODO.md b/docs/TODO.md index d151e8f..e9ef37c 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -2,7 +2,6 @@ These are in rough order of my priority, though no promises I do them in this order. -- [ ] Make a Rect struct to combine x, y, width, and height - [ ] Support window rules (float/tags/SSD by app-id/title) - [ ] Support overriding config location - [ ] Support configuring primary vs secondary stack side @@ -31,3 +30,4 @@ These are in rough order of my priority, though no promises I do them in this or - [x] Implement a river-tag-overlay clone - [x] Add options to the tag overlay - [x] Add options to the bar +- [x] Make a Rect struct to combine x, y, width, and height diff --git a/src/Bar.zig b/src/Bar.zig index e184206..275e5a4 100644 --- a/src/Bar.zig +++ b/src/Bar.zig @@ -15,7 +15,7 @@ timezone: zeit.timezone.TimeZone, options: Options, fcft_fonts: *fcft.Font, -font_scale: u31, +font_scale: u31 = 1, output: *Output, @@ -366,6 +366,7 @@ fn getFcftFonts(fonts: []const u8, scale: u31) !*fcft.Font { while (it.next()) |font| { if (scale > 1) { // If scale >1, we append :dpi so we can scale the font + log.debug("bwbuhse {d} {d}", .{ base_dpi, scale }); const scaled = try arena_alloc.dupeZ( u8, try std.fmt.allocPrint(arena_alloc, "{s}:dpi={}", .{ font, @as(u32, base_dpi) * scale }), diff --git a/src/Output.zig b/src/Output.zig index 4190fb6..b4ba827 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -92,34 +92,34 @@ pub fn create(context: *Context, river_output_v1: *river.OutputV1) !*Output { var output = try utils.gpa.create(Output); errdefer utils.gpa.destroy(output); - var bar = if (context.config.bar_config) |bar_config| blk: { - break :blk Bar.init(context, output, bar_config.toBarOptions()) catch |e| { - log.err("Failed to create a bar: {}", .{e}); - break :blk null; - }; - } else null; - errdefer if (bar) |*b| b.deinit(); - - var tag_overlay = if (context.config.tag_overlay_config) |tag_overlay_config| blk: { - break :blk TagOverlay.init(context, output, tag_overlay_config.toTagOverlayOptions()) catch |e| { - log.err("Failed to create a tag overlay: {}", .{e}); - break :blk null; - }; - } else null; - errdefer if (tag_overlay) |*to| to.deinit(); - output.* = .{ .context = context, .river_output_v1 = river_output_v1, .river_layer_shell_output_v1 = try context.river_layer_shell_v1.getOutput(river_output_v1), - .bar = bar, - .tag_overlay = tag_overlay, + .bar = null, + .tag_overlay = null, .primary_count = context.config.primary_count, .primary_ratio = context.config.primary_ratio, .windows = undefined, // we will initialize this shortly .link = undefined, // Handled by the wl.list }; + output.bar = if (context.config.bar_config) |bar_config| blk: { + break :blk Bar.init(context, output, bar_config.toBarOptions()) catch |e| { + log.err("Failed to create a bar: {}", .{e}); + break :blk null; + }; + } else null; + errdefer if (output.bar) |*b| b.deinit(); + + output.tag_overlay = if (context.config.tag_overlay_config) |tag_overlay_config| blk: { + break :blk TagOverlay.init(context, output, tag_overlay_config.toTagOverlayOptions()) catch |e| { + log.err("Failed to create a tag overlay: {}", .{e}); + break :blk null; + }; + } else null; + errdefer if (output.tag_overlay) |*to| to.deinit(); + output.windows.init(); output.river_output_v1.setListener(*Output, riverOutputListener, output); @@ -666,10 +666,11 @@ fn calculatePrimaryStackLayout(output: *Output) void { // Single window: maximize and return early if (active_count == 1) { const window: *Window = @fieldParentPtr("active_list_node", active_list.popFirst().?); - window.pending_render.x = output_x + border_width; - window.pending_render.y = output_y + border_width; - window.pending_manage.width = output_width - 2 * border_width; - window.pending_manage.height = output_height - 2 * border_width; + window.pending_render.position = .{ .x = output_x + border_width, .y = output_y + border_width }; + window.pending_manage.dimensions = .{ + .width = output_width - 2 * border_width, + .height = output_height - 2 * border_width, + }; window.pending_manage.maximized = true; return; } @@ -700,34 +701,44 @@ fn calculatePrimaryStackLayout(output: *Output) void { if (i < primary_count) { // Primary window(s) - right side - window.pending_render.x = output_x + @as(i32, stack_width); - window.pending_render.y = output_y + @as(i32, i) * @as(i32, primary_height); - window.pending_manage.width = primary_width; + window.pending_render.position = .{ + .x = output_x + @as(i32, stack_width), + .y = output_y + @as(i32, i) * @as(i32, primary_height), + }; + const pending_width = primary_width; // Last primary window gets remaining height to avoid gaps from integer division - if (i == primary_count - 1) { - window.pending_manage.height = output_height - i * primary_height; - } else { - window.pending_manage.height = primary_height; - } + const pending_height = if (i == primary_count - 1) + output_height - i * primary_height + else + primary_height; + window.pending_manage.dimensions = .{ + .width = pending_width, + .height = pending_height, + }; } else { // Stack window(s) - left side const stack_index = i - primary_count; - window.pending_render.x = output_x; - window.pending_render.y = output_y + @as(i32, stack_index) * @as(i32, stack_height); - window.pending_manage.width = stack_width; + window.pending_render.position = .{ + .x = output_x, + .y = output_y + @as(i32, stack_index) * @as(i32, stack_height), + }; + const pending_width = stack_width; // Last stack window gets remaining height to avoid gaps from integer division - if (stack_index == stack_count - 1) { - window.pending_manage.height = output_height - stack_index * stack_height; - } else { - window.pending_manage.height = stack_height; - } + const pending_height = if (stack_index == stack_count - 1) + output_height - stack_index * stack_height + else + stack_height; + window.pending_manage.dimensions = .{ + .width = pending_width, + .height = pending_height, + }; } // Make space for borders - window.pending_manage.height.? -= 2 * border_width; - window.pending_manage.width.? -= 2 * border_width; - window.pending_render.x.? += border_width; - window.pending_render.y.? += border_width; + window.pending_manage.dimensions.?.height -= 2 * border_width; + window.pending_manage.dimensions.?.width -= 2 * border_width; + window.pending_render.position.?.x += border_width; + window.pending_render.position.?.y += border_width; } // Make sure we went through the whole list diff --git a/src/Seat.zig b/src/Seat.zig index 0bcda3e..b24e050 100644 --- a/src/Seat.zig +++ b/src/Seat.zig @@ -201,8 +201,9 @@ pub fn manage(seat: *Seat) void { // Warp pointer to center of focused window; // because the x and y coords are set during render, we need to check if // there are new coordinates in window.pending_render. - const pointer_x: i32 = (window.pending_render.x orelse window.x) + @divFloor(window.width, 2); - const pointer_y: i32 = (window.pending_render.y orelse window.y) + @divFloor(window.height, 2); + const pos = window.pending_render.position; + const pointer_x: i32 = (if (pos) |p| p.x else window.rect.x) + @divFloor(window.rect.width, 2); + const pointer_y: i32 = (if (pos) |p| p.y else window.rect.y) + @divFloor(window.rect.height, 2); seat.river_seat_v1.pointerWarp(pointer_x, pointer_y); } } @@ -216,8 +217,8 @@ pub fn manage(seat: *Seat) void { seat.pointer_op = .{ .move = .{ .window = window, - .start_x = window.float_x, - .start_y = window.float_y, + .start_x = window.floating_rect.x, + .start_y = window.floating_rect.y, }, }; seat.river_seat_v1.opStartPointer(); @@ -230,10 +231,10 @@ pub fn manage(seat: *Seat) void { seat.pointer_op = .{ .resize = .{ .window = req.window, - .start_width = req.window.float_width, - .start_height = req.window.float_height, - .start_x = req.window.float_x, - .start_y = req.window.float_y, + .start_width = req.window.floating_rect.width, + .start_height = req.window.floating_rect.height, + .start_x = req.window.floating_rect.x, + .start_y = req.window.floating_rect.y, .edges = req.edges, }, }; @@ -247,10 +248,12 @@ pub fn manage(seat: *Seat) void { switch (seat.pointer_op) { .none => {}, .move => |op| { - op.window.float_x = op.start_x + delta.dx; - op.window.float_y = op.start_y + delta.dy; - op.window.pending_render.x = op.window.float_x; - op.window.pending_render.y = op.window.float_y; + op.window.floating_rect.x = op.start_x + delta.dx; + op.window.floating_rect.y = op.start_y + delta.dy; + op.window.pending_render.position = .{ + .x = op.window.floating_rect.x, + .y = op.window.floating_rect.y, + }; }, .resize => |op| { var new_width: i32 = op.start_width; @@ -277,17 +280,19 @@ pub fn manage(seat: *Seat) void { new_width = @max(new_width, min_size); new_height = @max(new_height, min_size); - op.window.float_width = @intCast(new_width); - op.window.float_height = @intCast(new_height); - op.window.float_x = new_x; - op.window.float_y = new_y; + op.window.floating_rect.width = @intCast(new_width); + op.window.floating_rect.height = @intCast(new_height); + op.window.floating_rect.x = new_x; + op.window.floating_rect.y = new_y; op.window.river_window_v1.proposeDimensions( - op.window.float_width, - op.window.float_height, + op.window.floating_rect.width, + op.window.floating_rect.height, ); - op.window.pending_render.x = op.window.float_x; - op.window.pending_render.y = op.window.float_y; + op.window.pending_render.position = .{ + .x = op.window.floating_rect.x, + .y = op.window.floating_rect.y, + }; }, } } diff --git a/src/Window.zig b/src/Window.zig index d3c7f1e..a21de68 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -9,11 +9,12 @@ context: *Context, river_window_v1: *river.WindowV1, river_node_v1: *river.NodeV1, -// TODO: Could switch this to a Rect { x, y, width, height } -width: u31 = 0, -height: u31 = 0, -x: i32 = 0, -y: i32 = 0, +rect: utils.Rect = .{ + .width = 0, + .height = 0, + .x = 0, + .y = 0, +}, fullscreen: bool = false, maximized: bool = false, @@ -22,10 +23,12 @@ tags: u32 = 0x0001, output: ?*Output, floating: bool = false, -float_width: u31 = 0, -float_height: u31 = 0, -float_x: i32 = 0, -float_y: i32 = 0, +floating_rect: utils.Rect = .{ + .width = 0, + .height = 0, + .x = 0, + .y = 0, +}, initialized: bool = false, @@ -41,8 +44,7 @@ active_list_node: DoublyLinkedList.Node = .{}, link: wl.list.Link, pub const PendingManage = struct { - width: ?u31 = null, - height: ?u31 = null, + dimensions: ?struct { width: u31, height: u31 } = null, fullscreen: ?bool = null, maximized: ?bool = null, @@ -59,8 +61,10 @@ pub const PendingManage = struct { }; pub const PendingRender = struct { - x: ?i32 = null, - y: ?i32 = null, + position: ?struct { + x: i32, + y: i32, + } = null, focused: ?bool = null, @@ -142,8 +146,7 @@ fn windowListener(river_window_v1: *river.WindowV1, event: river.WindowV1.Event, .dimensions => |ev| { // Protocol guarantees that width and height are strictly greater than zero assert(ev.width > 0 and ev.height > 0); - window.pending_manage.width = @intCast(ev.width); - window.pending_manage.height = @intCast(ev.height); + window.pending_manage.dimensions = .{ .width = @intCast(ev.width), .height = @intCast(ev.height) }; }, .dimensions_hint => { // TODO: Maybe could use this for floating windows @@ -183,40 +186,37 @@ pub fn manage(window: *Window) void { // Let the window know it isn't tiled river_window_v1.setTiled(.{}); - if (window.float_width == 0) { + if (window.floating_rect.width == 0) { // Never floated before; use 75% of usable area, centered on output if (window.output) |output| { - window.float_width = @divFloor(output.usable_width * 3, 4); - window.float_height = @divFloor(output.usable_height * 3, 4); - window.float_x = output.usable_x + @divFloor(output.usable_width, 2) - @divFloor(window.float_width, 2); - window.float_y = output.usable_y + @divFloor(output.usable_height, 2) - @divFloor(window.float_height, 2); + window.floating_rect.width = @divFloor(output.usable_width * 3, 4); + window.floating_rect.height = @divFloor(output.usable_height * 3, 4); + window.floating_rect.x = output.usable_x + @divFloor(output.usable_width, 2) - @divFloor(window.floating_rect.width, 2); + window.floating_rect.y = output.usable_y + @divFloor(output.usable_height, 2) - @divFloor(window.floating_rect.height, 2); } else { - window.float_width = window.width; - window.float_height = window.height; + window.floating_rect.width = window.rect.width; + window.floating_rect.height = window.rect.height; } - river_window_v1.proposeDimensions(window.float_width, window.float_height); - } else { - // Window has floated before; re-use its old dimensions - river_window_v1.proposeDimensions(window.float_width, window.float_height); } - window.pending_render.x = window.float_x; - window.pending_render.y = window.float_y; + river_window_v1.proposeDimensions(window.floating_rect.width, window.floating_rect.height); + window.pending_render.position = .{ + .x = window.floating_rect.x, + .y = window.floating_rect.y, + }; } else { river_window_v1.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true }); // Save floating dimensions in case the window gets floated again - window.float_width = window.width; - window.float_height = window.height; - window.float_x = window.x; - window.float_y = window.y; + window.floating_rect.width = window.rect.width; + window.floating_rect.height = window.rect.height; + window.floating_rect.x = window.rect.x; + window.floating_rect.y = window.rect.y; } } // Layout (pre-computed by WindowManager.calculatePrimaryStackLayout()) - if (pending_manage.width) |new_width| { - if (pending_manage.height) |new_height| { - window.width = new_width; - window.height = new_height; - window.river_window_v1.proposeDimensions(new_width, new_height); - } + if (pending_manage.dimensions) |dimensions| { + window.rect.width = dimensions.width; + window.rect.height = dimensions.height; + window.river_window_v1.proposeDimensions(dimensions.width, dimensions.height); } // Fullscreen and maximize operations if (pending_manage.fullscreen) |fullscreen| blk: { @@ -257,19 +257,10 @@ pub fn manage(window: *Window) void { pub fn render(window: *Window) void { defer window.pending_render = .{}; - // TODO: We probably could just move these back to pending_manage and have PendingRiver.new_coords: bool - // This would also simplify the pointer warp behaviour in Seat.manage() - if (window.pending_render.x) |new_x| { - if (window.pending_render.y) |new_y| { - window.x = new_x; - window.y = new_y; - - window.river_node_v1.setPosition(window.x, window.y); - } else { - log.err("Window.pending_render with only x set", .{}); - } - } else if (window.pending_render.y) |_| { - log.err("Window.pending_render with only y set", .{}); + if (window.pending_render.position) |position| { + window.rect.x = position.x; + window.rect.y = position.y; + window.river_node_v1.setPosition(window.rect.x, window.rect.y); } // Set borders diff --git a/src/XkbBindings.zig b/src/XkbBindings.zig index 20e9ae9..eaf9b40 100644 --- a/src/XkbBindings.zig +++ b/src/XkbBindings.zig @@ -341,10 +341,9 @@ const XkbBinding = struct { const window = seat.focused_window orelse return; if (!window.floating) return; - window.float_x += dx; - window.float_y += dy; - window.pending_render.x = window.float_x; - window.pending_render.y = window.float_y; + window.floating_rect.x += dx; + window.floating_rect.y += dy; + window.pending_render.position = .{ .x = window.floating_rect.x, .y = window.floating_rect.y }; context.wm.river_window_manager_v1.manageDirty(); } @@ -353,13 +352,12 @@ const XkbBinding = struct { const window = seat.focused_window orelse return; if (!window.floating) return; - const new_width: i32 = @as(i32, window.float_width) + dw; - const new_height: i32 = @as(i32, window.float_height) + dh; - window.float_width = @intCast(@max(50, new_width)); - window.float_height = @intCast(@max(50, new_height)); + const new_width: i32 = @as(i32, window.floating_rect.width) + dw; + const new_height: i32 = @as(i32, window.floating_rect.height) + dh; + window.floating_rect.width = @intCast(@max(50, new_width)); + window.floating_rect.height = @intCast(@max(50, new_height)); - window.pending_manage.width = window.float_width; - window.pending_manage.height = window.float_height; + window.pending_manage.dimensions = .{ .width = window.floating_rect.width, .height = window.floating_rect.height }; context.wm.river_window_manager_v1.manageDirty(); } @@ -369,10 +367,9 @@ const XkbBinding = struct { if (!window.floating) return; const output = window.output orelse return; - window.float_x = output.usable_x + @divFloor(output.usable_width, 2) - @divFloor(window.float_width, 2); - window.float_y = output.usable_y + @divFloor(output.usable_height, 2) - @divFloor(window.float_height, 2); - window.pending_render.x = window.float_x; - window.pending_render.y = window.float_y; + window.floating_rect.x = output.usable_x + @divFloor(output.usable_width, 2) - @divFloor(window.floating_rect.width, 2); + window.floating_rect.y = output.usable_y + @divFloor(output.usable_height, 2) - @divFloor(window.floating_rect.height, 2); + window.pending_render.position = .{ .x = window.floating_rect.x, .y = window.floating_rect.y }; context.wm.river_window_manager_v1.manageDirty(); } diff --git a/src/utils.zig b/src/utils.zig index fa6a171..d162bf9 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -12,6 +12,13 @@ pub const RiverColor = struct { alpha: u32, }; +pub const Rect = struct { + width: u31, + height: u31, + x: i32, + y: i32, +}; + /// Parse a color in the format 0xRRGGBB or 0xRRGGBBAA and convert it to /// 32-bit color values (used by Window.set_borders in rwm). pub fn parseRgba(s: []const u8) !RiverColor {