aboutsummaryrefslogtreecommitdiff
path: root/src/view.zig
blob: cb9ed33eca4cc520c5bf7fd7ac95660505628ca1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
const std = @import("std");
const c = @import("c.zig");

const Box = @import("box.zig");
const Log = @import("log.zig").Log;
const Output = @import("output.zig").Output;
const Root = @import("root.zig").Root;
const ViewStack = @import("view_stack.zig").ViewStack;
const XdgToplevel = @import("xdg_toplevel.zig");

pub const View = struct {
    const Self = @This();

    const ViewImpl = union(enum) {
        xdg_toplevel: XdgToplevel,
    };

    /// The implementation of this view
    impl: ViewImpl,

    /// The output this view is currently associated with
    output: *Output,

    /// This is non-null exactly when the view is mapped
    wlr_surface: ?*c.wlr_surface,

    /// If the view is floating or not
    floating: bool,

    /// True if the view is currentlt focused by at lease one seat
    focused: bool,

    /// The current output-relative coordinates and dimensions of the view
    current_box: Box,
    pending_box: ?Box,

    /// The dimensions the view would have taken if we didn't force it to tile
    natural_width: u32,
    natural_height: u32,

    current_tags: u32,
    pending_tags: ?u32,

    pending_serial: ?u32,

    // This is what we render while a transaction is in progress
    stashed_buffer: ?*c.wlr_buffer,

    pub fn init_xdg_toplevel(
        self: *Self,
        output: *Output,
        tags: u32,
        wlr_xdg_surface: *c.wlr_xdg_surface,
    ) void {
        self.output = output;

        self.wlr_surface = null;

        self.focused = false;

        self.current_box = Box{
            .x = 0,
            .y = 0,
            .height = 0,
            .width = 0,
        };
        self.pending_box = null;

        self.current_tags = tags;
        self.pending_tags = null;

        self.pending_serial = null;

        self.stashed_buffer = null;

        self.impl = .{ .xdg_toplevel = undefined };
        self.impl.xdg_toplevel.init(self, wlr_xdg_surface);
    }

    pub fn deinit(self: *Self) void {
        if (self.stashed_buffer) |buffer| {
            c.wlr_buffer_unref(buffer);
        }
    }

    pub fn needsConfigure(self: Self) bool {
        if (self.pending_box) |pending_box| {
            return pending_box.width != self.current_box.width or
                pending_box.height != self.current_box.height;
        } else {
            return false;
        }
    }

    pub fn configure(self: Self) void {
        if (self.pending_box) |pending_box| {
            switch (self.impl) {
                .xdg_toplevel => |xdg_toplevel| xdg_toplevel.configure(pending_box),
            }
        } else {
            Log.Error.log("Configure called on a View with no pending box", .{});
        }
    }

    pub fn sendFrameDone(self: Self) void {
        var now: c.timespec = undefined;
        _ = c.clock_gettime(c.CLOCK_MONOTONIC, &now);
        c.wlr_surface_send_frame_done(self.wlr_surface.?, &now);
    }

    pub fn dropStashedBuffer(self: *Self) void {
        // TODO: log debug error
        if (self.stashed_buffer) |buffer| {
            c.wlr_buffer_unref(buffer);
            self.stashed_buffer = null;
        }
    }

    pub fn stashBuffer(self: *Self) void {
        // TODO: log debug error if there is already a saved buffer
        if (self.wlr_surface) |wlr_surface| {
            if (c.wlr_surface_has_buffer(wlr_surface)) {
                _ = c.wlr_buffer_ref(wlr_surface.buffer);
                self.stashed_buffer = wlr_surface.buffer;
            }
        }
    }

    /// Set the focued bool and the active state of the view if it is a toplevel
    pub fn setFocused(self: *Self, focused: bool) void {
        self.focused = focused;
        switch (self.impl) {
            .xdg_toplevel => |xdg_toplevel| xdg_toplevel.setActivated(focused),
        }
    }

    /// If true is passsed, make the view float. If false, return it to the tiled
    /// layout.
    pub fn setFloating(self: *Self, float: bool) void {
        if (float and !self.floating) {
            self.floating = true;
            self.pending_box = Box{
                .x = std.math.max(0, @divTrunc(@intCast(i32, self.output.usable_box.width) -
                    @intCast(i32, self.natural_width), 2)),
                .y = std.math.max(0, @divTrunc(@intCast(i32, self.output.usable_box.height) -
                    @intCast(i32, self.natural_height), 2)),
                .width = self.natural_width,
                .height = self.natural_height,
            };
        } else if (!float and self.floating) {
            self.floating = false;
        }
    }

    /// Move a view from one output to another, sending the required enter/leave
    /// events.
    pub fn sendToOutput(self: *Self, destination_output: *Output) void {
        const node = @fieldParentPtr(ViewStack(View).Node, "view", self);

        self.output.views.remove(node);
        destination_output.views.push(node);

        c.wlr_surface_send_leave(self.wlr_surface, self.output.wlr_output);
        c.wlr_surface_send_enter(self.wlr_surface, destination_output.wlr_output);

        self.output = destination_output;
    }

    pub fn close(self: Self) void {
        switch (self.impl) {
            .xdg_toplevel => |xdg_toplevel| xdg_toplevel.close(),
        }
    }

    pub fn forEachSurface(
        self: Self,
        iterator: c.wlr_surface_iterator_func_t,
        user_data: ?*c_void,
    ) void {
        switch (self.impl) {
            .xdg_toplevel => |xdg_toplevel| xdg_toplevel.forEachSurface(iterator, user_data),
        }
    }
};