selections!

This commit is contained in:
Janis 2022-12-17 15:12:27 +01:00
parent b93dcfb797
commit a9a64d2f18
2 changed files with 290 additions and 18 deletions

View file

@ -70,6 +70,7 @@ pub fn build(b: *std.build.Builder) void {
const exe_tests = b.addTest("main.zig");
exe_tests.setTarget(target);
exe_tests.setBuildMode(mode);
exe_tests.linkLibC();
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&exe_tests.step);

305
main.zig
View file

@ -16,8 +16,17 @@ const Cairo = @cImport({
});
const Point = struct {
const Self = @This();
x: i32 = 0,
y: i32 = 0,
fn inverted(self: *const Self) Self {
return Self{
.x = self.x * -1,
.y = self.y * -1,
};
}
};
const Size = struct {
@ -58,6 +67,7 @@ const Buffer = struct {
} = .{},
size: Size = .{},
data: []u8 = undefined,
in_use: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(false),
fn randomName() [13]u8 {
var name = [_]u8{'/', 'z', 'l', 'u','r','p','-','0','0','0','0','0','0'};
@ -118,6 +128,8 @@ const Buffer = struct {
.ctx = cairo_ctx,
},
};
self.buffer.setListener(*Self, bufferListener, self);
}
fn deinit(self: *Self) void {
@ -127,8 +139,70 @@ const Buffer = struct {
Cairo.cairo_destroy(self.cairo.ctx);
Cairo.cairo_surface_destroy(self.cairo.surface);
}
fn bufferListener(buffer: *wl.Buffer, event: wl.Buffer.Event, self: *Self) void {
_ = buffer;
switch (event) {
.release => {
self.in_use.store(false, .Monotonic);
},
}
}
};
const Buffers = struct {
const Self = @This();
state: *State.Init,
buffers: [2]?Buffer = undefined,
fn init(self: *Self, state: *State.Init) !void {
self.* = Self{
.state = state,
};
for (self.buffers) |*buffer| {
buffer.* = null;
}
}
// returns any already initialized and not currently used buffer
fn getAvailableBuffer(self: *Self, size: Size) ?*Buffer {
for (self.buffers) |*buffer| {
if (buffer.*) |*buf| {
if (!buf.in_use.load(.Monotonic)) {
if (buf.size.width != size.width or buf.size.height != size.height) {
buf.deinit();
buf.* = .{};
buf.init(self.state.shm, size) catch {
buffer.* = null;
continue;
};
}
return buf;
}
}
}
for (self.buffers) |*buffer| {
if (buffer.* == null) {
buffer.* = .{};
buffer.*.?.init(self.state.shm, size) catch {
buffer.* = null;
continue;
};
return &buffer.*.?;
}
}
return null;
}
};
test "buffers" {
std.testing.refAllDecls(Buffers);
}
const Box = struct {
const Self = @This();
@ -183,6 +257,48 @@ const Box = struct {
};
}
fn getInner(comptime T: anytype, v: anytype) T {
switch (@typeInfo(T)) {
.Float => {
return @intToFloat(T, v);
},
.Int => {
return @intCast(T, v);
},
else => {
@compileError("can only cast positional scalar to integer or floating point types");
},
}
}
fn getX(self: *const Self, comptime T: anytype) T {
return getInner(T, self.position.x);
}
fn getY(self: *const Self, comptime T: anytype) T {
return getInner(T, self.position.y);
}
fn getWidth(self: *const Self, comptime T: anytype) T {
return getInner(T, self.extents.width);
}
fn getHeight(self: *const Self, comptime T: anytype) T {
return getInner(T, self.extents.height);
}
fn translate(self: *Self, delta: Point) void {
self.position.x += delta.x;
self.position.y += delta.y;
}
fn translated(self: Self, delta: Point) Self {
var new = self;
new.translate(delta);
return new;
}
fn contains(self: *const Self, point: Point) bool {
const x1 = self.position.x;
const y1 = self.position.y;
@ -352,10 +468,16 @@ const Output = struct {
size: Size = Size{},
configured: bool = false,
needs_redraw: bool = false,
buffers: Buffers = undefined,
frame_callback: ?*wl.Callback = null,
fn init(self: *Self, state: *State.Init) !void {
std.debug.print("output.init()\n", .{});
self.state = state;
try self.buffers.init(state);
self.output.setListener(*Self, outputListener, self);
@ -382,8 +504,126 @@ const Output = struct {
self.output.destroy();
}
fn setSourceU32(c: *Cairo.cairo_t, color: u32) void {
Cairo.cairo_set_source_rgba(
c,
@intToFloat(f32, color >> (3 * 8) & 0xff) / 255.0,
@intToFloat(f32, color >> (2 * 8) & 0xff) / 255.0,
@intToFloat(f32, color >> (1 * 8) & 0xff) / 255.0,
@intToFloat(f32, color >> (0 * 8) & 0xff) / 255.0,
);
}
fn drawRect(buffer: *Buffer, box: Box, color: u32) void {
const cairo = buffer.cairo.ctx;
setSourceU32(cairo, color);
Cairo.cairo_rectangle(
cairo,
box.getX(f64),
box.getY(f64),
box.getWidth(f64),
box.getHeight(f64),
);
}
fn render(self: *Self, buffer: *Buffer) void {
const cairo = &buffer.cairo;
Cairo.cairo_set_operator(cairo.ctx, Cairo.CAIRO_OPERATOR_SOURCE);
setSourceU32(cairo.ctx, 0xFFFFFF40);
Cairo.cairo_paint(cairo.ctx);
var seat_iter = self.state.seats.iterator(.forward);
while (seat_iter.next()) |link| {
const seat = link.get();
if (seat.pointer == null) {
continue;
}
if (seat.pointer.?.selection.getSelectionBox()) |box| {
// draw inner box
const corrected_box = box.translated(self.logical_geometry.position.inverted());
std.debug.print("selection: {}\n", .{corrected_box});
drawRect(buffer, corrected_box, 0x0);
Cairo.cairo_fill(cairo.ctx);
Cairo.cairo_set_line_width(cairo.ctx, 2.0);
drawRect(buffer, corrected_box, 0x000000FF);
Cairo.cairo_stroke(cairo.ctx);
// TODO: draw dimensions
}
}
}
fn commitFrame(self: *Self) void {
_ = self;
const size = Size{
.width = self.size.width * @intCast(u32, self.scale),
.height = self.size.height * @intCast(u32, self.scale),
};
// FIXME: maybe somehow make this retry to get another buffer if the one
// that was returned has just become in-use?
if (self.buffers.getAvailableBuffer(size)) |buffer| {
if (buffer.in_use.compareAndSwap(false, true, .Release, .Monotonic) == null) {
Cairo.cairo_identity_matrix(buffer.cairo.ctx);
Cairo.cairo_scale(
buffer.cairo.ctx,
@intToFloat(f64, self.scale),
@intToFloat(f64, self.scale),
);
// RENDER
self.render(buffer);
if (self.frame_callback == null) {
self.frame_callback = self.surface.frame() catch null;
if (self.frame_callback) |frame| {
frame.setListener(*Self, frameCallbackListener, self);
}
}
self.surface.attach(buffer.buffer, 0, 0);
self.surface.damage(0, 0,
@intCast(i32, self.size.width),
@intCast(i32, self.size.height),
);
self.surface.setBufferScale(self.scale);
self.surface.commit();
self.needs_redraw = false;
} else {
std.debug.print("failed to CAS in_use.\n", .{});
}
} else {
std.debug.print("No buffer available!\n", .{});
}
}
fn requestRedraw(self: *Self) !void {
self.needs_redraw = true;
if (self.frame_callback == null) {
self.frame_callback = try self.surface.frame();
self.frame_callback.?.setListener(*Self, frameCallbackListener, self);
self.surface.commit();
}
}
fn frameCallbackListener(frame: *wl.Callback, event: wl.Callback.Event, self: *Self) void {
switch (event) {
.done => {
frame.destroy();
self.frame_callback = null;
if (self.needs_redraw) {
self.commitFrame();
}
std.debug.print("frame_callback done\n", .{});
},
}
}
fn xdgOutputListener(output: *zxdg.OutputV1, event: zxdg.OutputV1.Event, self: *Self) void {
@ -494,6 +734,18 @@ const Seat = struct {
fn None() SelectionState {
return SelectionState.none;
}
fn getSelectionBox(self: SelectionState) ?Box {
switch (self) {
.updating => |selection| {
return selection.asBox();
},
.post => |box| {
return box;
},
else => {return null;},
}
}
};
fn init(self: *Self, state: *State.Init) void {
@ -503,11 +755,23 @@ const Seat = struct {
self.seat.setListener(*Self, seatListener, self);
}
fn calculateRedraws(self: *Self) void {
if (self.pointer) |ptr| {
const box = ptr.selection.getSelectionBox();
var output_iter = self.state.outputs.iterator(.forward);
while (output_iter.next()) |link| {
const output = link.get();
if (box == null or output.logical_geometry.intersects(&box.?)) {
output.requestRedraw() catch {};
}
}
}
}
fn pointerListener(pointer: *wl.Pointer, event: wl.Pointer.Event, self: *Self) void {
_ = pointer;
std.debug.print("selection: {}\n", .{self.pointer.?.selection});
switch (event) {
.enter => {},
.leave => {},
@ -520,9 +784,11 @@ const Seat = struct {
switch (self.pointer.?.selection) {
.pre => {
self.pointer.?.selection = .{.updating = .{.start = end, .end = end}};
return self.calculateRedraws();
},
.updating => |*selection| {
selection.end = end;
return self.calculateRedraws();
},
else => {},
}
@ -537,6 +803,7 @@ const Seat = struct {
.updating => |selection| {
const box = selection.asBox();
self.pointer.?.selection = .{.post = box};
return self.calculateRedraws();
},
else => {},
}
@ -614,7 +881,20 @@ const Seat = struct {
.pressed => {
switch (keysym) {
xkb.XKB_KEY_Escape, xkb.XKB_KEY_q => {
if (self.pointer) |*ptr| {
switch (ptr.selection) {
.none => {
self.state.running = false;
return;
},
else => {
ptr.selection = .none;
return self.calculateRedraws();
}
}
}
},
else => {},
}
@ -914,25 +1194,16 @@ const State = struct {
}
fn stuff(self: *Self) !void {
var it = self.outputs.iterator(.forward);
if (it.next()) |link| {
const output = link.get();
var buffer: Buffer = undefined;
const width = output.size.width * @intCast(u32, output.scale);
const height = output.size.height * @intCast(u32, output.scale);
try buffer.init(self.shm, .{.width = width, .height = height});
std.mem.copy(u8, buffer.data, @embedFile("cat.bgra"));
output.surface.attach(buffer.buffer, 0, 0);
output.surface.commit();
}
while (self.running) {
if (self.dpy.dispatch() != .SUCCESS) return error.DispatchFailed;
}
}
fn finish(self: *Self, box: Box) void {
_ = self;
_ = box;
}
fn removeOutput(self: *Self, output: *Output) void {
const link = ListItem(Output).fromValue(output);
link.remove();