aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2021-12-10 09:42:10 +0100
committerDrew DeVault <sir@cmpwn.com>2021-12-10 09:44:50 +0100
commit9607eb0de784fab1a99833f567c0eaca72b67e4a (patch)
tree7f79a0914a6db1d963d877bed051f6856c54cfa8
parentMakefile: drop -T+libc (diff)
downloadhare-chip8-9607eb0de784fab1a99833f567c0eaca72b67e4a.tar.gz
all: rig up Hare-native error handling
-rw-r--r--cmd/demo/main.ha77
-rw-r--r--sdl2/errors.ha31
-rw-r--r--sdl2/events.ha51
-rw-r--r--sdl2/gamecontroller.ha16
-rw-r--r--sdl2/image/image.ha22
-rw-r--r--sdl2/joystick.ha9
-rw-r--r--sdl2/render.ha92
-rw-r--r--sdl2/video.ha18
8 files changed, 213 insertions, 103 deletions
diff --git a/cmd/demo/main.ha b/cmd/demo/main.ha
index 71c747f..081134a 100644
--- a/cmd/demo/main.ha
+++ b/cmd/demo/main.ha
@@ -21,39 +21,37 @@ type state = struct {
};
export fn main() void = {
+ match (run()) {
+ case let err: sdl2::error =>
+ fmt::fatal("SDL2 error: {}", sdl2::strerror(err));
+ case void => void;
+ };
+};
+
+fn run() (void | sdl2::error) = {
sdl2::init(sdl2::init_flags::VIDEO | sdl2::init_flags::GAMECONTROLLER);
defer sdl2::quit();
image::init(image::init_flags::PNG | image::init_flags::JPG);
defer image::quit();
- const title = strings::to_c("Hare SDL2 demo");
- defer free(title);
- const win = match (sdl2::create_window(title,
+ const win = sdl2::create_window("Hare SDL2 demo",
sdl2::WINDOWPOS_UNDEFINED, sdl2::WINDOWPOS_UNDEFINED,
- 640, 480, window_flags::NONE)) {
- case let win: *sdl2::window =>
- yield win;
- case null =>
- fmt::fatal("sdl2::create_window failed");
- };
+ 640, 480, window_flags::NONE)?;
defer sdl2::destroy_window(win);
- const render = match (sdl2::create_renderer(win, -1, renderer_flags::NONE)) {
- case let rend: *sdl2::renderer =>
- yield rend;
- case null =>
- fmt::fatal("sdl2::create_renderer failed");
- };
+ const render = sdl2::create_renderer(win, -1, renderer_flags::NONE)?;
defer sdl2::destroy_renderer(render);
let controller: nullable *sdl2::gamecontroller = null;
- for (let i = 0; i < sdl2::numjoysticks(); i += 1) {
+ for (let i = 0; i < sdl2::numjoysticks()?; i += 1) {
if (!sdl2::is_game_controller(i)) {
continue;
};
- controller = sdl2::game_controller_open(i);
- if (controller != null) {
+ match (sdl2::game_controller_open(i)) {
+ case let c: *sdl2::gamecontroller =>
+ controller = c;
break;
+ case sdl2::error => void;
};
};
defer match (controller) {
@@ -65,8 +63,8 @@ export fn main() void = {
let state = state {
window = win,
render = render,
- hare = load_object(render, "mascot.jpg"),
- cat = load_object(render, "cat.png"),
+ hare = load_object(render, "mascot.jpg")?,
+ cat = load_object(render, "cat.png")?,
...
};
defer sdl2::destroy_texture(state.hare.tex);
@@ -76,15 +74,15 @@ export fn main() void = {
state.hare.dy = 2;
for (!state.quit) {
- update(&state);
- draw(&state);
+ update(&state)?;
+ draw(&state)?;
sdl2::delay(1000 / 60);
};
};
-fn update(state: *state) void = {
+fn update(state: *state) (void | sdl2::error) = {
let ev = sdl2::event { ... };
- for (sdl2::poll_event(&ev) == 1) switch (ev.event_type) {
+ for (sdl2::poll_event(&ev)? == 1) switch (ev.event_type) {
case event_type::QUIT =>
state.quit = true;
return;
@@ -131,26 +129,26 @@ fn update(state: *state) void = {
};
};
-fn draw(state: *state) void = {
+fn draw(state: *state) (void | sdl2::error) = {
if (state.nbutton == 0) {
- sdl2::set_render_draw_color(state.render, 50, 50, 50, 255);
+ sdl2::set_render_draw_color(state.render, 50, 50, 50, 255)?;
} else {
- sdl2::set_render_draw_color(state.render, 50, 50, 200, 255);
+ sdl2::set_render_draw_color(state.render, 50, 50, 200, 255)?;
};
- sdl2::render_clear(state.render);
+ sdl2::render_clear(state.render)?;
- draw_object(state, &state.hare);
- draw_object(state, &state.cat);
+ draw_object(state, &state.hare)?;
+ draw_object(state, &state.cat)?;
sdl2::render_present(state.render);
};
-fn draw_object(state: *state, obj: *object) void = {
+fn draw_object(state: *state, obj: *object) (void | sdl2::error) = {
sdl2::render_copy(state.render, obj.tex, null, &sdl2::rect {
x = obj.x,
y = obj.y,
w = obj.w,
h = obj.h,
- });
+ })?;
};
fn axis_x(axis: controller_axis) bool = {
@@ -171,19 +169,10 @@ fn axis_y(axis: controller_axis) bool = {
};
};
-fn load_object(render: *sdl2::renderer, path: str) object = {
- const path = strings::to_c(path);
- defer free(path);
-
- const tex = match (image::load_texture(render, path)) {
- case let tex: *sdl2::texture =>
- yield tex;
- case null =>
- fmt::fatal("sdl2::image::load_texture failed for {}", path);
- };
-
+fn load_object(render: *sdl2::renderer, path: str) (object | sdl2::error) = {
+ const tex = image::load_texture(render, path)?;
let w = 0, h = 0;
- sdl2::query_texture(tex, null, null, &w, &h);
+ sdl2::query_texture(tex, null, null, &w, &h)?;
return object {
tex = tex,
w = w,
diff --git a/sdl2/errors.ha b/sdl2/errors.ha
new file mode 100644
index 0000000..bc02505
--- /dev/null
+++ b/sdl2/errors.ha
@@ -0,0 +1,31 @@
+use strings;
+
+// Returned when an error occurs in an SDL function.
+export type error = !str;
+
+// Converts an SDL error into a human-friendly string.
+export fn strerror(err: error) str = {
+ return err: str;
+};
+
+@symbol("SDL_GetError") fn geterror() const *char;
+
+export fn wrapvoid(ret: int) (void | error) = {
+ wrapint(ret)?;
+};
+
+export fn wrapint(ret: int) (int | error) = {
+ if (ret < 0) {
+ return strings::fromc(geterror()): error;
+ };
+ return ret;
+};
+
+export fn wrapptr(ret: nullable *void) (*void | error) = {
+ match (ret) {
+ case let v: *void =>
+ return v;
+ case null =>
+ return strings::fromc(geterror()): error;
+ };
+};
diff --git a/sdl2/events.ha b/sdl2/events.ha
index e26333a..58bbfd7 100644
--- a/sdl2/events.ha
+++ b/sdl2/events.ha
@@ -377,6 +377,9 @@ export type eventaction = enum {
GETEVENT,
};
+@symbol("SDL_PeepEvents") fn _peep_events(events: *event, numevents: int,
+ action: eventaction, mintype: event_type, maxtype: event_type) int;
+
// Checks the event queue for messages and optionally returns them.
//
// If 'action' is ADDEVENT, up to 'numevents' events will be added to the back
@@ -390,11 +393,16 @@ export type eventaction = enum {
// queue, within the specified minimum and maximum type, will be returned and
// will be removed from the queue.
//
-// Returns the number of events actually stored, or -1 if there was an error.
-//
// This function is thread-safe.
-export @symbol("SDL_PeepEvents") fn peep_events(events: *event, numevents: int,
- action: eventaction, mintype: event_type, maxtype: event_type) int;
+export fn peep_events(
+ events: *event,
+ numevents: int,
+ action: eventaction,
+ mintype: event_type,
+ maxtype: event_type,
+) (int | error) = {
+ return wrapint(_peep_events(events, numevents, action, mintype, maxtype));
+};
// Checks to see if certain event types are in the event queue.
export @symbol("SDL_HasEvent") fn has_event(event_type: event_type) bool;
@@ -414,35 +422,48 @@ export @symbol("SDL_FlushEvent") fn flush_event(event_type: event_type) void;
// on the main thread immediately before the flush call.
export @symbol("SDL_FlushEvents") fn flush_events(mintype: event_type, maxtype: event_type) void;
+@symbol("SDL_PollEvent") fn _poll_event(event: nullable *event) int;
+
// Polls for currently pending events.
//
// Returns 1 if there are any pending events, or 0 if there are none available.
//
// If 'event' is not null, the next event is removed from the queue and stored
// in that area.
-export @symbol("SDL_PollEvent") fn poll_event(event: nullable *event) int;
+export fn poll_event(event: nullable *event) (int | error) = {
+ return wrapint(_poll_event(event));
+};
+
+@symbol("SDL_WaitEvent") fn _wait_event(event: nullable *event) int;
// Waits indefinitely for the next available event.
//
-// Returns 1, or 0 if there was an error while waiting for events.
-//
// If 'event' is not null, the next event is removed from the queue and stored
// in that area.
-export @symbol("SDL_WaitEvent") fn wait_event(event: nullable *event) int;
+export fn wait_event(event: nullable *event) (void | error) = {
+ return wrapvoid(_wait_event(event));
+};
+
+@symbol("SDL_WaitEventTimeout") fn _wait_event_timeout(
+ event: nullable *event, timeout: int) int;
// Waits until the specified timeout (in milliseconds) for the next available event.
//
-// Returns 1, or 0 if there was an error while waiting for events.
-//
// If 'event' is not null, the next event is removed from the queue and stored
// in that area. The 'timeout' is the time (in milliseconds) to wait for next
// event.
-export @symbol("SDL_WaitEventTimeout") fn wait_event_timeout(event: nullable *event, timeout: int) int;
+export fn wait_event_timeout(
+ event: nullable *event,
+ timeout: int,
+) (void | error) = {
+ return wrapvoid(_wait_event_timeout(event, timeout));
+};
+
+@symbol("SDL_PushEvent") fn _push_event(event: *event) int;
// Add an event to the event queue.
-//
-// Returns 1 on success, 0 if the event was filtered, or -1 if the event queue
-// was full or there was some other error.
-export @symbol("SDL_PushEvent") fn push_event(event: *event) int;
+export fn push_event(event: *event) (void | error) = {
+ return wrapvoid(_push_event(event));
+};
// TODO: Finish rigging up other SDL_events.h bits
diff --git a/sdl2/gamecontroller.ha b/sdl2/gamecontroller.ha
index 3b752e1..cf78674 100644
--- a/sdl2/gamecontroller.ha
+++ b/sdl2/gamecontroller.ha
@@ -27,16 +27,20 @@ export type controller_axis = enum u8 {
// 'joystick_index' is the same as the 'device_index' passed to
// [[joystick_open]].
//
-// Returns SDL_TRUE if the given joystick is supported by the game controller
-// interface, SDL_FALSE if it isn't or it's an invalid index.
+// Returns true if the given joystick is supported by the game controller
+// interface, false if it isn't or it's an invalid index.
export @symbol("SDL_IsGameController") fn is_game_controller(
joystick_index: int) bool;
+@symbol("SDL_GameControllerOpen") fn _game_controller_open(
+ joystick_index: int) nullable *gamecontroller;
+
// Get the SDL_GameController associated with an instance id.
-//
-// Returns a [[controller]] on success or NULL on failure.
-export @symbol("SDL_GameControllerOpen") fn game_controller_open(
- joystick_index: int) *gamecontroller;
+export fn game_controller_open(
+ joystick_index: int,
+) (*gamecontroller | error) = {
+ return wrapptr(_game_controller_open(joystick_index))?: *gamecontroller;
+};
// Close a game controller previously opened with [[game_controller_open]].
export @symbol("SDL_GameControllerClose") fn game_controller_close(
diff --git a/sdl2/image/image.ha b/sdl2/image/image.ha
index 03301e9..6cabee2 100644
--- a/sdl2/image/image.ha
+++ b/sdl2/image/image.ha
@@ -1,6 +1,7 @@
// TODO: Flesh me out
// TODO: SDL_RWops
use sdl2;
+use strings;
// Flags for [[init]].
export type init_flags = enum int {
@@ -20,9 +21,24 @@ export @symbol("IMG_Init") fn init(flags: init_flags) int;
// Unloads libraries loaded with [[init]]
export @symbol("IMG_Quit") fn quit() void;
+@symbol("IMG_Load") fn _load(file: const *char) nullable *sdl2::surface;
+
// Load an image from a file path.
-export @symbol("IMG_Load") fn load(file: const *char) nullable *sdl2::surface;
+export fn load(file: str) (*sdl2::surface | sdl2::error) = {
+ const file = strings::to_c(file);
+ defer free(file);
+ return sdl2::wrapptr(_load(file))?: *sdl2::surface;
+};
-// Load an image directly into a render texture.
-export @symbol("IMG_LoadTexture") fn load_texture(renderer: *sdl2::renderer,
+@symbol("IMG_LoadTexture") fn _load_texture(renderer: *sdl2::renderer,
file: const *char) nullable *sdl2::texture;
+
+// Load an image directly into a render texture.
+export fn load_texture(
+ renderer: *sdl2::renderer,
+ file: str,
+) (*sdl2::texture | sdl2::error) = {
+ const file = strings::to_c(file);
+ defer free(file);
+ return sdl2::wrapptr(_load_texture(renderer, file))?: *sdl2::texture;
+};
diff --git a/sdl2/joystick.ha b/sdl2/joystick.ha
index c4f4c96..adbcd14 100644
--- a/sdl2/joystick.ha
+++ b/sdl2/joystick.ha
@@ -6,8 +6,7 @@ export def JOYSTICK_AXIS_MIN: i16 = -32768;
// Minimum value for a joystick axis.
export def JOYSTICK_AXIS_MAX: i16 = 32767;
-// Count the number of joysticks attached to the system.
-//
-// Returns the number of attached joysticks on success or a negative error code
-// on failure.
-export @symbol("SDL_NumJoysticks") fn numjoysticks() int;
+@symbol("SDL_NumJoysticks") fn _numjoysticks() int;
+
+// Returns the number of joysticks attached to the system.
+export fn numjoysticks() (int | error) = wrapint(_numjoysticks());
diff --git a/sdl2/render.ha b/sdl2/render.ha
index 5bf1b09..649e786 100644
--- a/sdl2/render.ha
+++ b/sdl2/render.ha
@@ -15,6 +15,10 @@ export type renderer_flags = enum u32 {
TARGETTEXTURE = 0x00000008,
};
+@symbol("SDL_CreateWindowAndRenderer") fn _create_window_and_renderer(
+ width: int, height: int, window_flags: window_flags,
+ window: nullable **window, renderer: nullable **renderer) int;
+
// Create a window and default renderer.
//
// 'width' and 'height' set the width and height of the window, in screen
@@ -22,11 +26,17 @@ export type renderer_flags = enum u32 {
//
// 'window' and 'renderer' are out parameters, or null, which are filled in with
// the created window and renderer respectively.
-//
-// Returns 0 on success, or -1 on error.
-export @symbol("SDL_CreateWindowAndRenderer") fn create_window_and_renderer(
- width: int, height: int, window_flags: window_flags,
- window: nullable **window, renderer: nullable **renderer) int;
+export fn create_window_and_renderer(
+ width: int,
+ height: int,
+ window_flags: window_flags,
+ window: nullable **window,
+ renderer: nullable **renderer,
+) (void | error) = wrapvoid(_create_window_and_renderer(width, height,
+ window_flags, window, renderer));
+
+@symbol("SDL_CreateRenderer") fn _create_renderer(window: *window,
+ index: int, flags: renderer_flags) nullable *renderer;
// Create a 2D rendering context for a window.
//
@@ -34,12 +44,14 @@ export @symbol("SDL_CreateWindowAndRenderer") fn create_window_and_renderer(
// the rendering driver to initialize, or -1 to initialize the first one
// supporting the requested flags.
//
-// Returns a valid rendering context, or NULL if there was an error.
-//
// See also: [[create_software_renderer]], [[get_renderer_info]],
// [[destroy_renderer]].
-export @symbol("SDL_CreateRenderer") fn create_renderer(window: *window,
- index: int, flags: renderer_flags) nullable *renderer;
+export fn create_renderer(
+ window: *window,
+ index: int,
+ flags: renderer_flags,
+) (*renderer | error) =
+ wrapptr(_create_renderer(window, index, flags))?: *renderer;
// Destroy the rendering context for a window and free associated textures.
//
@@ -49,22 +61,27 @@ export @symbol("SDL_DestroyRenderer") fn destroy_renderer(renderer: *renderer) v
// Opaque value for the alpha channel (255).
export def ALPHA_OPAQUE: u8 = 255;
+@symbol("SDL_SetRenderDrawColor") fn _set_render_draw_color(renderer: *renderer,
+ r: u8, g: u8, b: u8, a: u8) int;
+
// Set the color used for drawing operations (Rect, Line and Clear).
//
// 'renderer' is the renderer for which drawing color should be set. 'r', 'g',
// 'b', and 'a' respectively set the red, gree, blue, and alpha channels.
-//
-// Returns 0 on success, or -1 on error
-export @symbol("SDL_SetRenderDrawColor") fn set_render_draw_color(renderer: *renderer,
- r: u8, g: u8, b: u8, a: u8) int;
+export fn set_render_draw_color(
+ renderer: *renderer,
+ r: u8, g: u8, b: u8, a: u8,
+) (void | error) = wrapvoid(_set_render_draw_color(renderer, r, g, b, a));
+
+@symbol("SDL_RenderClear") fn _render_clear(renderer: *renderer) int;
// Clear the current rendering target with the drawing color
//
// This function clears the entire rendering target, ignoring the viewport and
// the clip rectangle.
-//
-// Returns 0 on success, or -1 on error
-export @symbol("SDL_RenderClear") fn render_clear(renderer: *renderer) int;
+export fn render_clear(renderer: *renderer) (void | error) = {
+ return wrapvoid(_render_clear(renderer));
+};
// Update the screen with rendering performed.
export @symbol("SDL_RenderPresent") fn render_present(renderer: *renderer) void;
@@ -72,13 +89,23 @@ export @symbol("SDL_RenderPresent") fn render_present(renderer: *renderer) void;
// Destroy the specified texture.
export @symbol("SDL_DestroyTexture") fn destroy_texture(texture: *texture) void;
-// Query the attributes of a texture
-//
-// Returns 0 on success, or -1 if the texture is not valid.
-export @symbol("SDL_QueryTexture") fn query_texture(texture: *texture,
+@symbol("SDL_QueryTexture") fn _query_texture(texture: *texture,
format: nullable *u32, access: nullable *int,
w: nullable *int, h: nullable *int) int;
+// Query the attributes of a texture
+export fn query_texture(
+ texture: *texture,
+ format: nullable *u32,
+ access: nullable *int,
+ w: nullable *int, h: nullable *int,
+) (void | error) = {
+ return wrapvoid(_query_texture(texture, format, access, w, h));
+};
+
+@symbol("SDL_SetTextureColorMod") fn _set_texture_color_mod(
+ texture: *texture, r: u8, g: u8, b: u8) int;
+
// Set an additional color value multiplied into render copy operations.
//
// When this texture is rendered, during the copy operation each source color
@@ -89,13 +116,22 @@ export @symbol("SDL_QueryTexture") fn query_texture(texture: *texture,
//
// Color modulation is not always supported by the renderer; it will return -1
// if color modulation is not supported.
-//
-// Returns 0 on success or a negative error code on failure.
-export @symbol("SDL_SetTextureColorMod") fn set_texture_color_mod(
- texture: *texture, r: u8, g: u8, b: u8) int;
+export fn set_texture_color_mod(
+ texture: *texture,
+ r: u8, g: u8, b: u8,
+) (void | error) = {
+ return wrapvoid(_set_texture_color_mod(texture, r, g, b));
+};
-// Copy a portion of the texture to the current rendering target.
-//
-// Returns 0 on success, or -1 on error
-export @symbol("SDL_RenderCopy") fn render_copy(renderer: *renderer,
+@symbol("SDL_RenderCopy") fn _render_copy(renderer: *renderer,
texture: *texture, srcrect: nullable *rect, dstrect: nullable *rect) int;
+
+// Copy a portion of the texture to the current rendering target.
+export fn render_copy(
+ renderer: *renderer,
+ texture: *texture,
+ srcrect: nullable *rect,
+ dstrect: nullable *rect,
+) (void | error) = {
+ return wrapvoid(_render_copy(renderer, texture, srcrect, dstrect));
+};
diff --git a/sdl2/video.ha b/sdl2/video.ha
index e6dfa58..b1f8b22 100644
--- a/sdl2/video.ha
+++ b/sdl2/video.ha
@@ -1,4 +1,5 @@
// TODO: Flesh me out
+use strings;
// The type used to identify a window. (Opaque)
export type window = void;
@@ -32,6 +33,9 @@ export type window_flags = enum u32 {
export def WINDOWPOS_UNDEFINED: int = 0x1FFF0000;
export def WINDOWPOS_CENTERED: int = 0x2FFF0000;
+@symbol("SDL_CreateWindow") fn _create_window(title: const *char,
+ x: int, y: int, w: int, h: int, flags: window_flags) nullable *window;
+
// Create a window with the specified position, dimensions, and flags.
//
// 'title' is the title of the window, in UTF-8 encoding. See [[strings::to_c]]
@@ -66,8 +70,18 @@ export def WINDOWPOS_CENTERED: int = 0x2FFF0000;
// removed in a future version of SDL.
//
// See also: [[destroy_window]] [[gl_loadlibrary]], [[vulkan_loadlibrary]].
-export @symbol("SDL_CreateWindow") fn create_window(title: const *char,
- x: int, y: int, w: int, h: int, flags: window_flags) nullable *window;
+export fn create_window(
+ title: str,
+ x: int,
+ y: int,
+ w: int,
+ h: int,
+ flags: window_flags,
+) (*window | error) = {
+ let title = strings::to_c(title);
+ defer free(title);
+ return wrapptr(_create_window(title, x, y, w, h, flags))?: *window;
+};
// Destroy a window.
export @symbol("SDL_DestroyWindow") fn destroy_window(window: *window) void;