Initial commit
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
// It's also possible to define more custom flags to toggle optional features
|
||||||
|
// of this build script using `b.option()`. All defined flags (including
|
||||||
|
// target and optimize options) will be listed when running `zig build --help`
|
||||||
|
// in this directory.
|
||||||
|
|
||||||
|
const mod = b.addModule("puzzle", .{
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
.target = target,
|
||||||
|
});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "wl-main",
|
||||||
|
.root_module = b.createModule(.{
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.imports = &.{
|
||||||
|
.{ .name = "puzzle", .module = mod },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
exe.root_module.link_libc = true;
|
||||||
|
exe.root_module.linkSystemLibrary("wayland-client", .{});
|
||||||
|
|
||||||
|
exe.root_module.addIncludePath(b.path("src"));
|
||||||
|
exe.root_module.addCSourceFiles(.{
|
||||||
|
.files = &.{ "src/xdg-shell.c", },
|
||||||
|
.language = .c,
|
||||||
|
});
|
||||||
|
|
||||||
|
b.installArtifact(exe);
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
.{
|
||||||
|
// This is the default name used by packages depending on this one. For
|
||||||
|
// example, when a user runs `zig fetch --save <url>`, this field is used
|
||||||
|
// as the key in the `dependencies` table. Although the user can choose a
|
||||||
|
// different name, most users will stick with this provided value.
|
||||||
|
//
|
||||||
|
// It is redundant to include "zig" in this name because it is already
|
||||||
|
// within the Zig package namespace.
|
||||||
|
.name = .Zig_A_Puzzle_A_Day,
|
||||||
|
// This is a [Semantic Version](https://semver.org/).
|
||||||
|
// In a future version of Zig it will be used for package deduplication.
|
||||||
|
.version = "0.0.0",
|
||||||
|
// Together with name, this represents a globally unique package
|
||||||
|
// identifier. This field is generated by the Zig toolchain when the
|
||||||
|
// package is first created, and then *never changes*. This allows
|
||||||
|
// unambiguous detection of one package being an updated version of
|
||||||
|
// another.
|
||||||
|
//
|
||||||
|
// When forking a Zig project, this id should be regenerated (delete the
|
||||||
|
// field and run `zig build`) if the upstream project is still maintained.
|
||||||
|
// Otherwise, the fork is *hostile*, attempting to take control over the
|
||||||
|
// original project's identity. Thus it is recommended to leave the comment
|
||||||
|
// on the following line intact, so that it shows up in code reviews that
|
||||||
|
// modify the field.
|
||||||
|
.fingerprint = 0x5fad1fab9564c392, // Changing this has security and trust implications.
|
||||||
|
// Tracks the earliest Zig version that the package considers to be a
|
||||||
|
// supported use case.
|
||||||
|
.minimum_zig_version = "0.15.2",
|
||||||
|
// This field is optional.
|
||||||
|
// Each dependency must either provide a `url` and `hash`, or a `path`.
|
||||||
|
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
|
||||||
|
// Once all dependencies are fetched, `zig build` no longer requires
|
||||||
|
// internet connectivity.
|
||||||
|
.dependencies = .{
|
||||||
|
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
|
||||||
|
//.example = .{
|
||||||
|
// // When updating this field to a new URL, be sure to delete the corresponding
|
||||||
|
// // `hash`, otherwise you are communicating that you expect to find the old hash at
|
||||||
|
// // the new URL. If the contents of a URL change this will result in a hash mismatch
|
||||||
|
// // which will prevent zig from using it.
|
||||||
|
// .url = "https://example.com/foo.tar.gz",
|
||||||
|
//
|
||||||
|
// // This is computed from the file contents of the directory of files that is
|
||||||
|
// // obtained after fetching `url` and applying the inclusion rules given by
|
||||||
|
// // `paths`.
|
||||||
|
// //
|
||||||
|
// // This field is the source of truth; packages do not come from a `url`; they
|
||||||
|
// // come from a `hash`. `url` is just one of many possible mirrors for how to
|
||||||
|
// // obtain a package matching this `hash`.
|
||||||
|
// //
|
||||||
|
// // Uses the [multihash](https://multiformats.io/multihash/) format.
|
||||||
|
// .hash = "...",
|
||||||
|
//
|
||||||
|
// // When this is provided, the package is found in a directory relative to the
|
||||||
|
// // build root. In this case the package's hash is irrelevant and therefore not
|
||||||
|
// // computed. This field and `url` are mutually exclusive.
|
||||||
|
// .path = "foo",
|
||||||
|
//
|
||||||
|
// // When this is set to `true`, a package is declared to be lazily
|
||||||
|
// // fetched. This makes the dependency only get fetched if it is
|
||||||
|
// // actually used.
|
||||||
|
// .lazy = false,
|
||||||
|
//},
|
||||||
|
},
|
||||||
|
// Specifies the set of files and directories that are included in this package.
|
||||||
|
// Only files and directories listed here are included in the `hash` that
|
||||||
|
// is computed for this package. Only files listed here will remain on disk
|
||||||
|
// when using the zig package manager. As a rule of thumb, one should list
|
||||||
|
// files required for compilation plus any license(s).
|
||||||
|
// Paths are relative to the build root. Use the empty string (`""`) to refer to
|
||||||
|
// the build root itself.
|
||||||
|
// A directory listed here means that all files within, recursively, are included.
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
// For example...
|
||||||
|
//"LICENSE",
|
||||||
|
//"README.md",
|
||||||
|
},
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
|||||||
|
with import <nixpkgs> {};
|
||||||
|
mkShell {
|
||||||
|
packages = [
|
||||||
|
zig
|
||||||
|
zls
|
||||||
|
wayland-scanner
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = [
|
||||||
|
wayland
|
||||||
|
];
|
||||||
|
}
|
||||||
+849
@@ -0,0 +1,849 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const puzzle = @import("puzzle");
|
||||||
|
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("string.h");
|
||||||
|
@cInclude("wayland-client-core.h");
|
||||||
|
@cInclude("xdg-shell.h");
|
||||||
|
@cInclude("linux/input-event-codes.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
const WlPointerEventMask = packed struct(u16) {
|
||||||
|
ENTER: bool = false,
|
||||||
|
LEAVE: bool = false,
|
||||||
|
MOTION: bool = false,
|
||||||
|
BUTTON: bool = false,
|
||||||
|
AXIS: bool = false,
|
||||||
|
FRAME: bool = false,
|
||||||
|
AXIS_SOURCE: bool = false,
|
||||||
|
AXIS_STOP: bool = false,
|
||||||
|
AXIS_DISCRETE: bool = false,
|
||||||
|
AXIS_VALUE120: bool = false,
|
||||||
|
AXIS_RELATIVE_DIRECTION: bool = false,
|
||||||
|
_: u5,
|
||||||
|
};
|
||||||
|
|
||||||
|
const WlPointerEvent = struct {
|
||||||
|
mask: WlPointerEventMask,
|
||||||
|
time: u32,
|
||||||
|
serial: u32,
|
||||||
|
button: u32,
|
||||||
|
state: u32,
|
||||||
|
axis_x: f64,
|
||||||
|
axis_y: f64,
|
||||||
|
surface_x: c.wl_fixed_t = 0,
|
||||||
|
surface_y: c.wl_fixed_t = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const WaylandState = struct {
|
||||||
|
running: bool,
|
||||||
|
configured: bool,
|
||||||
|
|
||||||
|
compositor: ?*c.wl_compositor,
|
||||||
|
seat: ?*c.wl_seat,
|
||||||
|
shared_memory: ?*c.wl_shm,
|
||||||
|
xdg_wm_base: ?*c.xdg_wm_base,
|
||||||
|
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
|
resize: bool,
|
||||||
|
|
||||||
|
window_resized: bool,
|
||||||
|
window_width: i32,
|
||||||
|
window_height: i32,
|
||||||
|
|
||||||
|
pointer_event: WlPointerEvent,
|
||||||
|
|
||||||
|
app_input: puzzle.platform.AppInput,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MemoryPool = struct {
|
||||||
|
pool: ?*c.wl_shm_pool,
|
||||||
|
data: []u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn createShmFile(pool_size: usize) !std.posix.fd_t {
|
||||||
|
const name = "/wl_shm-oceanbox-puzzle-a-day";
|
||||||
|
const flags: std.os.linux.O = .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true };
|
||||||
|
const mode: std.os.linux.mode_t = 0o0600;
|
||||||
|
const result = std.c.shm_open(name, @bitCast(flags), mode);
|
||||||
|
if (result >= 0) {
|
||||||
|
std.debug.print("[Wayland] Created shm file {s}\n", .{name});
|
||||||
|
_ = std.c.shm_unlink(name);
|
||||||
|
} else {
|
||||||
|
std.debug.print("Error creating shm file {s}\n", .{name});
|
||||||
|
return error.ShmOpenError;
|
||||||
|
}
|
||||||
|
|
||||||
|
try std.posix.ftruncate(result, pool_size);
|
||||||
|
std.debug.print("[Wayland] ftruncate shm to size {d}B\n", .{pool_size});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn createSharedMemoryPool(wl_state: WaylandState) !MemoryPool {
|
||||||
|
const stride = wl_state.window_width * 4;
|
||||||
|
const pool_size: usize = @intCast(stride * wl_state.window_height);
|
||||||
|
|
||||||
|
const fd: c_int = try createShmFile(pool_size);
|
||||||
|
|
||||||
|
const prot = std.os.linux.PROT.READ | std.os.linux.PROT.WRITE;
|
||||||
|
const flags: std.os.linux.MAP = .{ .TYPE = .SHARED };
|
||||||
|
const ret = try std.posix.mmap(null, pool_size, prot, flags, fd, 0);
|
||||||
|
|
||||||
|
const pool = c.wl_shm_create_pool(wl_state.shared_memory, fd, @intCast(pool_size));
|
||||||
|
_ = std.os.linux.close(fd);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.pool = pool,
|
||||||
|
.data = ret[0..@intCast(pool_size)],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xdg_wm_base_handle_ping(data: ?*anyopaque, xdg_wm_base: ?*c.struct_xdg_wm_base, serial: u32) callconv(.c) void {
|
||||||
|
_ = data;
|
||||||
|
std.debug.print("[Wayland] Pong.\n", .{});
|
||||||
|
c.xdg_wm_base_pong(xdg_wm_base, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
const xdg_wm_base_listener: c.xdg_wm_base_listener = .{
|
||||||
|
.ping = xdg_wm_base_handle_ping,
|
||||||
|
};
|
||||||
|
|
||||||
|
// configure: ?*const fn (?*anyopaque, ?*struct_xdg_surface, u32) callconv(.c) void = @import("std").mem.zeroes(?*const fn (?*anyopaque, ?*struct_xdg_surface, u32) callconv(.c) void),
|
||||||
|
fn xdg_surface_handle_configure(data: ?*anyopaque, xdg_surface: ?*c.struct_xdg_surface, serial: u32) callconv(.c) void {
|
||||||
|
var wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
|
||||||
|
c.xdg_surface_ack_configure(xdg_surface, serial);
|
||||||
|
|
||||||
|
// TODO: Ready to resize?
|
||||||
|
if (wl_state.window_resized) {
|
||||||
|
wl_state.resize = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_state.configured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const xdg_surface_listener: c.xdg_surface_listener = .{
|
||||||
|
.configure = xdg_surface_handle_configure,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn xdg_toplevel_handle_configure(
|
||||||
|
data: ?*anyopaque,
|
||||||
|
xdg_toplevel: ?*c.struct_xdg_toplevel,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
names: [*c]c.struct_wl_array,
|
||||||
|
) callconv(.c) void {
|
||||||
|
var wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
_ = xdg_toplevel;
|
||||||
|
_ = names;
|
||||||
|
|
||||||
|
if (width != 0 and height != 0) {
|
||||||
|
std.debug.print("[Wayland] xdg toplevel configure: w = {d}, h = {d}.\n", .{ width, height });
|
||||||
|
wl_state.window_width = width;
|
||||||
|
wl_state.window_height = height;
|
||||||
|
wl_state.window_resized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xdg_toplevel_handle_close(data: ?*anyopaque, xdg_toplevel: ?*c.struct_xdg_toplevel) callconv(.c) void {
|
||||||
|
var wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
_ = xdg_toplevel;
|
||||||
|
|
||||||
|
std.debug.print("Close.\n", .{});
|
||||||
|
wl_state.running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xdg_toplevel_handle_configure_bounds(data: ?*anyopaque, toplevel: ?*c.struct_xdg_toplevel, width: i32, height: i32) callconv(.c) void {
|
||||||
|
_ = data;
|
||||||
|
_ = toplevel;
|
||||||
|
std.debug.print("[Wayland] xdg toplevel configure bounds: w = {d}, h = {d}.\n", .{ width, height });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xdg_toplevel_handle_wm_capabilities(data: ?*anyopaque, toplevel: ?*c.struct_xdg_toplevel, names: [*c]c.struct_wl_array) callconv(.c) void {
|
||||||
|
_ = data;
|
||||||
|
_ = toplevel;
|
||||||
|
_ = names;
|
||||||
|
std.debug.print("[Wayland] xdg toplevel wm capabilities.\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
const xdg_toplevel_listener: c.struct_xdg_toplevel_listener = .{
|
||||||
|
.configure = xdg_toplevel_handle_configure,
|
||||||
|
.close = xdg_toplevel_handle_close,
|
||||||
|
.configure_bounds = xdg_toplevel_handle_configure_bounds,
|
||||||
|
.wm_capabilities = xdg_toplevel_handle_wm_capabilities,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// enter event
|
||||||
|
///
|
||||||
|
/// Notification that this seat's pointer is focused on a certain
|
||||||
|
/// surface.
|
||||||
|
///
|
||||||
|
/// When a seat's focus enters a surface, the pointer image is
|
||||||
|
/// undefined and a client should respond to this event by setting
|
||||||
|
/// an appropriate pointer image with the set_cursor request.
|
||||||
|
/// @param serial serial number of the enter event
|
||||||
|
/// @param surface surface entered by the pointer
|
||||||
|
/// @param surface_x surface-local x coordinate
|
||||||
|
/// @param surface_y surface-local y coordinate
|
||||||
|
fn wlPointerHandleEnter(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface, surface_x: c.wl_fixed_t, surface_y: c.wl_fixed_t) callconv(.c) void {
|
||||||
|
const wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
|
||||||
|
_ = wl_pointer;
|
||||||
|
_ = surface;
|
||||||
|
|
||||||
|
wl_state.pointer_event.mask.ENTER = true;
|
||||||
|
wl_state.pointer_event.serial = serial;
|
||||||
|
wl_state.pointer_event.surface_x = surface_x;
|
||||||
|
wl_state.pointer_event.surface_y = surface_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// leave event
|
||||||
|
///
|
||||||
|
/// Notification that this seat's pointer is no longer focused on
|
||||||
|
/// a certain surface.
|
||||||
|
///
|
||||||
|
/// The leave notification is sent before the enter notification for
|
||||||
|
/// the new focus.
|
||||||
|
/// @param serial serial number of the leave event
|
||||||
|
/// @param surface surface left by the pointer
|
||||||
|
fn wlPointerHandleLeave(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface) callconv(.c) void {
|
||||||
|
const wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
|
||||||
|
_ = wl_pointer;
|
||||||
|
_ = surface;
|
||||||
|
|
||||||
|
wl_state.pointer_event.mask.LEAVE = true;
|
||||||
|
wl_state.pointer_event.serial = serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// pointer motion event
|
||||||
|
///
|
||||||
|
/// Notification of pointer location change. The arguments
|
||||||
|
/// surface_x and surface_y are the location relative to the focused
|
||||||
|
/// surface.
|
||||||
|
/// @param time timestamp with millisecond granularity
|
||||||
|
/// @param surface_x surface-local x coordinate
|
||||||
|
/// @param surface_y surface-local y coordinate
|
||||||
|
fn wlPointerHandleMotion(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer, time: u32, surface_x: c.wl_fixed_t, surface_y: c.wl_fixed_t) callconv(.c) void {
|
||||||
|
const wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
|
||||||
|
_ = wl_pointer;
|
||||||
|
|
||||||
|
wl_state.pointer_event.mask.MOTION = true;
|
||||||
|
wl_state.pointer_event.time = time;
|
||||||
|
wl_state.pointer_event.surface_x = surface_x;
|
||||||
|
wl_state.pointer_event.surface_y = surface_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// pointer button event
|
||||||
|
///
|
||||||
|
/// Mouse button click and release notifications.
|
||||||
|
///
|
||||||
|
/// The location of the click is given by the last motion or enter
|
||||||
|
/// event. The time argument is a timestamp with millisecond
|
||||||
|
/// granularity, with an undefined base.
|
||||||
|
///
|
||||||
|
/// The button is a button code as defined in the Linux kernel's
|
||||||
|
/// linux/input-event-codes.h header file, e.g. BTN_LEFT.
|
||||||
|
///
|
||||||
|
/// Any 16-bit button code value is reserved for future additions to
|
||||||
|
/// the kernel's event code list. All other button codes above
|
||||||
|
/// 0xFFFF are currently undefined but may be used in future
|
||||||
|
/// versions of this protocol.
|
||||||
|
/// @param serial serial number of the button event
|
||||||
|
/// @param time timestamp with millisecond granularity
|
||||||
|
/// @param button button that produced the event
|
||||||
|
/// @param state physical state of the button
|
||||||
|
fn wlPointerHandleButton(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer, serial: u32, time: u32, button: u32, state: u32) callconv(.c) void {
|
||||||
|
const wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
|
||||||
|
_ = wl_pointer;
|
||||||
|
|
||||||
|
wl_state.pointer_event.mask.BUTTON = true;
|
||||||
|
wl_state.pointer_event.time = time;
|
||||||
|
wl_state.pointer_event.serial = serial;
|
||||||
|
wl_state.pointer_event.button = button;
|
||||||
|
wl_state.pointer_event.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// axis event
|
||||||
|
///
|
||||||
|
/// Scroll and other axis notifications.
|
||||||
|
///
|
||||||
|
/// For scroll events (vertical and horizontal scroll axes), the
|
||||||
|
/// value parameter is the length of a vector along the specified
|
||||||
|
/// axis in a coordinate space identical to those of motion events,
|
||||||
|
/// representing a relative movement along the specified axis.
|
||||||
|
///
|
||||||
|
/// For devices that support movements non-parallel to axes multiple
|
||||||
|
/// axis events will be emitted.
|
||||||
|
///
|
||||||
|
/// When applicable, for example for touch pads, the server can
|
||||||
|
/// choose to emit scroll events where the motion vector is
|
||||||
|
/// equivalent to a motion event vector.
|
||||||
|
///
|
||||||
|
/// When applicable, a client can transform its content relative to
|
||||||
|
/// the scroll distance.
|
||||||
|
/// @param time timestamp with millisecond granularity
|
||||||
|
/// @param axis axis type
|
||||||
|
/// @param value length of vector in surface-local coordinate space
|
||||||
|
fn wlPointerHandleAxis(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer, time: u32, axis: u32, value: c.wl_fixed_t) callconv(.c) void {
|
||||||
|
const wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
|
||||||
|
_ = wl_pointer;
|
||||||
|
|
||||||
|
wl_state.pointer_event.mask.AXIS = true;
|
||||||
|
wl_state.pointer_event.time = time;
|
||||||
|
if (axis == 1) wl_state.pointer_event.axis_x = c.wl_fixed_to_double(value);
|
||||||
|
if (axis == 0) wl_state.pointer_event.axis_y = c.wl_fixed_to_double(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// axis source event
|
||||||
|
///
|
||||||
|
/// Source information for scroll and other axes.
|
||||||
|
///
|
||||||
|
/// This event does not occur on its own. It is sent before a
|
||||||
|
/// wl_pointer.frame event and carries the source information for
|
||||||
|
/// all events within that frame.
|
||||||
|
///
|
||||||
|
/// The source specifies how this event was generated. If the source
|
||||||
|
/// is wl_pointer.axis_source.finger, a wl_pointer.axis_stop event
|
||||||
|
/// will be sent when the user lifts the finger off the device.
|
||||||
|
///
|
||||||
|
/// If the source is wl_pointer.axis_source.wheel,
|
||||||
|
/// wl_pointer.axis_source.wheel_tilt or
|
||||||
|
/// wl_pointer.axis_source.continuous, a wl_pointer.axis_stop event
|
||||||
|
/// may or may not be sent. Whether a compositor sends an axis_stop
|
||||||
|
/// event for these sources is hardware-specific and
|
||||||
|
/// implementation-dependent; clients must not rely on receiving an
|
||||||
|
/// axis_stop event for these scroll sources and should treat scroll
|
||||||
|
/// sequences from these scroll sources as unterminated by default.
|
||||||
|
///
|
||||||
|
/// This event is optional. If the source is unknown for a
|
||||||
|
/// particular axis event sequence, no event is sent. Only one
|
||||||
|
/// wl_pointer.axis_source event is permitted per frame.
|
||||||
|
///
|
||||||
|
/// The order of wl_pointer.axis_discrete and wl_pointer.axis_source
|
||||||
|
/// is not guaranteed.
|
||||||
|
/// @param axis_source source of the axis event
|
||||||
|
/// @since 5
|
||||||
|
///
|
||||||
|
fn wlPointerHandleAxisSource(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer, axis_source: u32) callconv(.c) void {
|
||||||
|
_ = data;
|
||||||
|
_ = wl_pointer;
|
||||||
|
_ = axis_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// axis stop event
|
||||||
|
///
|
||||||
|
/// Stop notification for scroll and other axes.
|
||||||
|
///
|
||||||
|
/// For some wl_pointer.axis_source types, a wl_pointer.axis_stop
|
||||||
|
/// event is sent to notify a client that the axis sequence has
|
||||||
|
/// terminated. This enables the client to implement kinetic
|
||||||
|
/// scrolling. See the wl_pointer.axis_source documentation for
|
||||||
|
/// information on when this event may be generated.
|
||||||
|
///
|
||||||
|
/// Any wl_pointer.axis events with the same axis_source after this
|
||||||
|
/// event should be considered as the start of a new axis motion.
|
||||||
|
///
|
||||||
|
/// The timestamp is to be interpreted identical to the timestamp in
|
||||||
|
/// the wl_pointer.axis event. The timestamp value may be the same
|
||||||
|
/// as a preceding wl_pointer.axis event.
|
||||||
|
/// @param time timestamp with millisecond granularity
|
||||||
|
/// @param axis the axis stopped with this event
|
||||||
|
/// @since 5
|
||||||
|
fn wlPointerHandleAxisStop(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer, time: u32, axis: u32) callconv(.c) void {
|
||||||
|
_ = data;
|
||||||
|
_ = wl_pointer;
|
||||||
|
_ = time;
|
||||||
|
_ = axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// axis click event
|
||||||
|
///
|
||||||
|
/// Discrete step information for scroll and other axes.
|
||||||
|
///
|
||||||
|
/// This event carries the axis value of the wl_pointer.axis event
|
||||||
|
/// in discrete steps (e.g. mouse wheel clicks).
|
||||||
|
///
|
||||||
|
/// This event is deprecated with wl_pointer version 8 - this event
|
||||||
|
/// is not sent to clients supporting version 8 or later.
|
||||||
|
///
|
||||||
|
/// This event does not occur on its own, it is coupled with a
|
||||||
|
/// wl_pointer.axis event that represents this axis value on a
|
||||||
|
/// continuous scale. The protocol guarantees that each
|
||||||
|
/// axis_discrete event is always followed by exactly one axis event
|
||||||
|
/// with the same axis number within the same wl_pointer.frame. Note
|
||||||
|
/// that the protocol allows for other events to occur between the
|
||||||
|
/// axis_discrete and its coupled axis event, including other
|
||||||
|
/// axis_discrete or axis events. A wl_pointer.frame must not
|
||||||
|
/// contain more than one axis_discrete event per axis type.
|
||||||
|
///
|
||||||
|
/// This event is optional; continuous scrolling devices like
|
||||||
|
/// two-finger scrolling on touchpads do not have discrete steps and
|
||||||
|
/// do not generate this event.
|
||||||
|
///
|
||||||
|
/// The discrete value carries the directional information. e.g. a
|
||||||
|
/// value of -2 is two steps towards the negative direction of this
|
||||||
|
/// axis.
|
||||||
|
///
|
||||||
|
/// The axis number is identical to the axis number in the
|
||||||
|
/// associated axis event.
|
||||||
|
///
|
||||||
|
/// The order of wl_pointer.axis_discrete and wl_pointer.axis_source
|
||||||
|
/// is not guaranteed.
|
||||||
|
/// @param axis axis type
|
||||||
|
/// @param discrete number of steps
|
||||||
|
/// @since 5
|
||||||
|
/// @deprecated Deprecated since version 8
|
||||||
|
fn wlPointerHandleAxisDiscrete(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer, axis: u32, discrete: i32) callconv(.c) void {
|
||||||
|
_ = data;
|
||||||
|
_ = wl_pointer;
|
||||||
|
_ = axis;
|
||||||
|
_ = discrete;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// axis high-resolution scroll event
|
||||||
|
///
|
||||||
|
/// Discrete high-resolution scroll information.
|
||||||
|
///
|
||||||
|
/// This event carries high-resolution wheel scroll information,
|
||||||
|
/// with each multiple of 120 representing one logical scroll step
|
||||||
|
/// (a wheel detent). For example, an axis_value120 of 30 is one
|
||||||
|
/// quarter of a logical scroll step in the positive direction, a
|
||||||
|
/// value120 of -240 are two logical scroll steps in the negative
|
||||||
|
/// direction within the same hardware event. Clients that rely on
|
||||||
|
/// discrete scrolling should accumulate the value120 to multiples
|
||||||
|
/// of 120 before processing the event.
|
||||||
|
///
|
||||||
|
/// The value120 must not be zero.
|
||||||
|
///
|
||||||
|
/// This event replaces the wl_pointer.axis_discrete event in
|
||||||
|
/// clients supporting wl_pointer version 8 or later.
|
||||||
|
///
|
||||||
|
/// Where a wl_pointer.axis_source event occurs in the same
|
||||||
|
/// wl_pointer.frame, the axis source applies to this event.
|
||||||
|
///
|
||||||
|
/// The order of wl_pointer.axis_value120 and wl_pointer.axis_source
|
||||||
|
/// is not guaranteed.
|
||||||
|
/// @param axis axis type
|
||||||
|
/// @param value120 scroll distance as fraction of 120
|
||||||
|
/// @since 8
|
||||||
|
fn wlPointerHandleAxisValue120(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer, axis: u32, value120: i32) callconv(.c) void {
|
||||||
|
_ = data;
|
||||||
|
_ = wl_pointer;
|
||||||
|
_ = axis;
|
||||||
|
_ = value120;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// axis relative physical direction event
|
||||||
|
///
|
||||||
|
/// Relative directional information of the entity causing the
|
||||||
|
/// axis motion.
|
||||||
|
///
|
||||||
|
/// For a wl_pointer.axis event, the
|
||||||
|
/// wl_pointer.axis_relative_direction event specifies the movement
|
||||||
|
/// direction of the entity causing the wl_pointer.axis event. For
|
||||||
|
/// example: - if a user's fingers on a touchpad move down and this
|
||||||
|
/// causes a wl_pointer.axis vertical_scroll down event, the
|
||||||
|
/// physical direction is 'identical' - if a user's fingers on a
|
||||||
|
/// touchpad move down and this causes a wl_pointer.axis
|
||||||
|
/// vertical_scroll up scroll up event ('natural scrolling'), the
|
||||||
|
/// physical direction is 'inverted'.
|
||||||
|
///
|
||||||
|
/// A client may use this information to adjust scroll motion of
|
||||||
|
/// components. Specifically, enabling natural scrolling causes the
|
||||||
|
/// content to change direction compared to traditional scrolling.
|
||||||
|
/// Some widgets like volume control sliders should usually match
|
||||||
|
/// the physical direction regardless of whether natural scrolling
|
||||||
|
/// is active. This event enables clients to match the scroll
|
||||||
|
/// direction of a widget to the physical direction.
|
||||||
|
///
|
||||||
|
/// This event does not occur on its own, it is coupled with a
|
||||||
|
/// wl_pointer.axis event that represents this axis value. The
|
||||||
|
/// protocol guarantees that each axis_relative_direction event is
|
||||||
|
/// always followed by exactly one axis event with the same axis
|
||||||
|
/// number within the same wl_pointer.frame. Note that the protocol
|
||||||
|
/// allows for other events to occur between the
|
||||||
|
/// axis_relative_direction and its coupled axis event.
|
||||||
|
///
|
||||||
|
/// The axis number is identical to the axis number in the
|
||||||
|
/// associated axis event.
|
||||||
|
///
|
||||||
|
/// The order of wl_pointer.axis_relative_direction,
|
||||||
|
/// wl_pointer.axis_discrete and wl_pointer.axis_source is not
|
||||||
|
/// guaranteed.
|
||||||
|
/// @param axis axis type
|
||||||
|
/// @param direction physical direction relative to axis motion
|
||||||
|
/// @since 9
|
||||||
|
fn wlPointerHandleAxisRelativeDirection(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer, axis: u32, direction: u32) callconv(.c) void {
|
||||||
|
_ = data;
|
||||||
|
_ = wl_pointer;
|
||||||
|
_ = axis;
|
||||||
|
_ = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// end of a pointer event sequence
|
||||||
|
///
|
||||||
|
/// Indicates the end of a set of events that logically belong
|
||||||
|
/// together. A client is expected to accumulate the data in all
|
||||||
|
/// events within the frame before proceeding.
|
||||||
|
///
|
||||||
|
/// All wl_pointer events before a wl_pointer.frame event belong
|
||||||
|
/// logically together. For example, in a diagonal scroll motion the
|
||||||
|
/// compositor will send an optional wl_pointer.axis_source event,
|
||||||
|
/// two wl_pointer.axis events (horizontal and vertical) and finally
|
||||||
|
/// a wl_pointer.frame event. The client may use this information to
|
||||||
|
/// calculate a diagonal vector for scrolling.
|
||||||
|
///
|
||||||
|
/// When multiple wl_pointer.axis events occur within the same
|
||||||
|
/// frame, the motion vector is the combined motion of all events.
|
||||||
|
/// When a wl_pointer.axis and a wl_pointer.axis_stop event occur
|
||||||
|
/// within the same frame, this indicates that axis movement in one
|
||||||
|
/// axis has stopped but continues in the other axis. When multiple
|
||||||
|
/// wl_pointer.axis_stop events occur within the same frame, this
|
||||||
|
/// indicates that these axes stopped in the same instance.
|
||||||
|
///
|
||||||
|
/// A wl_pointer.frame event is sent for every logical event group,
|
||||||
|
/// even if the group only contains a single wl_pointer event.
|
||||||
|
/// Specifically, a client may get a sequence: motion, frame,
|
||||||
|
/// button, frame, axis, frame, axis_stop, frame.
|
||||||
|
///
|
||||||
|
/// The wl_pointer.enter and wl_pointer.leave events are logical
|
||||||
|
/// events generated by the compositor and not the hardware. These
|
||||||
|
/// events are also grouped by a wl_pointer.frame. When a pointer
|
||||||
|
/// moves from one surface to another, a compositor should group the
|
||||||
|
/// wl_pointer.leave event within the same wl_pointer.frame.
|
||||||
|
/// However, a client must not rely on wl_pointer.leave and
|
||||||
|
/// wl_pointer.enter being in the same wl_pointer.frame.
|
||||||
|
/// Compositor-specific policies may require the wl_pointer.leave
|
||||||
|
/// and wl_pointer.enter event being split across multiple
|
||||||
|
/// wl_pointer.frame groups.
|
||||||
|
/// @since 5
|
||||||
|
fn wlPointerHandleFrame(data: ?*anyopaque, wl_pointer: ?*c.struct_wl_pointer) callconv(.c) void {
|
||||||
|
_ = wl_pointer;
|
||||||
|
const wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
|
||||||
|
// std.debug.print("[Wayland] Pointer frame\n", .{});
|
||||||
|
|
||||||
|
if (wl_state.pointer_event.mask.ENTER) {
|
||||||
|
wl_state.app_input.mouse_present = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wl_state.pointer_event.mask.LEAVE) {
|
||||||
|
wl_state.app_input.mouse_present = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wl_state.pointer_event.mask.AXIS) {
|
||||||
|
std.debug.print(
|
||||||
|
"[Wayland] Pointer frame axis: x = {d}, y = {d}\n",
|
||||||
|
.{ wl_state.pointer_event.axis_x, wl_state.pointer_event.axis_y },
|
||||||
|
);
|
||||||
|
wl_state.app_input.scroll_y = wl_state.pointer_event.axis_y;
|
||||||
|
} else {
|
||||||
|
wl_state.app_input.scroll_y = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wl_state.pointer_event.mask.MOTION) {
|
||||||
|
// std.debug.print("[Wayland] Pointer frame motion\n", .{});
|
||||||
|
wl_state.app_input.mouse_x = c.wl_fixed_to_double(wl_state.pointer_event.surface_x);
|
||||||
|
wl_state.app_input.mouse_y = c.wl_fixed_to_double(wl_state.pointer_event.surface_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wl_state.pointer_event.mask.BUTTON) {
|
||||||
|
// std.debug.print("[Wayland] Pointer frame button\n", .{});
|
||||||
|
if (wl_state.pointer_event.button == c.BTN_LEFT) {
|
||||||
|
// std.debug.print("[Wayland] Pointer frame button left\n", .{});
|
||||||
|
if (wl_state.pointer_event.state == c.WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||||
|
// std.debug.print("[Wayland] Pointer frame button left down\n", .{});
|
||||||
|
wl_state.app_input.mouse_left_down = true;
|
||||||
|
} else if (wl_state.pointer_event.state == c.WL_POINTER_BUTTON_STATE_RELEASED) {
|
||||||
|
// std.debug.print("[Wayland] Pointer frame button left up\n", .{});
|
||||||
|
wl_state.app_input.mouse_left_down = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_state.pointer_event = std.mem.zeroInit(WlPointerEvent, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
const wl_pointer_listener: c.struct_wl_pointer_listener = .{
|
||||||
|
.enter = wlPointerHandleEnter,
|
||||||
|
.leave = wlPointerHandleLeave,
|
||||||
|
.motion = wlPointerHandleMotion,
|
||||||
|
.button = wlPointerHandleButton,
|
||||||
|
.axis = wlPointerHandleAxis,
|
||||||
|
.axis_source = wlPointerHandleAxisSource,
|
||||||
|
.axis_stop = wlPointerHandleAxisStop,
|
||||||
|
.axis_discrete = wlPointerHandleAxisDiscrete,
|
||||||
|
.axis_value120 = wlPointerHandleAxisValue120,
|
||||||
|
.axis_relative_direction = wlPointerHandleAxisRelativeDirection,
|
||||||
|
.frame = wlPointerHandleFrame,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn wlSeatHandleCapabilities(data: ?*anyopaque, wl_seat: ?*c.struct_wl_seat, capabilities: u32) callconv(.c) void {
|
||||||
|
const wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
|
||||||
|
if (capabilities & c.WL_SEAT_CAPABILITY_POINTER == 1) {
|
||||||
|
std.debug.print("[Wayland] wl_seat: got pointer capability\n", .{});
|
||||||
|
const pointer: ?*c.struct_wl_pointer = c.wl_seat_get_pointer(wl_seat);
|
||||||
|
_ = c.wl_pointer_add_listener(pointer, &wl_pointer_listener, wl_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wlSeatHandleName(data: ?*anyopaque, wl_seat: ?*c.struct_wl_seat, name: [*c]const u8) callconv(.c) void {
|
||||||
|
_ = data;
|
||||||
|
_ = wl_seat;
|
||||||
|
std.debug.print("[Wayland] wl_seat name {s}\n", .{name});
|
||||||
|
}
|
||||||
|
|
||||||
|
const wl_seat_listener: c.struct_wl_seat_listener = .{
|
||||||
|
.capabilities = wlSeatHandleCapabilities,
|
||||||
|
.name = wlSeatHandleName,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn registry_handle_global(
|
||||||
|
data: ?*anyopaque,
|
||||||
|
registry: ?*c.struct_wl_registry,
|
||||||
|
name: u32,
|
||||||
|
interface: [*c]const u8,
|
||||||
|
version: u32,
|
||||||
|
) callconv(.c) void {
|
||||||
|
var wl_state: *WaylandState = @ptrCast(@alignCast(data));
|
||||||
|
if (c.strcmp(interface, c.wl_compositor_interface.name) == 0) {
|
||||||
|
std.debug.print("[Wayland] Binding to interface {s} version {d}.\n", .{ interface, version });
|
||||||
|
wl_state.compositor = @ptrCast(@alignCast(c.wl_registry_bind(registry, name, &c.wl_compositor_interface, version)));
|
||||||
|
} else if (c.strcmp(interface, c.wl_shm_interface.name) == 0) {
|
||||||
|
std.debug.print("[Wayland] Binding to interface {s} version {d}.\n", .{ interface, version });
|
||||||
|
wl_state.shared_memory = @ptrCast(@alignCast(c.wl_registry_bind(registry, name, &c.wl_shm_interface, version)));
|
||||||
|
} else if (c.strcmp(interface, c.wl_seat_interface.name) == 0) {
|
||||||
|
std.debug.print("[Wayland] Binding to interface {s} version {d}.\n", .{ interface, version });
|
||||||
|
wl_state.seat = @ptrCast(@alignCast(c.wl_registry_bind(registry, name, &c.wl_seat_interface, version)));
|
||||||
|
_ = c.wl_seat_add_listener(wl_state.seat, &wl_seat_listener, wl_state);
|
||||||
|
} else if (c.strcmp(interface, c.xdg_wm_base_interface.name) == 0) {
|
||||||
|
std.debug.print("[Wayland] Binding to interface {s} version {d}.\n", .{ interface, version });
|
||||||
|
wl_state.xdg_wm_base = @ptrCast(@alignCast(c.wl_registry_bind(registry, name, &c.xdg_wm_base_interface, version)));
|
||||||
|
_ = c.xdg_wm_base_add_listener(wl_state.xdg_wm_base, &xdg_wm_base_listener, wl_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const registry_listener: c.struct_wl_registry_listener = .{
|
||||||
|
.global = registry_handle_global,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() u8 {
|
||||||
|
const display = c.wl_display_connect(null);
|
||||||
|
if (display == null) {
|
||||||
|
std.debug.print("Error connecting to wayland display.\n", .{});
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wl_fd = c.wl_display_get_fd(display);
|
||||||
|
|
||||||
|
std.debug.print("[Wayland] Connection established.\n", .{});
|
||||||
|
|
||||||
|
var wl_state: WaylandState = undefined;
|
||||||
|
|
||||||
|
const registry = c.wl_display_get_registry(display);
|
||||||
|
if (registry == null) {
|
||||||
|
std.debug.print("Error getting registry.\n", .{});
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = c.wl_registry_add_listener(registry, ®istry_listener, &wl_state);
|
||||||
|
|
||||||
|
_ = c.wl_display_roundtrip(display);
|
||||||
|
if (wl_state.compositor == null or wl_state.shared_memory == null or wl_state.seat == null) {
|
||||||
|
std.debug.print("ERROR: Wayland compositor, seat, or shm not bound.\n", .{});
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wl_surface = c.wl_compositor_create_surface(wl_state.compositor);
|
||||||
|
if (wl_surface == null) {
|
||||||
|
std.debug.print("ERROR: Could not create surface.\n", .{});
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const xdg_surface = c.xdg_wm_base_get_xdg_surface(wl_state.xdg_wm_base, wl_surface);
|
||||||
|
if (xdg_surface == null) {
|
||||||
|
std.debug.print("ERROR: Could not create xdg surface.\n", .{});
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = c.xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, &wl_state);
|
||||||
|
|
||||||
|
const xdg_toplevel = c.xdg_surface_get_toplevel(xdg_surface);
|
||||||
|
if (xdg_toplevel == null) {
|
||||||
|
std.debug.print("ERROR: Could not get xdg toplevel\n", .{});
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = c.xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, &wl_state);
|
||||||
|
|
||||||
|
c.xdg_toplevel_set_title(xdg_toplevel, "Oceanbox Tyler");
|
||||||
|
|
||||||
|
c.wl_surface_commit(wl_surface);
|
||||||
|
|
||||||
|
while (c.wl_display_dispatch(display) != -1 and !wl_state.configured) {}
|
||||||
|
|
||||||
|
_ = c.wl_display_roundtrip(display);
|
||||||
|
c.wl_surface_commit(wl_surface);
|
||||||
|
|
||||||
|
std.debug.print("[Wayland] Surface commited.\n", .{});
|
||||||
|
|
||||||
|
wl_state.running = true;
|
||||||
|
wl_state.resize = true;
|
||||||
|
wl_state.window_width = 800;
|
||||||
|
wl_state.window_height = 600;
|
||||||
|
|
||||||
|
var shared_memory_pool: ?*c.wl_shm_pool = undefined;
|
||||||
|
var shared_memory_pool_data: []u8 = undefined;
|
||||||
|
var wl_buffer: *c.struct_wl_buffer = undefined;
|
||||||
|
|
||||||
|
const monitor_update_hz: f64 = 30;
|
||||||
|
const game_update_hz: f64 = monitor_update_hz;
|
||||||
|
const frame_target_time_s: f64 = 1.0 / game_update_hz;
|
||||||
|
const frame_target_time_ms: f64 = std.time.ms_per_s * frame_target_time_s;
|
||||||
|
const frame_target_time_ns: u64 = std.time.ns_per_ms * @as(u64, @intFromFloat(frame_target_time_ms));
|
||||||
|
|
||||||
|
// Memory
|
||||||
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
|
|
||||||
|
const allocator = arena.allocator();
|
||||||
|
wl_state.allocator = allocator;
|
||||||
|
|
||||||
|
const app_permanent_storage = allocator.alloc(u8, puzzle.platform.megaBytes(8)) catch {
|
||||||
|
std.debug.print("[Wayland] ERROR: OOM!\n", .{});
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
var app_memory: puzzle.platform.AppMemory = .{
|
||||||
|
.initialized = false,
|
||||||
|
.permanent_storage = app_permanent_storage,
|
||||||
|
};
|
||||||
|
|
||||||
|
var timer = std.time.Timer.start() catch {
|
||||||
|
std.debug.print("[Wayland] ERROR: timer not supported.\n", .{});
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
var fds: [1]std.os.linux.pollfd = .{
|
||||||
|
.{
|
||||||
|
.fd = wl_fd,
|
||||||
|
.events = std.posix.POLL.IN,
|
||||||
|
.revents = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
while (wl_state.running) {
|
||||||
|
outer: while (true) {
|
||||||
|
const cont = std.posix.poll(&fds, 0) catch |err| {
|
||||||
|
switch (err) {
|
||||||
|
error.Unexpected => {
|
||||||
|
std.debug.print("Poll error: Unexpected.\n", .{});
|
||||||
|
},
|
||||||
|
error.SystemResources => {
|
||||||
|
std.debug.print("Poll error: SystemResources: Kernel has no space for table allocations.\n", .{});
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
std.debug.print("Poll error: network stuff idk.\n", .{});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (cont == 0) break;
|
||||||
|
|
||||||
|
for (fds) |fd| {
|
||||||
|
if (fd.revents & std.posix.POLL.IN > 0) {
|
||||||
|
const ret = c.wl_display_dispatch(display);
|
||||||
|
if (ret < 0) {
|
||||||
|
std.debug.print("Dispatch error {d}.\n", .{ret});
|
||||||
|
wl_state.running = false;
|
||||||
|
break :outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wl_state.resize) {
|
||||||
|
const new_pool = createSharedMemoryPool(wl_state) catch {
|
||||||
|
std.debug.print("[Wayland] Error creating memory pool!\n", .{});
|
||||||
|
wl_state.running = false;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
std.debug.print(
|
||||||
|
"[Wayland] Reizing window to {d}x{d} = {d}B.\n",
|
||||||
|
.{ wl_state.window_width, wl_state.window_height, new_pool.data.len },
|
||||||
|
);
|
||||||
|
|
||||||
|
shared_memory_pool = new_pool.pool;
|
||||||
|
shared_memory_pool_data = new_pool.data;
|
||||||
|
|
||||||
|
const stride = wl_state.window_width * 4;
|
||||||
|
const buffer = c.wl_shm_pool_create_buffer(
|
||||||
|
shared_memory_pool,
|
||||||
|
0,
|
||||||
|
wl_state.window_width,
|
||||||
|
wl_state.window_height,
|
||||||
|
stride,
|
||||||
|
c.WL_SHM_FORMAT_ARGB8888,
|
||||||
|
);
|
||||||
|
if (buffer != null) {
|
||||||
|
wl_buffer = buffer.?;
|
||||||
|
} else {
|
||||||
|
std.debug.print("[Wayland] Error creating buffer!\n", .{});
|
||||||
|
wl_state.running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_state.resize = false;
|
||||||
|
wl_state.window_resized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const render_start_time: u64 = timer.read();
|
||||||
|
|
||||||
|
const app_offscreen_buffer: puzzle.platform.OffscreenBuffer = .{
|
||||||
|
.width = wl_state.window_width,
|
||||||
|
.height = wl_state.window_height,
|
||||||
|
.stride = wl_state.window_width * 4,
|
||||||
|
.data = shared_memory_pool_data,
|
||||||
|
};
|
||||||
|
puzzle.updateAndRender(&app_memory, app_offscreen_buffer, wl_state.app_input);
|
||||||
|
|
||||||
|
const render_end_time: u64 = timer.read();
|
||||||
|
const render_time = render_end_time - render_start_time;
|
||||||
|
const render_time_ms: f64 = @as(f64, @floatFromInt(render_time)) / @as(f64, @floatFromInt(std.time.ns_per_ms));
|
||||||
|
|
||||||
|
var sleep_time: u64 = frame_target_time_ns;
|
||||||
|
if (render_time < frame_target_time_ns) {
|
||||||
|
sleep_time -= render_time;
|
||||||
|
std.posix.nanosleep(0, sleep_time);
|
||||||
|
} else {
|
||||||
|
sleep_time = 0;
|
||||||
|
std.debug.print("MISSED FRAME\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
c.wl_surface_attach(wl_surface, wl_buffer, 0, 0);
|
||||||
|
c.wl_surface_damage_buffer(wl_surface, 0, 0, wl_state.window_width, wl_state.window_height);
|
||||||
|
c.wl_surface_commit(wl_surface);
|
||||||
|
|
||||||
|
_ = c.wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
std.posix.nanosleep(0, std.time.ns_per_ms * 32);
|
||||||
|
|
||||||
|
const current = timer.lap();
|
||||||
|
const sleep_time_ms: f64 = @as(f64, @floatFromInt(sleep_time)) / @as(f64, @floatFromInt(std.time.ns_per_ms));
|
||||||
|
std.debug.print(
|
||||||
|
"Render: {d}ms, Sleeping: {d}ms, Frame: {d}ms.\n",
|
||||||
|
.{ render_time_ms, sleep_time_ms, current / std.time.ns_per_ms },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn kiloBytes(bytes: usize) usize {
|
||||||
|
return bytes * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn megaBytes(bytes: usize) usize {
|
||||||
|
return kiloBytes(bytes) * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const AppInput = struct {
|
||||||
|
mouse_present: bool = false,
|
||||||
|
mouse_left_down: bool = false,
|
||||||
|
mouse_x: f64,
|
||||||
|
mouse_y: f64,
|
||||||
|
|
||||||
|
scroll_y: f64,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Buffer the platform creates and hands off to the app
|
||||||
|
pub const OffscreenBuffer = struct {
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
stride: i32,
|
||||||
|
data: []u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const AppMemory = struct {
|
||||||
|
initialized: bool,
|
||||||
|
|
||||||
|
permanent_storage: []u8,
|
||||||
|
};
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
//! By convention, root.zig is the root source file when making a library.
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const platform = @import("platform.zig");
|
||||||
|
|
||||||
|
const TILE_SIZE = 32;
|
||||||
|
|
||||||
|
const V2 = struct {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
|
||||||
|
fn add(self: V2, other: V2) V2 {
|
||||||
|
return .{
|
||||||
|
.x = self.x + other.x,
|
||||||
|
.y = self.y + other.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub(self: V2, other: V2) V2 {
|
||||||
|
return .{
|
||||||
|
.x = self.x - other.x,
|
||||||
|
.y = self.y - other.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Camera = struct {
|
||||||
|
pos: V2,
|
||||||
|
offset: V2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Brick = struct {
|
||||||
|
pos: V2,
|
||||||
|
tiles: [4][4]u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const State = struct {
|
||||||
|
frame_count: i32,
|
||||||
|
mouse_pos: V2,
|
||||||
|
camera: Camera,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn fillRect(
|
||||||
|
buffer: platform.OffscreenBuffer,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
color: u32,
|
||||||
|
) void {
|
||||||
|
var pixels: []u32 = @ptrCast(@alignCast(buffer.data));
|
||||||
|
|
||||||
|
const start_x: usize = @intCast(x);
|
||||||
|
const end_x: usize = @intCast(x + width);
|
||||||
|
const start_y: usize = @intCast(y);
|
||||||
|
const end_y: usize = @intCast(y + height);
|
||||||
|
|
||||||
|
for (start_y..end_y) |i| {
|
||||||
|
for (start_x..end_x) |j| {
|
||||||
|
const idx: usize = i * @as(usize, @intCast(buffer.width)) + j;
|
||||||
|
|
||||||
|
pixels[idx] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fillSquare(
|
||||||
|
buffer: platform.OffscreenBuffer,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
size: i32,
|
||||||
|
color: u32,
|
||||||
|
) void {
|
||||||
|
fillRect(buffer, x, y, size, size, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBuffer, input: platform.AppInput) void {
|
||||||
|
var state: *State = @ptrCast(@alignCast(memory.permanent_storage));
|
||||||
|
|
||||||
|
if (!memory.initialized) {
|
||||||
|
state.camera.offset.x = @floatFromInt(@divTrunc(buffer.width, 2));
|
||||||
|
state.camera.offset.y = @floatFromInt(@divTrunc(buffer.height, 2));
|
||||||
|
|
||||||
|
state.camera.pos = state.camera.pos.add(state.camera.offset);
|
||||||
|
|
||||||
|
memory.initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update
|
||||||
|
const mouse_pos = V2{ .x = @floatCast(input.mouse_x), .y = @floatCast(input.mouse_y) };
|
||||||
|
|
||||||
|
if (input.mouse_left_down) {
|
||||||
|
const diff = mouse_pos.sub(state.mouse_pos);
|
||||||
|
state.camera.pos = state.camera.pos.add(diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.mouse_pos = mouse_pos;
|
||||||
|
|
||||||
|
const brick = Brick{
|
||||||
|
.pos = V2{ .x = 50, .y = 50},
|
||||||
|
.tiles = [4][4]u32{
|
||||||
|
.{1, 1, 1, 1},
|
||||||
|
.{1, 0, 0, 0},
|
||||||
|
.{0, 0, 0, 0},
|
||||||
|
.{0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Render
|
||||||
|
fillRect(buffer, 0, 0, buffer.width, buffer.height, 0xFFFFFFFF);
|
||||||
|
|
||||||
|
const board_pos = V2{ .x = 64, .y = 64 };
|
||||||
|
for (0..7) |i| {
|
||||||
|
for (0..7) |j| {
|
||||||
|
if (i == 0 and j == 6) continue;
|
||||||
|
if (i == 1 and j == 6) continue;
|
||||||
|
if (i == 6 and j == 3) continue;
|
||||||
|
if (i == 6 and j == 4) continue;
|
||||||
|
if (i == 6 and j == 5) continue;
|
||||||
|
if (i == 6 and j == 6) continue;
|
||||||
|
|
||||||
|
const tile_pos = V2{
|
||||||
|
.x = @floatFromInt(j * TILE_SIZE),
|
||||||
|
.y = @floatFromInt(i * TILE_SIZE),
|
||||||
|
};
|
||||||
|
const camera_pos = board_pos.add(tile_pos).add(state.camera.pos);
|
||||||
|
fillSquare(buffer, @intFromFloat(camera_pos.x), @intFromFloat(camera_pos.y), 30, 0xFF000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (brick.tiles, 0..) |row, i| {
|
||||||
|
for (row, 0..) |tile, j| {
|
||||||
|
if (tile == 1) {
|
||||||
|
const pos = V2{
|
||||||
|
.x = @floatFromInt(j * TILE_SIZE + 8),
|
||||||
|
.y = @floatFromInt(i * TILE_SIZE + 8),
|
||||||
|
};
|
||||||
|
const camera_pos = brick.pos.add(pos).add(state.camera.pos);
|
||||||
|
fillSquare(buffer, @intFromFloat(camera_pos.x), @intFromFloat(camera_pos.y), 30, 0xFFFF0000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.frame_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
+174
@@ -0,0 +1,174 @@
|
|||||||
|
/* Generated by wayland-scanner 1.24.0 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright © 2008-2013 Kristian Høgsberg
|
||||||
|
* Copyright © 2013 Rafael Antognolli
|
||||||
|
* Copyright © 2013 Jasper St. Pierre
|
||||||
|
* Copyright © 2010-2013 Intel Corporation
|
||||||
|
* Copyright © 2015-2017 Samsung Electronics Co., Ltd
|
||||||
|
* Copyright © 2015-2017 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "wayland-util.h"
|
||||||
|
|
||||||
|
extern const struct wl_interface wl_output_interface;
|
||||||
|
extern const struct wl_interface wl_seat_interface;
|
||||||
|
extern const struct wl_interface wl_surface_interface;
|
||||||
|
extern const struct wl_interface xdg_popup_interface;
|
||||||
|
extern const struct wl_interface xdg_positioner_interface;
|
||||||
|
extern const struct wl_interface xdg_surface_interface;
|
||||||
|
extern const struct wl_interface xdg_toplevel_interface;
|
||||||
|
|
||||||
|
static const struct wl_interface *xdg_shell_types[] = {
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&xdg_positioner_interface,
|
||||||
|
&xdg_surface_interface,
|
||||||
|
&wl_surface_interface,
|
||||||
|
&xdg_toplevel_interface,
|
||||||
|
&xdg_popup_interface,
|
||||||
|
&xdg_surface_interface,
|
||||||
|
&xdg_positioner_interface,
|
||||||
|
&xdg_toplevel_interface,
|
||||||
|
&wl_seat_interface,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&wl_seat_interface,
|
||||||
|
NULL,
|
||||||
|
&wl_seat_interface,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&wl_output_interface,
|
||||||
|
&wl_seat_interface,
|
||||||
|
NULL,
|
||||||
|
&xdg_positioner_interface,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wl_message xdg_wm_base_requests[] = {
|
||||||
|
{ "destroy", "", xdg_shell_types + 0 },
|
||||||
|
{ "create_positioner", "n", xdg_shell_types + 4 },
|
||||||
|
{ "get_xdg_surface", "no", xdg_shell_types + 5 },
|
||||||
|
{ "pong", "u", xdg_shell_types + 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wl_message xdg_wm_base_events[] = {
|
||||||
|
{ "ping", "u", xdg_shell_types + 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
WL_EXPORT const struct wl_interface xdg_wm_base_interface = {
|
||||||
|
"xdg_wm_base", 7,
|
||||||
|
4, xdg_wm_base_requests,
|
||||||
|
1, xdg_wm_base_events,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wl_message xdg_positioner_requests[] = {
|
||||||
|
{ "destroy", "", xdg_shell_types + 0 },
|
||||||
|
{ "set_size", "ii", xdg_shell_types + 0 },
|
||||||
|
{ "set_anchor_rect", "iiii", xdg_shell_types + 0 },
|
||||||
|
{ "set_anchor", "u", xdg_shell_types + 0 },
|
||||||
|
{ "set_gravity", "u", xdg_shell_types + 0 },
|
||||||
|
{ "set_constraint_adjustment", "u", xdg_shell_types + 0 },
|
||||||
|
{ "set_offset", "ii", xdg_shell_types + 0 },
|
||||||
|
{ "set_reactive", "3", xdg_shell_types + 0 },
|
||||||
|
{ "set_parent_size", "3ii", xdg_shell_types + 0 },
|
||||||
|
{ "set_parent_configure", "3u", xdg_shell_types + 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
WL_EXPORT const struct wl_interface xdg_positioner_interface = {
|
||||||
|
"xdg_positioner", 7,
|
||||||
|
10, xdg_positioner_requests,
|
||||||
|
0, NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wl_message xdg_surface_requests[] = {
|
||||||
|
{ "destroy", "", xdg_shell_types + 0 },
|
||||||
|
{ "get_toplevel", "n", xdg_shell_types + 7 },
|
||||||
|
{ "get_popup", "n?oo", xdg_shell_types + 8 },
|
||||||
|
{ "set_window_geometry", "iiii", xdg_shell_types + 0 },
|
||||||
|
{ "ack_configure", "u", xdg_shell_types + 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wl_message xdg_surface_events[] = {
|
||||||
|
{ "configure", "u", xdg_shell_types + 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
WL_EXPORT const struct wl_interface xdg_surface_interface = {
|
||||||
|
"xdg_surface", 7,
|
||||||
|
5, xdg_surface_requests,
|
||||||
|
1, xdg_surface_events,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wl_message xdg_toplevel_requests[] = {
|
||||||
|
{ "destroy", "", xdg_shell_types + 0 },
|
||||||
|
{ "set_parent", "?o", xdg_shell_types + 11 },
|
||||||
|
{ "set_title", "s", xdg_shell_types + 0 },
|
||||||
|
{ "set_app_id", "s", xdg_shell_types + 0 },
|
||||||
|
{ "show_window_menu", "ouii", xdg_shell_types + 12 },
|
||||||
|
{ "move", "ou", xdg_shell_types + 16 },
|
||||||
|
{ "resize", "ouu", xdg_shell_types + 18 },
|
||||||
|
{ "set_max_size", "ii", xdg_shell_types + 0 },
|
||||||
|
{ "set_min_size", "ii", xdg_shell_types + 0 },
|
||||||
|
{ "set_maximized", "", xdg_shell_types + 0 },
|
||||||
|
{ "unset_maximized", "", xdg_shell_types + 0 },
|
||||||
|
{ "set_fullscreen", "?o", xdg_shell_types + 21 },
|
||||||
|
{ "unset_fullscreen", "", xdg_shell_types + 0 },
|
||||||
|
{ "set_minimized", "", xdg_shell_types + 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wl_message xdg_toplevel_events[] = {
|
||||||
|
{ "configure", "iia", xdg_shell_types + 0 },
|
||||||
|
{ "close", "", xdg_shell_types + 0 },
|
||||||
|
{ "configure_bounds", "4ii", xdg_shell_types + 0 },
|
||||||
|
{ "wm_capabilities", "5a", xdg_shell_types + 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
WL_EXPORT const struct wl_interface xdg_toplevel_interface = {
|
||||||
|
"xdg_toplevel", 7,
|
||||||
|
14, xdg_toplevel_requests,
|
||||||
|
4, xdg_toplevel_events,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wl_message xdg_popup_requests[] = {
|
||||||
|
{ "destroy", "", xdg_shell_types + 0 },
|
||||||
|
{ "grab", "ou", xdg_shell_types + 22 },
|
||||||
|
{ "reposition", "3ou", xdg_shell_types + 24 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wl_message xdg_popup_events[] = {
|
||||||
|
{ "configure", "iiii", xdg_shell_types + 0 },
|
||||||
|
{ "popup_done", "", xdg_shell_types + 0 },
|
||||||
|
{ "repositioned", "3u", xdg_shell_types + 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
WL_EXPORT const struct wl_interface xdg_popup_interface = {
|
||||||
|
"xdg_popup", 7,
|
||||||
|
3, xdg_popup_requests,
|
||||||
|
3, xdg_popup_events,
|
||||||
|
};
|
||||||
|
|
||||||
+2381
File diff suppressed because it is too large
Load Diff
Executable
BIN
Binary file not shown.
Reference in New Issue
Block a user