From 01bb53f01d5d8b732bd80a84f25b661e1452faeb Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 9 Dec 2021 13:06:45 +0100 Subject: Rig up events, partially rig up video --- Makefile | 4 +- cmd/demo/main.ha | 29 +++- sdl2/events.ha | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++ sdl2/prototypes.ha | 21 --- sdl2/sdl2.ha | 21 +++ sdl2/video.ha | 73 +++++++++ 6 files changed, 571 insertions(+), 25 deletions(-) create mode 100644 sdl2/events.ha delete mode 100644 sdl2/prototypes.ha create mode 100644 sdl2/sdl2.ha create mode 100644 sdl2/video.ha diff --git a/Makefile b/Makefile index dd2d9dd..fa692a8 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ demo: hare build -lSDL2 -lc -T+libc cmd/demo -run: demo - ./demo +run: + hare run -lSDL2 -lc -T+libc cmd/demo .PHONY: demo run diff --git a/cmd/demo/main.ha b/cmd/demo/main.ha index 1a8e362..1c780fd 100644 --- a/cmd/demo/main.ha +++ b/cmd/demo/main.ha @@ -1,8 +1,33 @@ -use sdl2; +use fmt; use os; +use sdl2::{event_type, window_flags}; +use sdl2; +use strings; export fn main() void = { sdl2::init(sdl2::init_flags::VIDEO); defer sdl2::quit(); - os::exit(0); + + const title = strings::to_c("Hare SDL2 demo"); + defer free(title); + const win = match (sdl2::create_window(title, + 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"); + }; + defer sdl2::destroy_window(win); + + let ev = sdl2::event { ... }; + for (sdl2::wait_event(&ev) == 1) { + switch (ev.event_type) { + case event_type::QUIT => + break; + case => void; + }; + }; + + os::exit(0); // https://todo.sr.ht/~sircmpwn/hare/525 }; diff --git a/sdl2/events.ha b/sdl2/events.ha new file mode 100644 index 0000000..60ac771 --- /dev/null +++ b/sdl2/events.ha @@ -0,0 +1,448 @@ +export type event_type = enum u32 { + FIRSTEVENT = 0, + + QUIT = 0x100, + + APP_TERMINATING, + APP_LOWMEMORY, + APP_WILLENTERBACKGROUND, + APP_DIDENTERBACKGROUND, + APP_WILLENTERFOREGROUND, + APP_DIDENTERFOREGROUND, + + 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, + + CLIPBOARDUPDATE = 0x900, + + DROPFILE = 0x1000, + DROPTEXT, + DROPBEGIN, + DROPCOMPLETE, + + AUDIODEVICEADDED = 0x1100, + AUDIODEVICEREMOVED, + + SENSORUPDATE = 0x1200, + + RENDER_TARGETS_RESET = 0x2000, + RENDER_DEVICE_RESET, + + USEREVENT = 0x8000, + + LASTEVENT = 0xFFFF +}; + +// Fields shared by every event +export type common_event = struct { + event_type: event_type, + timestamp: u32, +}; + +// Display state change event data (event.display.*) +export type display_event = struct { + common_event, + display: u32, + event: u8, + padding1: u8, + padding2: u8, + padding3: u8, + data1: i32, +}; + +// Window state change event data (event.window.*) +export type window_event = struct { + common_event, + window_id: u32, + event: u8, + padding1: u8, + padding2: u8, + padding3: u8, + data1: i32, + data2: i32, +}; + +// Keyboard button event structure (event.key.*) +export type keyboard_event = struct { + common_event, + window_id: u32, + state: u8, + repeat: u8, + padding2: u8, + padding3: u8, + keysym: uint, // TODO: Rig me up with the keysym type +}; + +// Size of the [[text_editing_event]] 'text' field. +export def TEXTEDITINGEVENT_TEXT_SIZE: size = 32; + +// Keyboard text editing event structure (event.edit.*) +export type text_editing_event = struct { + common_event, + window_id: u32, + text: [TEXTEDITINGEVENT_TEXT_SIZE]char, + start: i32, + length: i32, +}; + +// Size of the [[text_input_event]] 'text' field. +export def TEXTINPUTEVENT_TEXT_SIZE: size = 32; + +// Keyboard text input event structure (event.text.*) +export type text_input_event = struct { + common_event, + window_id: u32, + text: [TEXTINPUTEVENT_TEXT_SIZE]char, +}; + +// Mouse motion event structure (event.motion.*) +export type mouse_motion_event = struct { + common_event, + window_id: u32, + which: u32, + state: u32, + x: i32, + y: i32, + xrel: i32, + yrel: i32, +}; + +// Mouse button event structure (event.button.*) +export type mouse_button_event = struct { + common_event, + window_id: u32, + which: u32, + button: u8, + state: u8, + clicks: u8, + padding1: u8, + x: i32, + y: i32, +}; + +// Mouse wheel event structure (event.wheel.*) +export type mouse_wheel_event = struct { + common_event, + window_id: u32, + which: u32, + x: i32, + y: i32, + direction: u32, +}; + +// Joystick axis motion event structure (event.jaxis.*) +export type joy_axis_event = struct { + common_event, + which: i32, // TODO: Rig me up with the JoystickID type + axis: u8, + padding1: u8, + padding2: u8, + padding3: u8, + value: i16, + padding4: u16, +}; + +// Joystick trackball motion event structure (event.jball.*) +export type joy_ball_event = struct { + common_event, + which: i32, // TODO: Rig me up with the JoystickID type + ball: u8, + padding1: u8, + padding2: u8, + padding3: u8, + xrel: i16, + yrel: i16, +}; + +// Joystick hat position change event structure (event.jhat.*) +export type joy_hat_event = struct { + common_event, + which: i32, // TODO: Rig me up with the JoystickID type + hat: u8, + value: u8, // TODO: Rig me up with HAT_UP et al + padding1: u8, + padding2: u8, +}; + +// Joystick button event structure (event.jbutton.*) +export type joy_button_event = struct { + common_event, + which: i32, // TODO: Rig me up with the JoystickID type + button: u8, + state: u8, + padding1: u8, + padding2: u8, +}; + +// Joystick device event structure (event.jdevice.*) +export type joy_device_event = struct { + common_event, + which: i32, +}; + +// Game controller axis motion event structure (event.caxis.*) +export type controller_axis_event = struct { + common_event, + which: i32, // TODO + axis: u8, + padding1: u8, + padding2: u8, + padding3: u8, + value: i16, + padding4: u16, +}; + +// Game controller button event structure (event.cbutton.*) +export type controller_button_event = struct { + common_event, + which: i32, + button: u8, + state: u8, + padding1: u8, + padding2: u8, +}; + +// Controller device event structure (event.cdevice.*) +export type controller_device_event = struct { + common_event, + which: i32, +}; + +// Audio device event structure (event.adevice.*) +export type audio_device_event = struct { + common_event, + which: u32, + iscapture: u8, + padding1: u8, + padding2: u8, + padding3: u8, +}; + +// Touch finger event structure (event.tfinger.*) +export type touch_finger_event = struct { + common_event, + touch_id: i64, // TODO + finger_id: i64, // TODO + x: f32, + y: f32, + dx: f32, + dy: f32, + pressure: f32, +}; + +// Multiple Finger Gesture Event (event.mgesture.*) +export type multi_gesture_event = struct { + common_event, + touch_id: i64, // TODO + dtheta: f32, + ddist: f32, + x: f32, + y: f32, + num_fingers: u16, + padding: u16, +}; + +// Dollar Gesture Event (event.dgesture.*) +export type dollar_gesture_event = struct { + common_event, + touch_id: i64, // TODO + gesture_id: i64, // TODO + num_fingers: u32, + error: f32, + x: f32, + y: f32, +}; + +// An event used to request a file open by the system (event.drop.*) +// This event is enabled by default, you can disable it with [[eventstate]]. +// If this event is enabled, you must free the filename in the event. +export type drop_event = struct { + common_event, + file: *char, + window_id: u32, +}; + +// Sensor event structure (event.sensor.*) +export type sensor_event = struct { + common_event, + which: i32, + data: [6]f32, +}; + +// The "quit requested" event +export type quit_event = struct { + common_event, +}; + +// OS Specific event +export type os_event = struct { + common_event, +}; + +// A user-defined event type (event.user.*) +export type user_event = struct { + common_event, + window_id: u32, + code: i32, + data1: *void, + data2: *void, +}; + +// A video driver dependent system event (event.syswm.*) +// This event is disabled by default, you can enable it with [[eventstate]]. +export type syswm_event = struct { + common_event, + msg: *void, // TODO +}; + +// General event structure +export type event = union { + event_type: event_type, + common: common_event, + display: display_event, + window: window_event, + key: keyboard_event, + edit: text_editing_event, + text: text_input_event, + motion: mouse_motion_event, + button: mouse_button_event, + wheel: mouse_wheel_event, + jaxis: joy_axis_event, + jball: joy_ball_event, + jhat: joy_hat_event, + jbutton: joy_button_event, + jdevice: joy_device_event, + caxis: controller_axis_event, + cbutton: controller_button_event, + cdevice: controller_device_event, + adevice: audio_device_event, + sensor: sensor_event, + quit: quit_event, + user: user_event, + syswm: syswm_event, + tfinger: touch_finger_event, + mgesture: multi_gesture_event, + dgestures: dollar_gesture_event, + drop: drop_event, + + padding: [56]u8, +}; + +// Pumps the event loop, gathering events from the input devices. +// +// This function updates the event queue and internal input device state. +// +// This should only be run in the thread that sets the video mode. +export @symbol("SDL_PumpEvents") fn pump_events() void; + +export type eventaction = enum { + ADDEVENT, + PEEKEVENT, + GETEVENT, +}; + +// Checks the event queue for messages and optionally returns them. +// +// If 'action' is ADDEVENT, up to 'numevents' events will be added to the back +// of the event queue. +// +// If 'action' is PEEKEVENT, up to 'numevents' events at the front of the event +// queue, within the specified minimum and maximum type, will be returned and +// will not be removed from the queue. +// +// If 'action' is GETEVENT, up to 'numevents' events at the front of the event +// 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; + +// Checks to see if certain event types are in the event queue. +export @symbol("SDL_HasEvent") fn has_event(event_type: event_type) bool; + +// Checks to see if certain event types are in the event queue. +export @symbol("SDL_HasEvents") fn has_events(mintype: event_type, maxtype: event_type) bool; + +// This function clears events from the event queue +// This function only affects currently queued events. If you want to make +// sure that all pending OS events are flushed, you can call SDL_PumpEvents() +// on the main thread immediately before the flush call. +export @symbol("SDL_FlushEvent") fn flush_event(event_type: event_type) void; + +// This function clears events from the event queue +// This function only affects currently queued events. If you want to make +// sure that all pending OS events are flushed, you can call SDL_PumpEvents() +// on the main thread immediately before the flush call. +export @symbol("SDL_FlushEvents") fn flush_events(mintype: event_type, maxtype: event_type) void; + +// 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; + +// 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; + +// 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; + +// 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; + +// TODO: Finish rigging up other SDL_events.h bits diff --git a/sdl2/prototypes.ha b/sdl2/prototypes.ha deleted file mode 100644 index b71c890..0000000 --- a/sdl2/prototypes.ha +++ /dev/null @@ -1,21 +0,0 @@ -// These are the flags which may be passed to [[init]]. You should specify the -// subsystems which you will be using in your application. -export type init_flags = enum uint { - TIMER = 0x00000001u, - AUDIO = 0x00000010u, - VIDEO = 0x00000020u, - JOYSTICK = 0x00000200u, - HAPTIC = 0x00001000u, - GAMECONTROLLER = 0x00002000u, - EVENTS = 0x00004000u, - SENSOR = 0x00008000u, - NOPARACHUTE = 0x00100000u, - EVERYTHING = TIMER | AUDIO | VIDEO | EVENTS | JOYSTICK | HAPTIC | GAMECONTROLLER | SENSOR, -}; - -// This function initializes the subsystems specified by 'flags'. -export @symbol("SDL_Init") fn init(flags: init_flags) void; - -// This function cleans up all initialized subsystems. You should call it upon -// all exit conditions. -export @symbol("SDL_Quit") fn quit() void; diff --git a/sdl2/sdl2.ha b/sdl2/sdl2.ha new file mode 100644 index 0000000..b71c890 --- /dev/null +++ b/sdl2/sdl2.ha @@ -0,0 +1,21 @@ +// These are the flags which may be passed to [[init]]. You should specify the +// subsystems which you will be using in your application. +export type init_flags = enum uint { + TIMER = 0x00000001u, + AUDIO = 0x00000010u, + VIDEO = 0x00000020u, + JOYSTICK = 0x00000200u, + HAPTIC = 0x00001000u, + GAMECONTROLLER = 0x00002000u, + EVENTS = 0x00004000u, + SENSOR = 0x00008000u, + NOPARACHUTE = 0x00100000u, + EVERYTHING = TIMER | AUDIO | VIDEO | EVENTS | JOYSTICK | HAPTIC | GAMECONTROLLER | SENSOR, +}; + +// This function initializes the subsystems specified by 'flags'. +export @symbol("SDL_Init") fn init(flags: init_flags) void; + +// This function cleans up all initialized subsystems. You should call it upon +// all exit conditions. +export @symbol("SDL_Quit") fn quit() void; diff --git a/sdl2/video.ha b/sdl2/video.ha new file mode 100644 index 0000000..e12ca12 --- /dev/null +++ b/sdl2/video.ha @@ -0,0 +1,73 @@ +// TODO: Flesh me out + +// The type used to identify a window. (Opaque) +export type window = void; + +// The flags on a window +export type window_flags = enum u32 { + NONE = 0, + FULLSCREEN = 0x00000001, + OPENGL = 0x00000002, + SHOWN = 0x00000004, + HIDDEN = 0x00000008, + BORDERLESS = 0x00000010, + RESIZABLE = 0x00000020, + MINIMIZED = 0x00000040, + MAXIMIZED = 0x00000080, + INPUT_GRABBED = 0x00000100, + INPUT_FOCUS = 0x00000200, + MOUSE_FOCUS = 0x00000400, + FULLSCREEN_DESKTOP = 0x00001001, + FOREIGN = 0x00000800, + ALLOW_HIGHDPI = 0x00002000, + MOUSE_CAPTURE = 0x00004000, + ALWAYS_ON_TOP = 0x00008000, + SKIP_TASKBAR = 0x00010000, + UTILITY = 0x00020000, + TOOLTIP = 0x00040000, + POPUP_MENU = 0x00080000, + VULKAN = 0x10000000 +}; + +export def WINDOWPOS_UNDEFINED: int = 0x1FFF0000; +export def WINDOWPOS_CENTERED: int = 0x2FFF0000; + +// 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]] +// to prepare a suitable string. +// +// 'x' and 'y' set the position of the window, or use [[WINDOWPOS_CENTERED]] or +// [[WINDOWPOS_UNDEFINED]]. +// +// 'w' and 'h' set the width and height of the window, in screen coordinates. +// +// 'flags' configure additional window parameters. +// +// Returns the created window, or null if window creation failed. +// +// If the window is created with the SDL_WINDOW_ALLOW_HIGHDPI flag, its size +// in pixels may differ from its size in screen coordinates on platforms with +// high-DPI support (e.g. iOS and Mac OS X). Use SDL_GetWindowSize() to query +// the client area's size in screen coordinates, and SDL_GL_GetDrawableSize(), +// SDL_Vulkan_GetDrawableSize(), or SDL_GetRendererOutputSize() to query the +// drawable size in pixels. +// +// If the window is created with any of the SDL_WINDOW_OPENGL or +// SDL_WINDOW_VULKAN flags, then the corresponding LoadLibrary function +// (SDL_GL_LoadLibrary or SDL_Vulkan_LoadLibrary) is called and the +// corresponding UnloadLibrary function is called by SDL_DestroyWindow(). +// +// If SDL_WINDOW_VULKAN is specified and there isn't a working Vulkan driver, +// SDL_CreateWindow() will fail because SDL_Vulkan_LoadLibrary() will fail. +// +// Note: On non-Apple devices, SDL requires you to either not link to the +// Vulkan loader or link to a dynamic library version. This limitation may be +// 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; + +// Destroy a window. +export @symbol("SDL_DestroyWindow") fn destroy_window(window: *window) void; -- cgit v1.2.3