From a2600208cd5c90bdcbe2cee9ca9004876ffbbc4e Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Tue, 19 May 2020 23:06:59 +0200 Subject: [PATCH] timmoc laitinI --- .gitignore | 1 + build.zig | 27 ++++++ src/main.zig | 93 +++++++++++++++++++ src/sdl.zig | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ttf.zig | 48 ++++++++++ 5 files changed, 424 insertions(+) create mode 100644 .gitignore create mode 100644 build.zig create mode 100644 src/main.zig create mode 100644 src/sdl.zig create mode 100644 src/ttf.zig diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3cef7be --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +zig-cache/ diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..39eab5c --- /dev/null +++ b/build.zig @@ -0,0 +1,27 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard release options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. + const mode = b.standardReleaseOptions(); + + const exe = b.addExecutable("zite", "src/main.zig"); + exe.linkSystemLibrary("c"); + exe.linkSystemLibrary("SDL2"); + exe.linkSystemLibrary("SDL2_ttf"); + exe.setTarget(target); + exe.setBuildMode(mode); + exe.install(); + + const run_cmd = exe.run(); + run_cmd.step.dependOn(b.getInstallStep()); + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); +} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..50a306e --- /dev/null +++ b/src/main.zig @@ -0,0 +1,93 @@ +const std = @import("std"); +const sdl = @import("sdl.zig"); +const ttf = @import("ttf.zig"); + +const Ui = struct { + window: sdl.Window, + renderer: sdl.Renderer, + + uiFont: ttf.Font, + codeFont: ttf.Font, + + treeWidget: TreeWidget, + + pub fn init() !Ui { + try sdl.init(); + var window = try sdl.Window.init("Zite", 1280, 720, sdl.Window.Shown); + var renderer = try window.createRenderer(null, 0); + + try ttf.init(); + var uiFont = try ttf.Font.init("/usr/share/fonts/ubuntu/Ubuntu-R.ttf", 16); + var codeFont = try ttf.Font.init("/usr/share/fonts/TTF/Cascadia.ttf", 16); + + var treeWidget = TreeWidget.init(); + + return Ui{ + .window = window, + .renderer = renderer, + + .uiFont = uiFont, + .codeFont = codeFont, + + .treeWidget = treeWidget, + }; + } + + pub fn deinit(self: *Ui) void { + self.uiFont.deinit(); + self.codeFont.deinit(); + ttf.quit(); + + self.renderer.deinit(); + self.window.deinit(); + sdl.quit(); + } + + pub fn update(self: Ui) !void { + while (sdl.Event.poll()) |event| { + var type_ = event.type_(); + // std.debug.warn("event: {}\n", .{@tagName(type_)}); + + switch (type_) { + .Quit => return error.Exiting, + else => {}, + } + } + } + + pub fn render(self: Ui) !void { + try self.renderer.setDrawColor(.{ .r = 30, .g = 30, .b = 30 }); + try self.renderer.clear(); + + try self.treeWidget.render(self); + + self.renderer.present(); + } +}; + +const TreeWidget = struct { + pub fn init() TreeWidget { + return .{}; + } + + pub fn render(self: TreeWidget, ui: Ui) !void { + var surface = try ui.uiFont.renderBlended("Hello world", .{ .r = 255, .g = 255, .b = 255 }); + defer surface.deinit(); + + var texture = try ui.renderer.createTextureFromSurface(surface); + defer texture.deinit(); + + var dst = sdl.Rect{ .w = surface.width(), .h = surface.height() }; + try ui.renderer.copy(texture, null, dst); + } +}; + +pub fn main() !void { + var ui = try Ui.init(); + defer ui.deinit(); + + while (true) { + try ui.update(); + try ui.render(); + } +} diff --git a/src/sdl.zig b/src/sdl.zig new file mode 100644 index 0000000..e0ff5ed --- /dev/null +++ b/src/sdl.zig @@ -0,0 +1,255 @@ +pub const c = @cImport(@cInclude("SDL2/SDL.h")); + +pub const Color = struct { + r: u8, + g: u8, + b: u8, + a: u8 = c.SDL_ALPHA_OPAQUE, +}; + +pub const WindowPos = struct { + const Undefined: i32 = c.SDL_WINDOWPOS_UNDEFINED; + const Centered: i32 = c.SDL_WINDOWPOS_CENTERED; +}; + +pub const Window = struct { + pub const Shown: i32 = c.SDL_WINDOW_SHOWN; + + window: *c.SDL_Window, + + pub fn init(title: []const u8, w: i32, h: i32, flags: u32) !Window { + return Window.initWithPos(title, WindowPos.Undefined, WindowPos.Undefined, w, h, flags); + } + + pub fn initWithPos(title: []const u8, x: i32, y: i32, w: i32, h: i32, flags: u32) !Window { + var title_ = @ptrCast([*c]const u8, title); + var window = c.SDL_CreateWindow(title_, x, y, w, h, flags) orelse return error.SdlError; + return Window{ .window = window }; + } + + pub fn deinit(self: *Window) void { + c.SDL_DestroyWindow(self.window); + } + + pub fn surface(self: Window) !Surface { + return Surface{ + .surface = c.SDL_GetWindowSurface(self.window) orelse return error.SdlError, + }; + } + + pub fn updateSurface(self: Window) !void { + if (c.SDL_UpdateWindowSurface(self.window) != 0) { + return error.SdlError; + } + } + + pub fn createRenderer(self: Window, index: ?i32, flags: u32) !Renderer { + var renderer = c.SDL_CreateRenderer(self.window, index orelse -1, flags) orelse return error.SdlError; + return Renderer{ .renderer = renderer }; + } +}; + +pub const Renderer = struct { + renderer: *c.SDL_Renderer, + + pub fn deinit(self: Renderer) void { + c.SDL_DestroyRenderer(self.renderer); + } + + pub fn createTextureFromSurface(self: Renderer, surface: Surface) !Texture { + var texture = c.SDL_CreateTextureFromSurface(self.renderer, surface.surface) orelse return error.SdlError; + return Texture{ .texture = texture }; + } + + pub fn clear(self: Renderer) !void { + if (c.SDL_RenderClear(self.renderer) != 0) { + return error.SdlError; + } + } + + pub fn copy(self: Renderer, texture: Texture, srcrect: ?Rect, dstrect: ?Rect) !void { + var src = if (srcrect) |inner| &inner.c() else null; + var dst = if (dstrect) |inner| &inner.c() else null; + if (c.SDL_RenderCopy(self.renderer, texture.texture, src, dst) != 0) { + return error.SdlError; + } + } + + pub fn present(self: Renderer) void { + c.SDL_RenderPresent(self.renderer); + } + + pub fn setDrawColor(self: Renderer, color: Color) !void { + if (c.SDL_SetRenderDrawColor(self.renderer, color.r, color.g, color.b, color.a) != 0) { + return error.SdlError; + } + } +}; + +pub const Texture = struct { + texture: *c.SDL_Texture, + + pub fn deinit(self: Texture) void { + c.SDL_DestroyTexture(self.texture); + } +}; + +pub const Surface = struct { + surface: *c.SDL_Surface, + + pub fn initFromBmp(path: []const u8) !Surface { + var path_ = @ptrCast([*c]const u8, path); + var rw = c.SDL_RWFromFile(path_, "rb") orelse return error.SdlError; + var surface = c.SDL_LoadBMP_RW(rw, 1) orelse return error.SdlError; + return Surface{ .surface = surface }; + } + + pub fn deinit(self: Surface) void { + c.SDL_FreeSurface(self.surface); + } + + pub fn fillRect(self: Surface, rect: [*c]const c.SDL_Rect, color: u32) !void { + if (c.SDL_FillRect(self.surface, rect, color) != 0) { + return error.SdlError; + } + } + + pub fn format(self: Surface) PixelFormat { + return PixelFormat{ .pixelFormat = self.surface.*.format }; + } + + pub fn width(self: Surface) i32 { + return self.surface.w; + } + + pub fn height(self: Surface) i32 { + return self.surface.h; + } +}; + +pub const PixelFormat = struct { + pixelFormat: *c.SDL_PixelFormat, + + pub fn mapRgb(self: PixelFormat, r: u8, g: u8, b: u8) u32 { + return c.SDL_MapRGB(self.pixelFormat, r, g, b); + } +}; + +pub const Rect = struct { + x: i32 = 0, + y: i32 = 0, + w: i32 = 0, + h: i32 = 0, + + fn c(self: Rect) c.SDL_Rect { + return .{ + .x = self.x, + .y = self.y, + .w = self.w, + .h = self.h, + }; + } +}; + +pub fn init() !void { + // TODO Make this configurable + if (c.SDL_Init(c.SDL_INIT_EVERYTHING) != 0) { + return error.SdlError; + } +} + +pub fn quit() void { + c.SDL_Quit(); +} + +pub fn delay(ms: u32) void { + c.SDL_Delay(ms); +} + +pub const EventType = enum(u32) { + FirstEvent = 0, + Quit = 0x100, + AppTerminating, + AppLowMemory, + AppWillEnterBackground, + AppDidEnterBackground, + AppWillEnterForeground, + AppDidEnterForeground, + LocaleChanged, + + DisplayEvent = 0x150, + + WindowEvent = 0x200, + SysWmEvent, + + KeyDown = 0x300, + KeyUp, + TextEditing, + TextInput, + KeymapChanged, + + MouseMotion = 0x400, + MouseButtonDown, + MouseButtonUp, + MouseWheel, + + JoyAxisMotion = 0x600, + JoyBallMotion, + JoyHatMotion, + JoyButtonDown, + JoyButtonUp, + JoyDeviceAdded, + JoyDeviceRemoved, + + ControllerAxisMotion = 0x650, + ControllerButtonDown, + ControllerButtonUp, + ControllerDeviceAdded, + ControllerDeviceRemoved, + ControllerDeviceRemapped, + + FingerDown = 0x700, + FingerUp, + FingerMotion, + + DollarGesture = 0x800, + DollarRecord, + MultiGesture, + + ClipboardUpate = 0x900, + + DropFile = 0x1000, + DropText, + DropBegin, + DropComplete, + + AudioDeviceAdded = 0x1100, + AudioDeviceRemoved, + + SensorUpdate = 0x1200, + + RenderTargetsReset = 0x2000, + RenderDeviceReset, + + UserEvent = 0x8000, + + LastEvent = 0xFFFF, + + _, +}; + +pub const Event = struct { + event: c.SDL_Event, + + pub fn poll() ?Event { + var event = c.SDL_Event{ .type = 0 }; + if (c.SDL_PollEvent(&event) == 0) { + return null; + } + return Event{ .event = event }; + } + + pub fn type_(self: Event) EventType { + return @intToEnum(EventType, self.event.type); + } +}; diff --git a/src/ttf.zig b/src/ttf.zig new file mode 100644 index 0000000..4787c79 --- /dev/null +++ b/src/ttf.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const sdl = @import("sdl.zig"); +pub const c = @cImport(@cInclude("SDL2/SDL_ttf.h")); + +pub fn init() !void { + if (c.TTF_Init() == -1) { + return error.SdlTtfError; + } +} + +pub fn quit() void { + c.TTF_Quit(); +} + +pub fn getError() ?[*:0]const u8 { + if (c.TTF_GetError()) |inner| { + return @ptrCast([*:0]const u8, inner); + } + return null; +} + +pub const Font = struct { + font: *c.TTF_Font, + + pub fn init(path: []const u8, size: i32) !Font { + var path_ = @ptrCast([*c]const u8, path); + if (c.TTF_OpenFont(path_, size)) |inner| { + return Font{ .font = inner }; + } else { + std.debug.warn("Font.init: {}\n", .{getError()}); + return error.SdlTtfError; + } + } + + pub fn deinit(self: Font) void { + c.TTF_CloseFont(self.font); + } + + pub fn renderBlended(self: Font, text: [:0]const u8, color: sdl.Color) !sdl.Surface { + var cText = @ptrCast([*c]const u8, text); + if (c.TTF_RenderUTF8_Blended(self.font, cText, @bitCast(c.SDL_Color, color))) |surface| { + return sdl.Surface{ .surface = @ptrCast(*sdl.c.SDL_Surface, surface) }; + } else { + std.debug.warn("Font.renderBlended: {}\n", .{getError()}); + return error.SdlTtfError; + } + } +};