From 7f3dcce048ede2dd8180966a5ca5b5a41b5b79dc Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Fri, 22 May 2020 17:46:21 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + build.zig | 14 ++ src/main.zig | 2 + src/sdl.zig | 398 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ttf.zig | 48 +++++++ 5 files changed, 463 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..0a4f2dc --- /dev/null +++ b/build.zig @@ -0,0 +1,14 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + const lib = b.addStaticLibrary("zig-sdl", "src/main.zig"); + lib.setBuildMode(mode); + lib.install(); + + var main_tests = b.addTest("src/main.zig"); + main_tests.setBuildMode(mode); + + const test_step = b.step("test", "Run library tests"); + test_step.dependOn(&main_tests.step); +} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..eb09ba6 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,2 @@ +pub const sdl = @import("sdl.zig"); +pub const ttf = @import("ttf.zig"); diff --git a/src/sdl.zig b/src/sdl.zig new file mode 100644 index 0000000..794b9b6 --- /dev/null +++ b/src/sdl.zig @@ -0,0 +1,398 @@ +const std = @import("std"); +pub const c = @cImport(@cInclude("SDL2/SDL.h")); + +pub fn getError() ?[*:0]const u8 { + if (c.SDL_GetError()) |inner| { + return @ptrCast([*:0]const u8, inner); + } + return null; +} + +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 WindowFlags = struct { + pub const Fullscreen: i32 = c.SDL_WINDOW_FULLSCREEN; + pub const OpenGL: i32 = c.SDL_WINDOW_OPENGL; + pub const Shown: i32 = c.SDL_WINDOW_SHOWN; + pub const Hidden: i32 = c.SDL_WINDOW_HIDDEN; + pub const Borderless: i32 = c.SDL_WINDOW_BORDERLESS; + pub const Resizable: i32 = c.SDL_WINDOW_RESIZABLE; + pub const Minimized: i32 = c.SDL_WINDOW_MINIMIZED; + pub const InputGrabbed: i32 = c.SDL_WINDOW_INPUT_GRABBED; + pub const InputFocus: i32 = c.SDL_WINDOW_INPUT_FOCUS; + pub const MouseFocus: i32 = c.SDL_WINDOW_MOUSE_FOCUS; + pub const FullscreenDesktop: i32 = c.SDL_WINDOW_FULLSCREEN_DESKTOP; + pub const Foreign: i32 = c.SDL_WINDOW_FOREIGN; + pub const AllowHighDpi: i32 = c.SDL_WINDOW_ALLOW_HIGHDPI; + pub const MouseCapture: i32 = c.SDL_WINDOW_MOUSE_CAPTURE; + pub const AlwaysOnTop: i32 = c.SDL_WINDOW_ALWAYS_ON_TOP; + pub const SkipTaskbar: i32 = c.SDL_WINDOW_SKIP_TASKBAR; + pub const Utility: i32 = c.SDL_WINDOW_UTILITY; + pub const Tooltip: i32 = c.SDL_WINDOW_TOOLTIP; + pub const PopupMenu: i32 = c.SDL_WINDOW_POPUP_MENU; + pub const Vulkan: i32 = c.SDL_WINDOW_VULKAN; + pub const Metal: i32 = c.SDL_WINDOW_METAL; +}; + +pub const Window = struct { + 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 { + std.debug.warn("Window.initWithPos: {}\n", .{getError()}); + return error.SdlError; + }; + return Window{ .window = window }; + } + + pub fn deinit(self: *Window) void { + c.SDL_DestroyWindow(self.window); + } + + pub fn surface(self: Window) !Surface { + var c = c.SDL_GetWindowSurface(self.window) orelse { + std.debug.warn("Window.surface: {}\n", .{getError()}); + return error.SdlError; + }; + return Surface{ .surface = c }; + } + + pub fn updateSurface(self: Window) !void { + if (c.SDL_UpdateWindowSurface(self.window) != 0) { + std.debug.warn("Window.updateSurface: {}\n", .{getError()}); + 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 { + std.debug.warn("Window.createRenderer: {}\n", .{getError()}); + return error.SdlError; + }; + return Renderer{ .renderer = renderer }; + } +}; + +pub const RendererFlags = struct { + pub const Software: u32 = c.SDL_RENDERER_SOFTWARE; + pub const Accelerated: u32 = c.SDL_RENDERER_ACCELERATED; + pub const PresentVSync: u32 = c.SDL_RENDERER_PRESENTVSYNC; + pub const TargetTexture: u32 = c.SDL_RENDERER_TARGETTEXTURE; +}; + +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 fn fillRect(self: Renderer, rect: ?Rect) !void { + var dst = if (rect) |inner| &inner.c() else null; + if (c.SDL_RenderFillRect(self.renderer, dst) != 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 MouseMotionEvent = struct { + timestamp: u32, + windowId: u32, + which: u32, + state: u32, + x: i32, + y: i32, + xrel: i32, + yrel: i32, +}; + +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); + } + + pub fn mouseMotion(self: Event) ?MouseMotionEvent { + if (self.type_() != .MouseMotion) { + return null; + } + + var inner = self.event.motion; + return MouseMotionEvent{ + .timestamp = inner.timestamp, + .windowId = inner.windowID, + .which = inner.which, + .state = inner.state, + .x = inner.x, + .y = inner.y, + .xrel = inner.xrel, + .yrel = inner.yrel, + }; + } +}; + +pub const SystemCursor = extern enum { + Arrow = c.SDL_SYSTEM_CURSOR_ARROW, + IBeam = c.SDL_SYSTEM_CURSOR_IBEAM, + Wait = c.SDL_SYSTEM_CURSOR_WAIT, + Crosshair = c.SDL_SYSTEM_CURSOR_CROSSHAIR, + WaitArrow = c.SDL_SYSTEM_CURSOR_WAITARROW, + SizeNwSe = c.SDL_SYSTEM_CURSOR_SIZENWSE, + SizeNeSw = c.SDL_SYSTEM_CURSOR_SIZENESW, + SizeWE = c.SDL_SYSTEM_CURSOR_SIZEWE, + SizeNS = c.SDL_SYSTEM_CURSOR_SIZENS, + SizeAll = c.SDL_SYSTEM_CURSOR_SIZEALL, + No = c.SDL_SYSTEM_CURSOR_NO, + Hand = c.SDL_SYSTEM_CURSOR_HAND, +}; + +pub const Cursor = struct { + cursor: *c.SDL_Cursor, + + pub fn initFromSystemCursor(id: SystemCursor) !Cursor { + var cursor = c.SDL_CreateSystemCursor(@intToEnum(c.SDL_SystemCursor, @enumToInt(id))) orelse { + std.debug.warn("Window.initWithPos: {}\n", .{getError()}); + return error.SdlError; + }; + return Cursor{ .cursor = cursor }; + } + + pub fn deinit(self: Cursor) void { + c.SDL_FreeCursor(self.cursor); + } + + pub fn activate(self: Cursor) void { + c.SDL_SetCursor(self.cursor); + } +}; + +pub const PerformanceCounter = struct { + counter: u64, + + pub fn init(counter: u64) PerformanceCounter { + return PerformanceCounter{ .counter = counter }; + } + + pub fn now() PerformanceCounter { + return PerformanceCounter{ .counter = c.SDL_GetPerformanceCounter() }; + } + + pub fn elapsed(self: PerformanceCounter) PerformanceCounter { + return PerformanceCounter{ + .counter = c.SDL_GetPerformanceCounter() - self.counter, + }; + } + + pub fn frequency() u64 { + return c.SDL_GetPerformanceFrequency(); + } + + pub fn ms(self: PerformanceCounter) f64 { + return @intToFloat(f64, self.counter) * 1000 / @intToFloat(f64, PerformanceCounter.frequency()); + } +}; diff --git a/src/ttf.zig b/src/ttf.zig new file mode 100644 index 0000000..86cf83b --- /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: []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; + } + } +};