diff --git a/.gitignore b/.gitignore index 19892e4..d8c8979 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .zig-cache +zig-out diff --git a/build.zig b/build.zig index eb98255..47d1bc3 100644 --- a/build.zig +++ b/build.zig @@ -4,9 +4,20 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - _ = b.addModule("drm_fourcc", .{ + const drm = b.addModule("drm", .{ .target = target, .optimize = optimize, - .root_source_file = b.path("drm_fourcc.zig"), + .root_source_file = b.path("src/drm.zig"), }); + + const example = b.addExecutable(.{ + .name = "example", + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("example.zig"), + }), + }); + example.root_module.addImport("drm", drm); + b.installArtifact(example); } diff --git a/example.zig b/example.zig new file mode 100644 index 0000000..d0358a1 --- /dev/null +++ b/example.zig @@ -0,0 +1,38 @@ +const std = @import("std"); +const drm = @import("drm"); + +pub fn main(init: std.process.Init) !void { + const io = init.io; + const gpa = init.gpa; + + const card = try drm.Card.openAuto(io); + defer card.close(io); + + std.log.info("Using drm.Card.getModeAlloc...", .{}); + const mode_res_1 = try card.getModeAlloc(gpa); + defer mode_res_1.deinit(gpa); + + std.log.info("Min dimensions: {d}x{d}.", .{ mode_res_1.min_width, mode_res_1.min_height }); + std.log.info("Max dimensions: {d}x{d}.", .{ mode_res_1.max_width, mode_res_1.max_height }); + + std.log.info("Got {d} fbs: {any}.", .{ mode_res_1.fbs.len, mode_res_1.fbs }); + std.log.info("Got {d} crtcs: {any}.", .{ mode_res_1.crtcs.len, mode_res_1.crtcs }); + std.log.info("Got {d} connectors: {any}.", .{ mode_res_1.connectors.len, mode_res_1.connectors }); + std.log.info("Got {d} encoders: {any}.", .{ mode_res_1.encoders.len, mode_res_1.encoders }); + + var fb_buf: [16]u32 = undefined; + var crtc_buf: [16]u32 = undefined; + var connector_buf: [16]u32 = undefined; + var encoder_buf: [16]u32 = undefined; + + std.log.info("Using drm.Card.getModeBuffered...", .{}); + const mode_res_2 = try card.getModeBuffered(&fb_buf, &crtc_buf, &connector_buf, &encoder_buf); + + std.log.info("Min dimensions: {d}x{d}.", .{ mode_res_2.min_width, mode_res_2.min_height }); + std.log.info("Max dimensions: {d}x{d}.", .{ mode_res_2.max_width, mode_res_2.max_height }); + + std.log.info("Got {d} fbs: {any}.", .{ mode_res_2.fbs.len, mode_res_2.fbs }); + std.log.info("Got {d} crtcs: {any}.", .{ mode_res_2.crtcs.len, mode_res_2.crtcs }); + std.log.info("Got {d} connectors: {any}.", .{ mode_res_2.connectors.len, mode_res_2.connectors }); + std.log.info("Got {d} encoders: {any}.", .{ mode_res_2.encoders.len, mode_res_2.encoders }); +} diff --git a/src/drm.zig b/src/drm.zig new file mode 100644 index 0000000..c2ad074 --- /dev/null +++ b/src/drm.zig @@ -0,0 +1,178 @@ +const std = @import("std"); +const format = @import("format.zig"); +const log = std.log.scoped(.drm); + +pub const sys = @import("sys.zig"); +pub const Format = format.Format; +pub const FormatModifiers = format.FormatModifiers; + +pub const Card = struct { + handle: std.Io.File, + + pub fn openAuto(io: std.Io) !Card { + const dir = try std.Io.Dir.openDirAbsolute(io, "/dev/dri", .{ .iterate = true }); + defer dir.close(io); + + var it = dir.iterate(); + const handle = try while (try it.next(io)) |entry| { + if (entry.kind == .character_device) { + log.debug("Found device /dev/dri/{s}.", .{entry.name}); + break dir.openFile(io, entry.name, .{}); + } + } else error.NoDevicesFound; + + return Card{ .handle = handle }; + } + + pub fn open(io: std.Io, path: []const u8) !Card { + const handle = try if (std.Io.Dir.path.isAbsolute(path)) handle: { + log.debug("Using device path {s}.", .{path}); + break :handle std.Io.Dir.openFileAbsolute(io, path, .{}); + } else handle: { + const dir = try std.Io.Dir.openDirAbsolute(io, "/dev/dri", .{}); + defer dir.close(io); + + log.debug("Using device path /dev/dri/{s}.", .{path}); + break :handle dir.openDir(io, path, .{}); + }; + + return Card{ .handle = handle }; + } + + pub fn close(self: Card, io: std.Io) void { + self.handle.close(io); + } + + pub const GetModeError = sys.IoctlError; + + pub fn getModeAlloc( + self: Card, + gpa: std.mem.Allocator, + ) (GetModeError || std.mem.Allocator.Error)!ModeCard { + return while (true) { + var res = std.mem.zeroes(sys.mode.CardRes); + try sys.ioctl(self.handle.handle, .mode_getresources, &res); + + var ret = ModeCard{ + .handle = self.handle, + .min_width = res.min_width, + .max_width = res.max_width, + .min_height = res.min_height, + .max_height = res.max_height, + .fbs = &.{}, + .crtcs = &.{}, + .connectors = &.{}, + .encoders = &.{}, + }; + + ret.fbs = try gpa.alloc(u32, res.count_fbs); + errdefer gpa.free(ret.fbs); + res.fb_id_ptr = @intFromPtr(ret.fbs.ptr); + + ret.crtcs = try gpa.alloc(u32, res.count_crtcs); + errdefer gpa.free(ret.crtcs); + res.crtc_id_ptr = @intFromPtr(ret.crtcs.ptr); + + ret.connectors = try gpa.alloc(u32, res.count_connectors); + errdefer gpa.free(ret.connectors); + res.connector_id_ptr = @intFromPtr(ret.connectors.ptr); + + ret.encoders = try gpa.alloc(u32, res.count_encoders); + errdefer gpa.free(ret.encoders); + res.encoder_id_ptr = @intFromPtr(ret.encoders.ptr); + + const cached = res; + try sys.ioctl(self.handle.handle, .mode_getresources, &res); + + if (cached.count_fbs < res.count_fbs or + cached.count_crtcs < res.count_crtcs or + cached.count_connectors < res.count_connectors or + cached.count_encoders < res.count_encoders) + { + gpa.free(ret.encoders); + gpa.free(ret.connectors); + gpa.free(ret.crtcs); + gpa.free(ret.fbs); + continue; + } + + break ret; + }; + } + + pub fn getModeBuffered( + self: Card, + fbs: []u32, + crtcs: []u32, + connectors: []u32, + encoders: []u32, + ) (GetModeError || error{ + TooManyFbs, + TooManyCrtcs, + TooManyConnectors, + TooManyEncoders, + })!ModeCard { + return while (true) { + var res = std.mem.zeroes(sys.mode.CardRes); + try sys.ioctl(self.handle.handle, .mode_getresources, &res); + + var ret = ModeCard{ + .handle = self.handle, + .min_width = res.min_width, + .max_width = res.max_width, + .min_height = res.min_height, + .max_height = res.max_height, + .fbs = &.{}, + .crtcs = &.{}, + .connectors = &.{}, + .encoders = &.{}, + }; + + if (res.count_fbs > fbs.len) return error.TooManyFbs; + ret.fbs = fbs[0..res.count_fbs]; + res.fb_id_ptr = @intFromPtr(ret.fbs.ptr); + + if (res.count_crtcs > crtcs.len) return error.TooManyCrtcs; + ret.crtcs = crtcs[0..res.count_crtcs]; + res.crtc_id_ptr = @intFromPtr(ret.crtcs.ptr); + + if (res.count_connectors > connectors.len) return error.TooManyConnectors; + ret.connectors = connectors[0..res.count_connectors]; + res.connector_id_ptr = @intFromPtr(ret.connectors.ptr); + + if (res.count_encoders > encoders.len) return error.TooManyEncoders; + ret.encoders = encoders[0..res.count_encoders]; + res.encoder_id_ptr = @intFromPtr(ret.encoders.ptr); + + const cached = res; + try sys.ioctl(self.handle.handle, .mode_getresources, &res); + + if (cached.count_fbs < res.count_fbs or + cached.count_crtcs < res.count_crtcs or + cached.count_connectors < res.count_connectors or + cached.count_encoders < res.count_encoders) + continue; + + break ret; + }; + } +}; + +pub const ModeCard = struct { + handle: std.Io.File, + min_width: u32, + max_width: u32, + min_height: u32, + max_height: u32, + fbs: []const u32, + crtcs: []const u32, + connectors: []const u32, + encoders: []const u32, + + pub fn deinit(self: ModeCard, gpa: std.mem.Allocator) void { + gpa.free(self.encoders); + gpa.free(self.connectors); + gpa.free(self.crtcs); + gpa.free(self.fbs); + } +}; diff --git a/drm_fourcc.zig b/src/format.zig similarity index 100% rename from drm_fourcc.zig rename to src/format.zig diff --git a/src/sys.zig b/src/sys.zig new file mode 100644 index 0000000..d0554d2 --- /dev/null +++ b/src/sys.zig @@ -0,0 +1,554 @@ +const std = @import("std"); +const IOCTL = std.os.linux.IOCTL; + +pub const IoctlError = std.posix.UnexpectedError; + +pub fn ioctl(fd: std.posix.fd_t, request: Request, arg: anytype) IoctlError!void { + const casted: usize = switch (@typeInfo(@TypeOf(arg))) { + .int => @intCast(arg), + .@"enum" => @intCast(@intFromEnum(arg)), + .pointer => @intFromPtr(arg), + else => @compileError("Unsupported type: " ++ @typeName(@TypeOf(arg))), + }; + + const rc = std.posix.system.ioctl(fd, @intCast(@intFromEnum(request)), casted); + + return switch (std.posix.errno(rc)) { + .SUCCESS => {}, + else => |err| std.posix.unexpectedErrno(err), + }; +} + +pub const Request = enum(u32) { + version = drm_iowr(0x00, Version), + get_unique = drm_iowr(0x01, Unique), + get_magic = drm_ior(0x02, Auth), + irq_busid = drm_iowr(0x03, IrqBusid), + get_map = drm_iowr(0x04, Map), + get_client = drm_iowr(0x05, Client), + get_stats = drm_ior(0x06, Stats), + set_version = drm_iowr(0x07, SetVersion), + // modeset_ctl = drm_iow(0x08, drm_modeset_ctl), + // gem_close = drm_iow(0x09, drm_gem_close), + // gem_flink = drm_iowr(0x0a, drm_gem_flink), + // gem_open = drm_iowr(0x0b, drm_gem_open), + // get_cap = drm_iowr(0x0c, drm_get_cap), + // set_client_cap = drm_iow(0x0d, drm_set_client_cap), + set_unique = drm_iow(0x10, Unique), + auth_magic = drm_iow(0x11, Auth), + // block = drm_iowr(0x12, drm_block), + // unblock = drm_iowr(0x13, drm_block), + // control = drm_iow(0x14, drm_control), + add_map = drm_iowr(0x15, Map), + // add_bufs = drm_iowr(0x16, drm_buf_desc), + // mark_bufs = drm_iow(0x17, drm_buf_desc), + // info_bufs = drm_iowr(0x18, drm_buf_info), + // map_bufs = drm_iowr(0x19, drm_buf_map), + // free_bufs = drm_iow(0x1a, drm_buf_free), + rm_map = drm_iow(0x1b, Map), + // set_sarea_ctx = drm_iow(0x1c, drm_ctx_priv_map), + // get_sarea_ctx = drm_iowr(0x1d, drm_ctx_priv_map), + set_master = drm_io(0x1e), + drop_master = drm_io(0x1f), + add_ctx = drm_iowr(0x20, Ctx), + rm_ctx = drm_iowr(0x21, Ctx), + mod_ctx = drm_iow(0x22, Ctx), + get_ctx = drm_iowr(0x23, Ctx), + switch_ctx = drm_iow(0x24, Ctx), + new_ctx = drm_iow(0x25, Ctx), + res_ctx = drm_iowr(0x26, CtxRes), + // add_draw = drm_iowr(0x27, drm_draw), + // rm_draw = drm_iowr(0x28, drm_draw), + // dma = drm_iowr(0x29, drm_dma), + // lock = drm_iow(0x2a, drm_lock), + // unlock = drm_iow(0x2b, drm_lock), + // finish = drm_iow(0x2c, drm_lock), + // prime_handle_to_fd = drm_iowr(0x2d, drm_prime_handle), + // prime_fd_to_handle = drm_iowr(0x2e, drm_prime_handle), + agp_acquire = drm_io(0x30), + agp_release = drm_io(0x31), + // agp_enable = drm_iow(0x32, drm_agp_mode), + // agp_info = drm_ior(0x33, drm_agp_info), + // agp_alloc = drm_iowr(0x34, drm_agp_buffer), + // agp_free = drm_iow(0x35, drm_agp_buffer), + // agp_bind = drm_iow(0x36, drm_agp_binding), + // agp_unbind = drm_iow(0x37, drm_agp_binding), + // sg_alloc = drm_iowr(0x38, drm_scatter_gather), + // sg_free = drm_iow(0x39, drm_scatter_gather), + // wait_vblank = drm_iowr(0x3a, drm_wait_vblank), + // crtc_get_sequence = drm_iowr(0x3b, drm_crtc_get_sequence), + // crtc_queue_sequence = drm_iowr(0x3c, drm_crtc_queue_sequence), + // update_draw = drm_iow(0x3f, drm_update_draw), + mode_getresources = drm_iowr(0xA0, mode.CardRes), + mode_getcrtc = drm_iowr(0xA1, mode.Crtc), + mode_setcrtc = drm_iowr(0xA2, mode.Crtc), + mode_cursor = drm_iowr(0xA3, mode.Cursor), + mode_getgamma = drm_iowr(0xA4, mode.Crtc.Lut), + mode_setgamma = drm_iowr(0xA5, mode.Crtc.Lut), + mode_getencoder = drm_iowr(0xA6, mode.GetEncoder), + mode_getconnector = drm_iowr(0xA7, mode.GetConnector), + mode_getproperty = drm_iowr(0xAA, mode.GetProperty), + mode_setproperty = drm_iowr(0xAB, mode.ConnectorSetProperty), + mode_getpropblob = drm_iowr(0xAC, mode.GetBlob), + mode_getfb = drm_iowr(0xAD, mode.FbCmd), + mode_addfb = drm_iowr(0xAE, mode.FbCmd), + mode_rmfb = drm_iowr(0xAF, c_uint), + mode_page_flip = drm_iowr(0xB0, mode.CrtcPageFlip), + mode_dirtyfb = drm_iowr(0xB1, mode.FbDirtyCmd), + mode_create_dumb = drm_iowr(0xB2, mode.CreateDumb), + mode_map_dumb = drm_iowr(0xB3, mode.MapDumb), + mode_destroy_dumb = drm_iowr(0xB4, mode.DestroyDumb), + mode_getplaneresources = drm_iowr(0xB5, mode.GetPlaneRes), + mode_getplane = drm_iowr(0xB6, mode.GetPlane), + mode_setplane = drm_iowr(0xB7, mode.SetPlane), + mode_addfb2 = drm_iowr(0xB8, mode.FbCmd2), + mode_obj_getproperties = drm_iowr(0xB9, mode.ObjGetProperties), + mode_obj_setproperty = drm_iowr(0xBA, mode.ObjSetProperty), + mode_cursor2 = drm_iowr(0xBB, mode.Cursor2), + mode_atomic = drm_iowr(0xBC, mode.Atomic), + mode_createpropblob = drm_iowr(0xBD, mode.CreateBlob), + mode_destroypropblob = drm_iowr(0xBE, mode.DestroyBlob), + // syncobj_create = drm_iowr(0xBF, drm_syncobj_create), + // syncobj_destroy = drm_iowr(0xC0, drm_syncobj_destroy), + // syncobj_handle_to_fd = drm_iowr(0xC1, drm_syncobj_handle), + // syncobj_fd_to_handle = drm_iowr(0xC2, drm_syncobj_handle), + // syncobj_wait = drm_iowr(0xC3, drm_syncobj_wait), + // syncobj_reset = drm_iowr(0xC4, drm_syncobj_array), + // syncobj_signal = drm_iowr(0xC5, drm_syncobj_array), + mode_create_lease = drm_iowr(0xC6, mode.CreateLease), + mode_list_lessees = drm_iowr(0xC7, mode.ListLessees), + mode_get_lease = drm_iowr(0xC8, mode.GetLease), + mode_revoke_lease = drm_iowr(0xC9, mode.RevokeLease), + // syncobj_timeline_wait = drm_iowr(0xCA, drm_syncobj_timeline_wait), + // syncobj_query = drm_iowr(0xCB, drm_syncobj_timeline_array), + // syncobj_transfer = drm_iowr(0xCC, drm_syncobj_transfer), + // syncobj_timeline_signal = drm_iowr(0xCD, drm_syncobj_timeline_array), + mode_getfb2 = drm_iowr(0xCE, mode.FbCmd2), + // syncobj_eventfd = drm_iowr(0xCF, drm_syncobj_eventfd), + mode_closefb = drm_iowr(0xD0, mode.CloseFb), + // set_client_name = drm_iowr(0xD1, drm_set_client_name), + // gem_change_handle = drm_iowr(0xD2, drm_gem_change_handle), + + const io_type = 'd'; + + fn drm_io(nr: u8) u32 { + return IOCTL.IO(io_type, nr); + } + + fn drm_ior(nr: u8, comptime T: type) u32 { + return IOCTL.IOR(io_type, nr, T); + } + + fn drm_iow(nr: u8, comptime T: type) u32 { + return IOCTL.IOW(io_type, nr, T); + } + + fn drm_iowr(nr: u8, comptime T: type) u32 { + return IOCTL.IOWR(io_type, nr, T); + } +}; + +pub const Magic = c_uint; + +pub const Context = c_ulong; + +pub const Version = extern struct { + version_major: c_int, + version_minor: c_int, + version_patch: c_int, + name_len: c_ulong, + name: [*:0]const u8, + date_len: c_ulong, + date: [*:0]const u8, + desc_len: c_ulong, + desc: [*:0]const u8, +}; + +pub const Unique = extern struct { + unique_len: c_ulong, + unique: [*:0]const u8, +}; + +pub const Auth = extern struct { + magic: Magic, +}; + +pub const IrqBusid = extern struct { + irq: c_int, + busnum: c_int, + devnum: c_int, + funcnum: c_int, +}; + +pub const Map = extern struct { + offset: c_ulong, + size: c_ulong, + type: Type, + flags: Flags, + handle: *anyopaque, + mtrr: c_int, + + pub const Type = enum(c_int) { + frame_buffer = 0, + registers = 1, + shm = 2, + agp = 3, + scatter_gather = 4, + consistent = 5, + }; + + pub const Flags = packed struct(c_int) { + restricted: bool = false, + read_only: bool = false, + locked: bool = false, + kernel: bool = false, + write_combining: bool = false, + contains_lock: bool = false, + removable: bool = false, + driver: bool = false, + _: @Int(.signed, @bitSizeOf(c_int) - 8) = 0, + }; +}; + +pub const Client = extern struct { + idx: c_int, + auth: c_int, + pid: c_ulong, + uid: c_ulong, + magic: Magic, + iocs: c_ulong, +}; + +pub const Stats = extern struct { + count: c_ulong, + data: [15]Stat, + + pub const Stat = extern struct { + value: c_ulong, + type: Type, + + pub const Type = enum(c_int) { + lock, + opens, + closes, + ioctls, + locks, + unlocks, + value, + byte, + count, + irq, + primary, + secondary, + dma, + special, + missed, + }; + }; +}; + +pub const SetVersion = extern struct { + di_major: c_int, + di_minor: c_int, + dd_major: c_int, + dd_minor: c_int, +}; + +pub const Ctx = extern struct { + handle: Context, + flags: Flags, + + pub const Flags = packed struct(c_int) { + preserved: bool = false, + @"2d_only": bool = false, + _: @Int(.signed, @bitSizeOf(c_int) - 2) = 0, + }; +}; + +pub const CtxRes = extern struct { + count: c_int, + contexts: [*]Ctx, +}; + +pub const mode = struct { + pub const display_mode_len = 32; + pub const prop_name_len = 32; + + pub const CardRes = extern struct { + fb_id_ptr: u64, + crtc_id_ptr: u64, + connector_id_ptr: u64, + encoder_id_ptr: u64, + count_fbs: u32, + count_crtcs: u32, + count_connectors: u32, + count_encoders: u32, + min_width: u32, + max_width: u32, + min_height: u32, + max_height: u32, + }; + + pub const Crtc = extern struct { + set_connectors_ptr: u64, + count_connectors: u32, + crtc_id: u32, + fb_id: u32, + x: u32, + y: u32, + gamma_size: u32, + mode_valid: u32, + mode: Modeinfo, + + pub const Lut = extern struct { + crtc_id: u32, + gamma_size: u32, + red: u64, + green: u64, + blue: u64, + }; + }; + + pub const Modeinfo = extern struct { + clock: u32, + hdisplay: u16, + hsync_start: u16, + hsync_end: u16, + htotal: u16, + hskew: u16, + vdisplay: u16, + vsync_start: u16, + vsync_end: u16, + vtotal: u16, + vscan: u16, + vrefresh: u32, + flags: u32, + type: u32, + + name: [display_mode_len]u8, + }; + + pub const Cursor = extern struct { + flags: u32, + crtc_id: u32, + x: i32, + y: i32, + width: u32, + height: u32, + handle: u32, + }; + + pub const GetEncoder = extern struct { + encoder_id: u32, + encoder_type: u32, + crtc_id: u32, + possible_crtcs: u32, + possible_clones: u32, + }; + + pub const GetConnector = extern struct { + encoders_ptr: u64, + modes_ptr: u64, + props_ptr: u64, + prop_values_ptr: u64, + count_modes: u32, + count_props: u32, + count_encoders: u32, + encoder_id: u32, + connector_id: u32, + connector_type: u32, + connector_type_id: u32, + connection: u32, + mm_width: u32, + mm_height: u32, + subpixel: u32, + pad: u32, + }; + + pub const GetProperty = extern struct { + values_ptr: u64, + enum_blob_ptr: u64, + prop_id: u32, + flags: u32, + name: [prop_name_len]u8, + count_values: u32, + count_enum_blobs: u32, + }; + + pub const ConnectorSetProperty = extern struct { + value: u64, + prop_id: u32, + connector_id: u32, + }; + + pub const GetBlob = extern struct { + blob_id: u32, + length: u32, + data: u64, + }; + + pub const FbCmd = extern struct { + fb_id: u32, + width: u32, + height: u32, + pitch: u32, + bpp: u32, + depth: u32, + handle: u32, + }; + + pub const CrtcPageFlip = extern struct { + crtc_id: u32, + fb_id: u32, + flags: u32, + reserved: u32, + user_data: u64, + }; + + pub const FbDirtyCmd = extern struct { + fb_id: u32, + flags: u32, + color: u32, + num_clips: u32, + clips_ptr: u64, + }; + + pub const CreateDumb = extern struct { + height: u32, + width: u32, + bpp: u32, + flags: u32, + handle: u32, + pitch: u32, + size: u64, + }; + + pub const MapDumb = extern struct { + handle: u32, + pad: u32, + offset: u64, + }; + + pub const DestroyDumb = extern struct { + handle: u32, + }; + + pub const GetPlaneRes = extern struct { + plane_id_ptr: u64, + count_planes: u32, + }; + + pub const GetPlane = extern struct { + plane_id: u32, + crtc_id: u32, + fb_id: u32, + possible_crtcs: u32, + gamma_size: u32, + count_format_types: u32, + format_type_ptr: u64, + }; + + pub const SetPlane = extern struct { + plane_id: u32, + crtc_id: u32, + fb_id: u32, + flags: u32, + crtc_x: i32, + crtc_y: i32, + crtc_w: u32, + crtc_h: u32, + src_x: u32, + src_y: u32, + src_h: u32, + src_w: u32, + }; + + pub const FbCmd2 = extern struct { + fb_id: u32, + width: u32, + height: u32, + pixel_format: u32, + flags: u32, + handles: [4]u32, + pitches: [4]u32, + offsets: [4]u32, + modifier: [4]u64, + }; + + pub const ObjGetProperties = extern struct { + props_ptr: u64, + prop_values_ptr: u64, + count_props: u32, + obj_id: u32, + obj_type: u32, + }; + + pub const ObjSetProperty = extern struct { + value: u64, + prop_id: u32, + obj_id: u32, + obj_type: u32, + }; + + pub const Cursor2 = extern struct { + flags: u32, + crtc_id: u32, + x: i32, + y: i32, + width: u32, + height: u32, + handle: u32, + hot_x: i32, + hot_y: i32, + }; + + pub const Atomic = extern struct { + flags: u32, + count_objs: u32, + objs_ptr: u64, + count_props_ptr: u64, + props_ptr: u64, + prop_values_ptr: u64, + reserved: u64, + user_data: u64, + }; + + pub const CreateBlob = extern struct { + data: u64, + length: u32, + blob_id: u32, + }; + + pub const DestroyBlob = extern struct { + blob_id: u32, + }; + + pub const CreateLease = extern struct { + object_ids: u64, + object_count: u32, + flags: u32, + lessee_id: u32, + fd: u32, + }; + + pub const ListLessees = extern struct { + count_lessees: u32, + pad: u32, + lessees_ptr: u64, + }; + + pub const GetLease = extern struct { + count_objects: u32, + pad: u32, + objects_ptr: u64, + }; + + pub const RevokeLease = extern struct { + lessee_id: u32, + }; + + pub const CloseFb = extern struct { + fb_id: u32, + pad: u32, + }; +};