summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2025-02-01 12:47:35 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2025-02-01 12:47:35 +0100
commit85bcada8cf78bdf2bfb3be583289686026e0f25e (patch)
tree0ce404c4840432db9b6d3addd3947a736d103382 /src
parent2ce14aec655589f00442ab469b9d877a143eeefd (diff)
screen: start drm implementation
Diffstat (limited to 'src')
-rw-r--r--src/estd/cursor.zig69
-rw-r--r--src/estd/parser/common.zig28
-rw-r--r--src/estd/parser/context.zig9
-rw-r--r--src/estd/parser/root.zig284
-rw-r--r--src/estd/root.zig8
-rw-r--r--src/init/main.zig21
-rw-r--r--src/screen/cerror.zig272
-rw-r--r--src/screen/drm/card.zig45
-rw-r--r--src/screen/drm/connector.zig156
-rw-r--r--src/screen/drm/request.zig19
-rw-r--r--src/screen/drm/resources.zig76
-rw-r--r--src/screen/main.zig20
-rw-r--r--src/shell/main.zig (renamed from src/main.zig)12
13 files changed, 1013 insertions, 6 deletions
diff --git a/src/estd/cursor.zig b/src/estd/cursor.zig
new file mode 100644
index 0000000..c0a4a32
--- /dev/null
+++ b/src/estd/cursor.zig
@@ -0,0 +1,69 @@
+const std = @import("std");
+
+pub const ReadError = error {
+ EndOfBuffer
+};
+
+pub fn Cursor(comptime T: type) type {
+ return struct {
+ const Self = @This();
+
+ buffer: []const T,
+ index: usize,
+ touched_index: usize,
+
+ pub fn init(buffer: []const T) Self {
+ return .{
+ .buffer = buffer,
+ .index = 0,
+ .touched_index = 0,
+ };
+ }
+
+ pub fn peek(self: Self, amount: usize) ReadError![]const T {
+ if (self.index + amount > self.buffer.len) {
+ return ReadError.EndOfBuffer;
+ }
+
+ return self.buffer[self.index..self.index + amount];
+ }
+
+ pub fn consume(self: *Self, amount: usize) ReadError![]const T {
+ const slice = try self.peek(amount);
+ self.index += amount;
+ self.touched_index = self.index;
+ return slice;
+ }
+
+ pub fn snapshot(self: Self) usize {
+ return self.index;
+ }
+
+ pub fn rollback(self: *Self, index: usize) void {
+ self.index = index;
+ }
+
+ pub fn touched_slice(self: Self) []const T {
+ return self.buffer[0..self.touched_index];
+ }
+ };
+}
+
+test "peek" {
+ const buffer = "1234";
+ const cursor = Cursor(u8).init(buffer);
+ try std.testing.expectEqualSlices(u8, "1", try cursor.peek(1));
+ try std.testing.expectEqualSlices(u8, "12", try cursor.peek(2));
+ try std.testing.expectEqualSlices(u8, "123", try cursor.peek(3));
+ try std.testing.expectEqualSlices(u8, "1234", try cursor.peek(4));
+ try std.testing.expectError(ReadError.EndOfBuffer, cursor.peek(5));
+}
+
+test "consume" {
+ const buffer = "12345678";
+ var cursor = Cursor(u8).init(buffer);
+ try std.testing.expectEqualSlices(u8, "1", try cursor.consume(1));
+ try std.testing.expectEqualSlices(u8, "23", try cursor.consume(2));
+ try std.testing.expectEqualSlices(u8, "456", try cursor.consume(3));
+ try std.testing.expectError(ReadError.EndOfBuffer, cursor.consume(4));
+}
diff --git a/src/estd/parser/common.zig b/src/estd/parser/common.zig
new file mode 100644
index 0000000..986af58
--- /dev/null
+++ b/src/estd/parser/common.zig
@@ -0,0 +1,28 @@
+const std = @import("std");
+const root = @import("root.zig");
+const Context = @import("context.zig").Context;
+const ParseFn = root.ParseFn;
+
+pub fn LiteralFn(comptime I: type, comptime O: type) type {
+ return *const fn (comptime literal: []const I, value: O) ParseFn(I, O);
+}
+
+pub fn Literal(comptime I: type, comptime O: type) LiteralFn(I, O) {
+ return struct {
+ pub fn literal(comptime lit: []const I, value: O) ParseFn(I, O) {
+ return struct {
+ pub fn parse(ctx: *Context(I)) !O {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ const slice = try ctx.cursor.consume(lit.len);
+ if (std.mem.eql(I, lit, slice)) {
+ return value;
+ }
+
+ return error.UnexpectedLiteral;
+ }
+ }.parse;
+ }
+ }.literal;
+}
diff --git a/src/estd/parser/context.zig b/src/estd/parser/context.zig
new file mode 100644
index 0000000..56fb025
--- /dev/null
+++ b/src/estd/parser/context.zig
@@ -0,0 +1,9 @@
+const std = @import("std");
+const Cursor = @import("../cursor.zig").Cursor;
+
+pub fn Context(comptime I: type) type {
+ return struct {
+ cursor: Cursor(I),
+ allocator: std.mem.Allocator,
+ };
+}
diff --git a/src/estd/parser/root.zig b/src/estd/parser/root.zig
new file mode 100644
index 0000000..b168a88
--- /dev/null
+++ b/src/estd/parser/root.zig
@@ -0,0 +1,284 @@
+const std = @import("std");
+const Cursor = @import("../cursor.zig").Cursor;
+pub const common = @import("common.zig");
+pub const Context = @import("context.zig").Context;
+
+
+pub fn ParseFn(comptime I: type, comptime O: type) type {
+ return *const fn (ctx: *Context(I)) anyerror!O;
+}
+
+pub fn Parser(comptime I: type, comptime O: type, comptime parse_fn: ParseFn(I, O)) type {
+ return struct {
+ const Self = @This();
+
+ pub fn parse(ctx: *Context(I)) !O {
+ return try parse_fn(ctx);
+ }
+
+ pub fn or_else(comptime other: ParseFn(I, O)) type {
+ return Parser(I, O, struct {
+ pub fn parse(ctx: *Context(I)) !O {
+ return parse_fn(ctx) catch other(ctx);
+ }
+ }.parse);
+ }
+
+ pub fn and_skip(comptime skip: anytype) type {
+ return Parser(I, O, struct {
+ pub fn parse(ctx: *Context(I)) !O {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ _ = try skip(ctx);
+ return try parse_fn(ctx);
+ }
+ }.parse);
+ }
+
+ pub fn then_skip(comptime skip: anytype) type {
+ return Parser(I, O, struct {
+ pub fn parse(ctx: *Context(I)) !O {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+ const value = try parse_fn(ctx);
+ _ = try skip(ctx);
+ return value;
+ }
+ }.parse);
+ }
+
+ pub fn then(comptime O2: type, comptime other: ParseFn(I, O2)) type {
+ const CombinedOutput = struct { O, O2 };
+
+ return Parser(I, CombinedOutput, struct {
+ pub fn parse(ctx: *Context(I)) !CombinedOutput {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ return .{ try parse_fn(ctx), try other(ctx) };
+ }
+ }.parse);
+ }
+
+ pub fn zero_or_more() type {
+ return Parser(I, std.ArrayList(O), struct {
+ pub fn parse(ctx: *Context(I)) !std.ArrayList(O) {
+ var values = std.ArrayList(O).init(ctx.allocator);
+ errdefer values.deinit();
+
+ while (parse_fn(ctx)) |value| {
+ try values.append(value);
+ } else |_| {}
+
+ return values;
+ }
+ }.parse);
+ }
+
+ pub fn repeat(n: usize) type {
+ return Parser(I, [n]O, struct {
+ pub fn parse(ctx: *Context(I)) ![n]O {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ var values: [n]O = undefined;
+
+ for (&values) |*value| {
+ value.* = try parse_fn(ctx);
+ }
+
+ return values;
+ }
+ }.parse);
+ }
+
+ pub fn map(M: type, map_fn: *const fn (value: O) M) type {
+ return Parser(I, M, struct {
+ pub fn parse(ctx: *Context(I)) !M {
+ return map_fn(try parse_fn(ctx));
+ }
+ }.parse);
+ }
+
+ pub fn end() type {
+ return Parser(I, O, struct {
+ pub fn parse(ctx: *Context(I)) !O {
+ const value = try parse_fn(ctx);
+ _ = ctx.cursor.peek(1) catch return value;
+ return error.NotEnd;
+ }
+ }.parse);
+ }
+ };
+}
+
+fn parse_zero(ctx: *Context(u8)) !u8 {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ const value = try ctx.cursor.consume(1);
+
+ if (value[0] == '0') {
+ return 0;
+ }
+
+ return error.NotZero;
+}
+
+fn parse_one(ctx: *Context(u8)) !u8 {
+ const snapshot = ctx.cursor.snapshot();
+ errdefer ctx.cursor.rollback(snapshot);
+
+ const value = try ctx.cursor.consume(1);
+
+ if (value[0] == '1') {
+ return 1;
+ }
+
+ return error.NotOne;
+}
+
+
+test "direct" {
+ const ZeroParser = Parser(u8, u8, parse_zero);
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("0"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectEqual(0, try ZeroParser.parse(&ctx));
+
+ ctx.cursor = Cursor(u8).init("1");
+ try std.testing.expectError(error.NotZero, ZeroParser.parse(&ctx));
+}
+
+test "or_else" {
+ const ZeroParser = Parser(u8, u8, parse_zero);
+ const ZeroAndOneParser = ZeroParser.or_else(parse_one);
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("0"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectEqual(0, try ZeroAndOneParser.parse(&ctx));
+
+ ctx.cursor = Cursor(u8).init("1");
+ try std.testing.expectEqual(1, try ZeroAndOneParser.parse(&ctx));
+
+ ctx.cursor = Cursor(u8).init("2");
+ try std.testing.expectError(error.NotZero, ZeroParser.parse(&ctx));
+}
+
+test "then" {
+ const ZeroParser = Parser(u8, u8, parse_zero);
+ const ZeroThenOneParser = ZeroParser.then(u8, parse_one);
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("01"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectEqual(.{ 0, 1 }, try ZeroThenOneParser.parse(&ctx));
+
+ ctx.cursor = Cursor(u8).init("11");
+ try std.testing.expectError(error.NotZero, ZeroThenOneParser.parse(&ctx));
+
+ ctx.cursor = Cursor(u8).init("00");
+ try std.testing.expectError(error.NotOne, ZeroThenOneParser.parse(&ctx));
+}
+
+test "zero_or_more" {
+ const ZeroParser = Parser(u8, u8, parse_zero);
+ const ZeroWildcardParser = ZeroParser.zero_or_more();
+ const ZeroOrOneWildcardParser = ZeroParser.or_else(parse_one).zero_or_more();
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("0000"),
+ .allocator = std.testing.allocator,
+ };
+ var result = try ZeroWildcardParser.parse(&ctx);
+ try std.testing.expectEqualSlices(u8, &[_]u8{0, 0, 0, 0}, result.items);
+ result.deinit();
+
+ ctx.cursor = Cursor(u8).init("00001");
+ result = try ZeroWildcardParser.parse(&ctx);
+ try std.testing.expectEqualSlices(u8, &[_]u8{0, 0, 0, 0}, result.items);
+ result.deinit();
+
+ ctx.cursor = Cursor(u8).init("0101110");
+ result = try ZeroOrOneWildcardParser.parse(&ctx);
+ try std.testing.expectEqualSlices(u8, &[_]u8{0, 1, 0, 1, 1, 1, 0}, result.items);
+ result.deinit();
+
+ ctx.cursor = Cursor(u8).init("0101110abc");
+ result = try ZeroOrOneWildcardParser.parse(&ctx);
+ try std.testing.expectEqualSlices(u8, &[_]u8{0, 1, 0, 1, 1, 1, 0}, result.items);
+ result.deinit();
+}
+
+test "repeat" {
+ const ZeroParser = Parser(u8, u8, parse_zero);
+ const FourZerosParser = ZeroParser.repeat(4);
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("00001"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectEqualSlices(
+ u8,
+ &[_]u8{0, 0, 0, 0},
+ &(try FourZerosParser.parse(&ctx))
+ );
+
+ ctx.cursor = Cursor(u8).init("0001");
+ try std.testing.expectError(
+ error.NotZero,
+ FourZerosParser.parse(&ctx)
+ );
+}
+
+test "map" {
+ const ZeroOrOneParser = Parser(u8, u8, parse_zero).or_else(parse_one);
+ const ZeroOrOneMappedParser = ZeroOrOneParser.map(u8, struct {
+ pub fn map(i: u8) u8 {
+ return i * 2;
+ }
+ }.map).zero_or_more();
+
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("00110101"),
+ .allocator = std.testing.allocator,
+ };
+ const result = try ZeroOrOneMappedParser.parse(&ctx);
+ try std.testing.expectEqualSlices(u8, &[_]u8{ 0, 0, 2, 2, 0, 2, 0, 2 }, result.items);
+ defer result.deinit();
+}
+
+test "cursor rollback" {
+ const DoubleZeroOrOneParser = Parser(u8, u8, parse_zero).repeat(3).or_else(
+ Parser(u8, u8, parse_zero).repeat(2).then(u8, parse_one).map([3]u8, struct {
+ pub fn map(r: struct { [2]u8, u8 }) [3]u8 {
+ const array, const last = r;
+ return array ++ .{ last };
+ }
+ }.map).parse
+ );
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("0001"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectEqualSlices(u8, &[_]u8{ 0, 0, 0 }, &(try DoubleZeroOrOneParser.parse(&ctx)));
+
+ ctx.cursor = Cursor(u8).init("0010");
+ try std.testing.expectEqualSlices(u8, &[_]u8{ 0, 0, 1 }, &(try DoubleZeroOrOneParser.parse(&ctx)));
+}
+
+test "cursor error touched slice" {
+ const OneThenDoubleZeroParser = Parser(u8, u8, parse_one).then([2]u8, Parser(u8, u8, parse_zero).repeat(2).parse);
+ var ctx = Context(u8) {
+ .cursor = Cursor(u8).init("10110"),
+ .allocator = std.testing.allocator,
+ };
+ try std.testing.expectError(error.NotZero, OneThenDoubleZeroParser.parse(&ctx));
+ try std.testing.expectEqualSlices(u8, "101", ctx.cursor.touched_slice());
+}
diff --git a/src/estd/root.zig b/src/estd/root.zig
new file mode 100644
index 0000000..86af090
--- /dev/null
+++ b/src/estd/root.zig
@@ -0,0 +1,8 @@
+const std = @import("std");
+
+pub const cursor = @import("cursor.zig");
+pub const parser = @import("parser/root.zig");
+
+test {
+ std.testing.refAllDecls(@This());
+}
diff --git a/src/init/main.zig b/src/init/main.zig
new file mode 100644
index 0000000..0af43bc
--- /dev/null
+++ b/src/init/main.zig
@@ -0,0 +1,21 @@
+const std = @import("std");
+const os = std.os.linux;
+
+pub fn main() !void {
+ std.debug.print("\u{1b}[1;1H\u{1b}[J", .{});
+ _ = std.os.linux.mount("none", "/dev/", "devtmpfs", 0, 0);
+
+
+ const pid = @as(i32, @intCast(os.fork()));
+
+ if (pid == 0) {
+ _ = os.execve("process", &[_:null]?[*:0]const u8 { "process" }, &[0:null]?[*:0]const u8{});
+
+ return error.ExecFailed;
+ }
+
+ var status: u32 = undefined;
+ _ = os.waitpid(pid, &status, 0);
+
+ while (true) { std.time.sleep(100); }
+}
diff --git a/src/screen/cerror.zig b/src/screen/cerror.zig
new file mode 100644
index 0000000..cd7f6e6
--- /dev/null
+++ b/src/screen/cerror.zig
@@ -0,0 +1,272 @@
+
+pub const CError = error {
+ OperationNotPermitted,
+ NoSuchFileOrDirectory,
+ NoSuchProcess,
+ InterruptedSystemCall,
+ InputOutputError,
+ NoSuchDeviceOrAddress,
+ ArgumentListTooLong,
+ ExecFormatError,
+ BadFileDescriptor,
+ NoChildProcesses,
+ ResourceTemporarilyUnavailable,
+ CannotAllocateMemory,
+ PermissionDenied,
+ BadAddress,
+ BlockDeviceRequired,
+ DeviceOrResourceBusy,
+ FileExists,
+ InvalidCrossDeviceLink,
+ NoSuchDevice,
+ NotADirectory,
+ IsADirectory,
+ InvalidArgument,
+ TooManyOpenFilesInSystem,
+ TooManyOpenFiles,
+ InappropriateIoctlForDevice,
+ TextFileBusy,
+ FileTooLarge,
+ NoSpaceLeftOnDevice,
+ IllegalSeek,
+ ReadOnlyFileSystem,
+ TooManyLinks,
+ BrokenPipe,
+ NumericalArgumentOutOfDomain,
+ NumericalResultOutOfRange,
+ ResourceDeadlockAvoided,
+ FileNameTooLong,
+ NoLocksAvailable,
+ FunctionNotImplemented,
+ DirectoryNotEmpty,
+ TooManyLevelsOfSymbolicLinks,
+ NoMessageOfDesiredType,
+ IdentifierRemoved,
+ ChannelNumberOutOfRange,
+ Level2NotSynchronized,
+ Level3Halted,
+ Level3Reset,
+ LinkNumberOutOfRange,
+ ProtocolDriverNotAttached,
+ NoCSIStructureAvailable,
+ Level2Halted,
+ InvalidExchange,
+ InvalidRequestDescriptor,
+ ExchangeFull,
+ NoAnode,
+ InvalidRequestCode,
+ InvalidSlot,
+ BadFontFileFormat,
+ DeviceNotAStream,
+ NoDataAvailable,
+ TimerExpired,
+ OutOfStreamsResources,
+ MachineIsNotOnTheNetwork,
+ PackageNotInstalled,
+ ObjectIsRemote,
+ LinkHasBeenSevered,
+ AdvertiseError,
+ SrmountError,
+ CommunicationErrorOnSend,
+ ProtocolError,
+ MultihopAttempted,
+ RFSSpecificError,
+ BadMessage,
+ ValueTooLargeForDefinedDataType,
+ NameNotUniqueOnNetwork,
+ FileDescriptorInBadState,
+ RemoteAddressChanged,
+ CanNotAccessANeededSharedLibrary,
+ AccessingACorruptedSharedLibrary,
+ LibSectionInAOutCorrupted,
+ AttemptingToLinkInTooManySharedLibraries,
+ CannotExecASharedLibraryDirectly,
+ InvalidOrIncompleteMultibyteOrWideCharacter,
+ InterruptedSystemCallShouldBeRestarted,
+ StreamsPipeError,
+ TooManyUsers,
+ SocketOperationOnNonSocket,
+ DestinationAddressRequired,
+ MessageTooLong,
+ ProtocolWrongTypeForSocket,
+ ProtocolNotAvailable,
+ ProtocolNotSupported,
+ SocketTypeNotSupported,
+ OperationNotSupported,
+ ProtocolFamilyNotSupported,
+ AddressFamilyNotSupportedByProtocol,
+ AddressAlreadyInUse,
+ CannotAssignRequestedAddress,
+ NetworkIsDown,
+ NetworkIsUnreachable,
+ NetworkDroppedConnectionOnReset,
+ SoftwareCausedConnectionAbort,
+ ConnectionResetByPeer,
+ NoBufferSpaceAvailable,
+ TransportEndpointIsAlreadyConnected,
+ TransportEndpointIsNotConnected,
+ CannotSendAfterTransportEndpointShutdown,
+ TooManyReferences,
+ ConnectionTimedOut,
+ ConnectionRefused,
+ HostIsDown,
+ NoRouteToHost,
+ OperationAlreadyInProgress,
+ OperationNowInProgress,
+ StaleFileHandle,
+ StructureNeedsCleaning,
+ NotAXENIXNamedTypeFile,
+ NoXENIXSemaphoresAvailable,
+ IsANamedTypeFile,
+ RemoteIOError,
+ DiskQuotaExceeded,
+ NoMediumFound,
+ WrongMediumType,
+ OperationCanceled,
+ RequiredKeyNotAvailable,
+ KeyHasExpired,
+ KeyHasBeenRevoked,
+ KeyWasRejectedByService,
+ OwnerDied,
+ StateNotRecoverable,
+ OperationNotPossibleDueToRFKill,
+ MemoryPageHasHardwareError,
+};
+
+pub fn from_usize(errno: usize) CError!void {
+ const n = -@as(isize, @bitCast(errno));
+ return switch (n) {
+ 1 => CError.OperationNotPermitted,
+ 2 => CError.NoSuchFileOrDirectory,
+ 3 => CError.NoSuchProcess,
+ 4 => CError.InterruptedSystemCall,
+ 5 => CError.InputOutputError,
+ 6 => CError.NoSuchDeviceOrAddress,
+ 7 => CError.ArgumentListTooLong,
+ 8 => CError.ExecFormatError,
+ 9 => CError.BadFileDescriptor,
+ 10 => CError.NoChildProcesses,
+ 11 => CError.ResourceTemporarilyUnavailable,
+ 12 => CError.CannotAllocateMemory,
+ 13 => CError.PermissionDenied,
+ 14 => CError.BadAddress,
+ 15 => CError.BlockDeviceRequired,
+ 16 => CError.DeviceOrResourceBusy,
+ 17 => CError.FileExists,
+ 18 => CError.InvalidCrossDeviceLink,
+ 19 => CError.NoSuchDevice,
+ 20 => CError.NotADirectory,
+ 21 => CError.IsADirectory,
+ 22 => CError.InvalidArgument,
+ 23 => CError.TooManyOpenFilesInSystem,
+ 24 => CError.TooManyOpenFiles,
+ 25 => CError.InappropriateIoctlForDevice,
+ 26 => CError.TextFileBusy,
+ 27 => CError.FileTooLarge,
+ 28 => CError.NoSpaceLeftOnDevice,
+ 29 => CError.IllegalSeek,
+ 30 => CError.ReadOnlyFileSystem,
+ 31 => CError.TooManyLinks,
+ 32 => CError.BrokenPipe,
+ 33 => CError.NumericalArgumentOutOfDomain,
+ 34 => CError.NumericalResultOutOfRange,
+ 35 => CError.ResourceDeadlockAvoided,
+ 36 => CError.FileNameTooLong,
+ 37 => CError.NoLocksAvailable,
+ 38 => CError.FunctionNotImplemented,
+ 39 => CError.DirectoryNotEmpty,
+ 40 => CError.TooManyLevelsOfSymbolicLinks,
+ 42 => CError.NoMessageOfDesiredType,
+ 43 => CError.IdentifierRemoved,
+ 44 => CError.ChannelNumberOutOfRange,
+ 45 => CError.Level2NotSynchronized,
+ 46 => CError.Level3Halted,
+ 47 => CError.Level3Reset,
+ 48 => CError.LinkNumberOutOfRange,
+ 49 => CError.ProtocolDriverNotAttached,
+ 50 => CError.NoCSIStructureAvailable,
+ 51 => CError.Level2Halted,
+ 52 => CError.InvalidExchange,
+ 53 => CError.InvalidRequestDescriptor,
+ 54 => CError.ExchangeFull,
+ 55 => CError.NoAnode,
+ 56 => CError.InvalidRequestCode,
+ 57 => CError.InvalidSlot,
+ 59 => CError.BadFontFileFormat,
+ 60 => CError.DeviceNotAStream,
+ 61 => CError.NoDataAvailable,
+ 62 => CError.TimerExpired,
+ 63 => CError.OutOfStreamsResources,
+ 64 => CError.MachineIsNotOnTheNetwork,
+ 65 => CError.PackageNotInstalled,
+ 66 => CError.ObjectIsRemote,
+ 67 => CError.LinkHasBeenSevered,
+ 68 => CError.AdvertiseError,
+ 69 => CError.SrmountError,
+ 70 => CError.CommunicationErrorOnSend,
+ 71 => CError.ProtocolError,
+ 72 => CError.MultihopAttempted,
+ 73 => CError.RFSSpecificError,
+ 74 => CError.BadMessage,
+ 75 => CError.ValueTooLargeForDefinedDataType,
+ 76 => CError.NameNotUniqueOnNetwork,
+ 77 => CError.FileDescriptorInBadState,
+ 78 => CError.RemoteAddressChanged,
+ 79 => CError.CanNotAccessANeededSharedLibrary,
+ 80 => CError.AccessingACorruptedSharedLibrary,
+ 81 => CError.LibSectionInAOutCorrupted,
+ 82 => CError.AttemptingToLinkInTooManySharedLibraries,
+ 83 => CError.CannotExecASharedLibraryDirectly,
+ 84 => CError.InvalidOrIncompleteMultibyteOrWideCharacter,
+ 85 => CError.InterruptedSystemCallShouldBeRestarted,
+ 86 => CError.StreamsPipeError,
+ 87 => CError.TooManyUsers,
+ 88 => CError.SocketOperationOnNonSocket,
+ 89 => CError.DestinationAddressRequired,
+ 90 => CError.MessageTooLong,
+ 91 => CError.ProtocolWrongTypeForSocket,
+ 92 => CError.ProtocolNotAvailable,
+ 93 => CError.ProtocolNotSupported,
+ 94 => CError.SocketTypeNotSupported,
+ 95 => CError.OperationNotSupported,
+ 96 => CError.ProtocolFamilyNotSupported,
+ 97 => CError.AddressFamilyNotSupportedByProtocol,
+ 98 => CError.AddressAlreadyInUse,
+ 99 => CError.CannotAssignRequestedAddress,
+ 100 => CError.NetworkIsDown,
+ 101 => CError.NetworkIsUnreachable,
+ 102 => CError.NetworkDroppedConnectionOnReset,
+ 103 => CError.SoftwareCausedConnectionAbort,
+ 104 => CError.ConnectionResetByPeer,
+ 105 => CError.NoBufferSpaceAvailable,
+ 106 => CError.TransportEndpointIsAlreadyConnected,
+ 107 => CError.TransportEndpointIsNotConnected,
+ 108 => CError.CannotSendAfterTransportEndpointShutdown,
+ 109 => CError.TooManyReferences,
+ 110 => CError.ConnectionTimedOut,
+ 111 => CError.ConnectionRefused,
+ 112 => CError.HostIsDown,
+ 113 => CError.NoRouteToHost,
+ 114 => CError.OperationAlreadyInProgress,
+ 115 => CError.OperationNowInProgress,
+ 116 => CError.StaleFileHandle,
+ 117 => CError.StructureNeedsCleaning,
+ 118 => CError.NotAXENIXNamedTypeFile,
+ 119 => CError.NoXENIXSemaphoresAvailable,
+ 120 => CError.IsANamedTypeFile,
+ 121 => CError.RemoteIOError,
+ 122 => CError.DiskQuotaExceeded,
+ 123 => CError.NoMediumFound,
+ 124 => CError.WrongMediumType,
+ 125 => CError.OperationCanceled,
+ 126 => CError.RequiredKeyNotAvailable,
+ 127 => CError.KeyHasExpired,
+ 128 => CError.KeyHasBeenRevoked,
+ 129 => CError.KeyWasRejectedByService,
+ 130 => CError.OwnerDied,
+ 131 => CError.StateNotRecoverable,
+ 132 => CError.OperationNotPossibleDueToRFKill,
+ 133 => CError.MemoryPageHasHardwareError,
+ else => void{}
+ };
+}
diff --git a/src/screen/drm/card.zig b/src/screen/drm/card.zig
new file mode 100644
index 0000000..67f81f3
--- /dev/null
+++ b/src/screen/drm/card.zig
@@ -0,0 +1,45 @@
+const std = @import("std");
+const os = std.os.linux;
+
+const cerror = @import("../cerror.zig");
+
+const Resources = @import("resources.zig").Resources;
+const Connector = @import("connector.zig").Connector;
+
+
+pub const Card = struct {
+ const Self = @This();
+
+ file: std.fs.File,
+ allocator: std.mem.Allocator,
+
+ pub fn open(name: []const u8, allocator: std.mem.Allocator) !Self {
+ var dri_dir = try std.fs.openDirAbsolute("/dev/dri", .{});
+ defer dri_dir.close();
+
+ return .{
+ .file = try dri_dir.openFile(name, .{
+ .mode = .read_write,
+ .lock_nonblocking = true,
+ }),
+ .allocator = allocator,
+ };
+ }
+
+ pub fn close(self: *Card) void {
+ self.file.close();
+ }
+
+ pub fn is_kms(self: *Card) !bool {
+ const raw_info = Resources.raw_without_ids(self);
+ return raw_info.count_crtcs > 0 and raw_info.count_connectors > 0 and raw_info.count_encoders > 0;
+ }
+
+ pub fn resources(self: *Card) !Resources {
+ return Resources.init(self);
+ }
+
+ pub fn connector(self: *Card, id: u32) !Connector {
+ return Connector.init(self, id);
+ }
+};
diff --git a/src/screen/drm/connector.zig b/src/screen/drm/connector.zig
new file mode 100644
index 0000000..9d7f9ad
--- /dev/null
+++ b/src/screen/drm/connector.zig
@@ -0,0 +1,156 @@
+const std = @import("std");
+const os = std.os.linux;
+const Drm = @import("request.zig").Drm;
+const Card = @import("card.zig").Card;
+
+const RawConnector = extern struct {
+ encoder_ids: ?*u32,
+ modes: ?*ModeInfo,
+ prop_ids: ?*u32,
+ prop_value_ids: ?*u64,
+ count_modes: u32,
+ count_props: u32,
+ count_encoders: u32,
+ encoder_id: u32,
+ connector_id: u32,
+ connector_type: u32,
+ connector_type_id: u32,
+ connection: Connection,
+ mm_width: u32,
+ mm_height: u32,
+ subpixel: u32,
+ pad: u32,
+};
+
+const Connection = enum(u32) {
+ undefined_state = 0,
+ connected = 1,
+ disconnected = 2,
+ unknown = 3,
+};
+
+const ModeInfo = extern struct {
+ const Self = @This();
+ const Flags = packed struct(u32) {
+ phsync: bool,
+ nhsync: bool,
+ pvsync: bool,
+ nvsync: bool,
+ interlace: bool,
+ dblscan: bool,
+ csync: bool,
+ pcsync: bool,
+ ncsync: bool,
+ hskew: bool,
+ bcast: bool,
+ pixmux: bool,
+ dblclk: bool,
+ clkdiv2: bool,
+ _padding: u18,
+ };
+
+ 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: Flags,
+ type: u32,
+ name: [32]u8,
+
+ pub fn frame_rate(self: *const Self) f32 {
+ var rate = (@as(u64, @intCast(self.clock)) * 1000000 / self.htotal + self.vtotal / 2) / self.vtotal;
+
+ if (self.flags.interlace) {
+ rate *= 2;
+ }
+
+ if (self.flags.dblscan) {
+ rate /= 2;
+ }
+
+ if (self.vscan > 1) {
+ rate /= self.vscan;
+ }
+
+ return @as(f32, @floatFromInt(rate)) / 1000.0;
+ }
+};
+
+pub const Connector = struct {
+ const Self = @This();
+
+ card: *Card,
+
+ encoder_ids: []u32,
+ modes: []ModeInfo,
+ prop_ids: []u32,
+ prop_value_ids: []u64,
+ encoder_id: u32,
+ connector_id: u32,
+ connector_type: u32,
+ connector_type_id: u32,
+ connection: Connection,
+ mm_width: u32,
+ mm_height: u32,
+ subpixel: u32,
+ pad: u32,
+
+ pub fn raw_without_ids(card: *Card, id: u32) !RawConnector {
+ var result = std.mem.zeroInit(RawConnector, .{ .connector_id = id });
+ try Drm.get_connector.request(card.file.handle, RawConnector, &result);
+ return result;
+ }
+
+ // NOTE: This function does not take in account
+ // that there might be some hot-plugging going
+ // on. This might have to change in the future.
+ pub fn init(card: *Card, id: u32) !Self {
+ var raw = try Self.raw_without_ids(card, id);
+ const resources = .{
+ .encoder_ids = try card.allocator.alloc(u32, raw.count_encoders),
+ .modes = try card.allocator.alloc(ModeInfo, raw.count_modes),
+ .prop_ids = try card.allocator.alloc(u32, raw.count_props),
+ .prop_value_ids = try card.allocator.alloc(u64, raw.count_props),
+ .encoder_id = raw.encoder_id,
+ .connector_id = raw.connector_id,
+ .connector_type = raw.connector_type,
+ .connector_type_id = raw.connector_type_id,
+ .connection = raw.connection,
+ .mm_width = raw.mm_width,
+ .mm_height = raw.mm_height,
+ .subpixel = raw.subpixel,
+ .pad = raw.pad,
+ .card = card,
+ };
+
+ @memset(resources.encoder_ids, 0);
+ @memset(resources.modes, std.mem.zeroes(ModeInfo));
+ @memset(resources.prop_ids, 0);
+ @memset(resources.prop_value_ids, 0);
+
+ raw.encoder_ids = @ptrCast(resources.encoder_ids);
+ raw.modes = @ptrCast(resources.modes);
+ raw.prop_ids = @ptrCast(resources.prop_ids);
+ raw.prop_value_ids = @ptrCast(resources.prop_value_ids);
+
+ try Drm.get_connector.request(card.file.handle, RawConnector, &raw);
+
+ return resources;
+ }
+
+ pub fn deinit(self: *Self) void {
+ self.card.allocator.free(self.encoder_ids);
+ self.card.allocator.free(self.modes);
+ self.card.allocator.free(self.prop_ids);
+ }
+};
diff --git a/src/screen/drm/request.zig b/src/screen/drm/request.zig
new file mode 100644
index 0000000..bf9701c
--- /dev/null
+++ b/src/screen/drm/request.zig
@@ -0,0 +1,19 @@
+const std = @import("std");
+const os = std.os.linux;
+const cerror = @import("../cerror.zig");
+
+fn ioctl(fd: os.fd_t, request: u32, arg: usize) !void {
+ try cerror.from_usize(os.ioctl(fd, request, arg));
+}
+
+pub const Drm = enum(u8) {
+ const Self = @This();
+
+ get_resources = 0xA0,
+ get_connector = 0xA7,
+
+ pub fn request(self: Self, fd: os.fd_t, T: type, arg: *T) !void {
+ const id = os.IOCTL.IOWR('d', @intFromEnum(self), T);
+ try ioctl(fd, id, @intFromPtr(arg));
+ }
+};
diff --git a/src/screen/drm/resources.zig b/src/screen/drm/resources.zig
new file mode 100644
index 0000000..6128f37
--- /dev/null
+++ b/src/screen/drm/resources.zig
@@ -0,0 +1,76 @@
+const std = @import("std");
+const os = std.os.linux;
+const cerror = @import("../cerror.zig");
+const Card = @import("card.zig").Card;
+const Drm = @import("request.zig").Drm;
+
+const RawResources = extern struct {
+ fb_ids: ?*u32,
+ crtc_ids: ?*u32,
+ connector_ids: ?*u32,
+ encoder_ids: ?*u32,
+ 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 Resources = struct {
+ const Self = @This();
+ const Range = struct { min: u32, max: u32 };
+
+ card: *Card,
+
+ fb_ids: []u32,
+ crtc_ids: []u32,
+ connector_ids: []u32,
+ encoder_ids: []u32,
+ width: Range,
+ height: Range,
+
+ pub fn raw_without_ids(card: *Card) !RawResources {
+ var result = std.mem.zeroes(RawResources);
+ try Drm.get_resources.request(card.file.handle, RawResources, &result);
+ return result;
+ }
+
+ // NOTE: This function does not take in account
+ // that there might be some hot-plugging going
+ // on. This might have to change in the future.
+ pub fn init(card: *Card) !Self {
+ var raw = try Self.raw_without_ids(card);
+ const resources = .{
+ .fb_ids = try card.allocator.alloc(u32, raw.count_fbs),
+ .crtc_ids = try card.allocator.alloc(u32, raw.count_crtcs),
+ .connector_ids = try card.allocator.alloc(u32, raw.count_connectors),
+ .encoder_ids = try card.allocator.alloc(u32, raw.count_encoders),
+ .width = .{ .min = raw.min_width, .max = raw.max_width },
+ .height = .{ .min = raw.min_height, .max = raw.max_height },
+ .card = card,
+ };
+
+ @memset(resources.fb_ids, 0);
+ @memset(resources.crtc_ids, 0);
+ @memset(resources.connector_ids, 0);
+ @memset(resources.encoder_ids, 0);
+
+ raw.fb_ids = @ptrCast(resources.fb_ids);
+ raw.crtc_ids = @ptrCast(resources.crtc_ids);
+ raw.connector_ids = @ptrCast(resources.connector_ids);
+ raw.encoder_ids = @ptrCast(resources.encoder_ids);
+ try Drm.get_resources.request(card.file.handle, RawResources, &raw);
+
+ return resources;
+ }
+
+ pub fn deinit(self: *Self) void {
+ self.card.allocator.free(self.fb_ids);
+ self.card.allocator.free(self.crtc_ids);
+ self.card.allocator.free(self.connector_ids);
+ self.card.allocator.free(self.encoder_ids);
+ }
+};
diff --git a/src/screen/main.zig b/src/screen/main.zig
new file mode 100644
index 0000000..c23e7c5
--- /dev/null
+++ b/src/screen/main.zig
@@ -0,0 +1,20 @@
+const std = @import("std");
+const drm = @import("drm/card.zig");
+
+pub fn main() !void {
+ var gpa = std.heap.GeneralPurposeAllocator(.{}){};
+ const allocator = gpa.allocator();
+
+ var card = try drm.Card.open("card0", allocator);
+ defer card.close();
+
+ var resources = try card.resources();
+ defer resources.deinit();
+
+ var connector = try card.connector(resources.connector_ids[0]);
+ defer connector.deinit();
+
+ for (connector.modes) |mode| {
+ std.debug.print("{d}x{d}@{d}\n", .{mode.hdisplay, mode.vdisplay, mode.frame_rate()});
+ }
+}
diff --git a/src/main.zig b/src/shell/main.zig
index 2144798..337185b 100644
--- a/src/main.zig
+++ b/src/shell/main.zig
@@ -1,10 +1,10 @@
const std = @import("std");
-const eostre = @import("eostre");
-const Cursor = eostre.cursor.Cursor;
-const Parser = eostre.parser.Parser;
-const Context = eostre.parser.Context;
-const Literal = eostre.parser.common.Literal;
-const ParseFn = eostre.parser.ParseFn;
+const estd = @import("estd");
+const Cursor = estd.cursor.Cursor;
+const Parser = estd.parser.Parser;
+const Context = estd.parser.Context;
+const Literal = estd.parser.common.Literal;
+const ParseFn = estd.parser.ParseFn;
const Keyword = enum {
if_statement,