diff options
| author | Drew DeVault <sir@cmpwn.com> | 2021-12-10 09:42:10 +0100 |
|---|---|---|
| committer | Drew DeVault <sir@cmpwn.com> | 2021-12-10 09:44:50 +0100 |
| commit | 9607eb0de784fab1a99833f567c0eaca72b67e4a (patch) | |
| tree | 7f79a0914a6db1d963d877bed051f6856c54cfa8 | |
| parent | Makefile: drop -T+libc (diff) | |
| download | hare-chip8-9607eb0de784fab1a99833f567c0eaca72b67e4a.tar.gz | |
all: rig up Hare-native error handling
| -rw-r--r-- | cmd/demo/main.ha | 77 | ||||
| -rw-r--r-- | sdl2/errors.ha | 31 | ||||
| -rw-r--r-- | sdl2/events.ha | 51 | ||||
| -rw-r--r-- | sdl2/gamecontroller.ha | 16 | ||||
| -rw-r--r-- | sdl2/image/image.ha | 22 | ||||
| -rw-r--r-- | sdl2/joystick.ha | 9 | ||||
| -rw-r--r-- | sdl2/render.ha | 92 | ||||
| -rw-r--r-- | sdl2/video.ha | 18 |
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; |
