spaces and LF please
This commit is contained in:
1 parent
a2671bed5d
commit
cd6332fe0a
11 files changed
+3616
-3616
No files matched your search
+79
-79
@@ -1,80 +1,80 @@
|
|||||||
// audio.h
|
// audio.h
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef AUDIO_H
|
#ifndef AUDIO_H
|
||||||
#define AUDIO_H
|
#define AUDIO_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#else
|
#else
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct audio_t audio_t;
|
typedef struct audio_t audio_t;
|
||||||
typedef uint32_t audio_stream_id_t;
|
typedef uint32_t audio_stream_id_t;
|
||||||
|
|
||||||
// If the return type is bool, false means failure.
|
// If the return type is bool, false means failure.
|
||||||
|
|
||||||
// Initialize audio system
|
// Initialize audio system
|
||||||
// nullptr means failure
|
// nullptr means failure
|
||||||
audio_t* audio_init(void);
|
audio_t* audio_init(void);
|
||||||
|
|
||||||
// Destroy audio system and free resources
|
// Destroy audio system and free resources
|
||||||
void audio_destroy(audio_t* audio);
|
void audio_destroy(audio_t* audio);
|
||||||
|
|
||||||
// Load MADPCM data from a WAV file.
|
// Load MADPCM data from a WAV file.
|
||||||
// Returns a pointer to the allocated audio data, or nullptr on failure.
|
// Returns a pointer to the allocated audio data, or nullptr on failure.
|
||||||
// The returned buffer must be freed with audio_free_data().
|
// The returned buffer must be freed with audio_free_data().
|
||||||
uint8_t* audio_load_madpcm(const char* filename, int* out_size, int* out_channels, int* out_rate, int* out_total_samples);
|
uint8_t* audio_load_madpcm(const char* filename, int* out_size, int* out_channels, int* out_rate, int* out_total_samples);
|
||||||
|
|
||||||
// Free data allocated by audio_load_madpcm
|
// Free data allocated by audio_load_madpcm
|
||||||
void audio_free_data(uint8_t* data);
|
void audio_free_data(uint8_t* data);
|
||||||
|
|
||||||
// Play a MADPCM stream, returns unique ID
|
// Play a MADPCM stream, returns unique ID
|
||||||
// data must remain valid until the stream ends or is stopped
|
// data must remain valid until the stream ends or is stopped
|
||||||
// loop: if true, the stream restarts on EOF
|
// loop: if true, the stream restarts on EOF
|
||||||
// 0 means failure, valid IDs start from 1
|
// 0 means failure, valid IDs start from 1
|
||||||
audio_stream_id_t audio_play(audio_t* audio,
|
audio_stream_id_t audio_play(audio_t* audio,
|
||||||
const uint8_t* data,
|
const uint8_t* data,
|
||||||
int size,
|
int size,
|
||||||
int channels,
|
int channels,
|
||||||
int rate,
|
int rate,
|
||||||
bool loop);
|
bool loop);
|
||||||
|
|
||||||
// Stop and remove stream by ID
|
// Stop and remove stream by ID
|
||||||
bool audio_stop(audio_t* audio, audio_stream_id_t id);
|
bool audio_stop(audio_t* audio, audio_stream_id_t id);
|
||||||
|
|
||||||
bool audio_pause(audio_t* audio, audio_stream_id_t id);
|
bool audio_pause(audio_t* audio, audio_stream_id_t id);
|
||||||
bool audio_resume(audio_t* audio, audio_stream_id_t id);
|
bool audio_resume(audio_t* audio, audio_stream_id_t id);
|
||||||
|
|
||||||
// Returns true if the stream is active (playing or paused)
|
// Returns true if the stream is active (playing or paused)
|
||||||
bool audio_is_active(audio_t* audio, audio_stream_id_t id);
|
bool audio_is_active(audio_t* audio, audio_stream_id_t id);
|
||||||
|
|
||||||
// Returns true if the stream is currently playing (not paused, not stopped)
|
// Returns true if the stream is currently playing (not paused, not stopped)
|
||||||
bool audio_is_playing(audio_t* audio, audio_stream_id_t id);
|
bool audio_is_playing(audio_t* audio, audio_stream_id_t id);
|
||||||
|
|
||||||
// Per-stream volume, immediate effect, range [0.0, 1.0]
|
// Per-stream volume, immediate effect, range [0.0, 1.0]
|
||||||
bool audio_set_volume(audio_t* audio, audio_stream_id_t id, float volume);
|
bool audio_set_volume(audio_t* audio, audio_stream_id_t id, float volume);
|
||||||
|
|
||||||
// Animate per-stream volume linearly over time, driven by audio_tick()
|
// Animate per-stream volume linearly over time, driven by audio_tick()
|
||||||
bool audio_animate_volume(audio_t* audio, audio_stream_id_t id,
|
bool audio_animate_volume(audio_t* audio, audio_stream_id_t id,
|
||||||
float to_volume, float duration_sec);
|
float to_volume, float duration_sec);
|
||||||
|
|
||||||
// Global volume, immediate effect, range [0.0, 1.0]
|
// Global volume, immediate effect, range [0.0, 1.0]
|
||||||
// Applied on top of per-stream volume at mix time
|
// Applied on top of per-stream volume at mix time
|
||||||
void audio_set_global_volume(audio_t* audio, float volume);
|
void audio_set_global_volume(audio_t* audio, float volume);
|
||||||
|
|
||||||
// Animate global volume linearly over time, driven by audio_tick()
|
// Animate global volume linearly over time, driven by audio_tick()
|
||||||
void audio_animate_global_volume(audio_t* audio,
|
void audio_animate_global_volume(audio_t* audio,
|
||||||
float to_volume, float duration_sec);
|
float to_volume, float duration_sec);
|
||||||
|
|
||||||
// Advances stream decoding and volume animations
|
// Advances stream decoding and volume animations
|
||||||
// Call once per main loop iteration with the same delta as compositor_tick()
|
// Call once per main loop iteration with the same delta as compositor_tick()
|
||||||
void audio_tick(audio_t* audio, float delta_time_sec);
|
void audio_tick(audio_t* audio, float delta_time_sec);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // AUDIO_H
|
#endif // AUDIO_H
|
||||||
+119
-119
@@ -1,120 +1,120 @@
|
|||||||
// compositor.h v0.3
|
// compositor.h v0.3
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef COMPOSITOR_H
|
#ifndef COMPOSITOR_H
|
||||||
#define COMPOSITOR_H
|
#define COMPOSITOR_H
|
||||||
|
|
||||||
// This subsystem is thread-unsafe.
|
// This subsystem is thread-unsafe.
|
||||||
// Caller must serialize all calls. Calls do not need to be made from the main thread.
|
// Caller must serialize all calls. Calls do not need to be made from the main thread.
|
||||||
// Removing an element while ticking/rendering may crash.
|
// Removing an element while ticking/rendering may crash.
|
||||||
// Modifying an element while ticking/rendering may cause visual glitches.
|
// Modifying an element while ticking/rendering may cause visual glitches.
|
||||||
|
|
||||||
// Easing functions
|
// Easing functions
|
||||||
enum easing_t {
|
enum easing_t {
|
||||||
EASE_LINEAR = 0,
|
EASE_LINEAR = 0,
|
||||||
EASE_IN_QUAD,
|
EASE_IN_QUAD,
|
||||||
EASE_OUT_QUAD,
|
EASE_OUT_QUAD,
|
||||||
EASE_IN_OUT_QUAD,
|
EASE_IN_OUT_QUAD,
|
||||||
EASE_IN_CUBIC,
|
EASE_IN_CUBIC,
|
||||||
EASE_OUT_CUBIC,
|
EASE_OUT_CUBIC,
|
||||||
EASE_IN_OUT_CUBIC
|
EASE_IN_OUT_CUBIC
|
||||||
};
|
};
|
||||||
|
|
||||||
// Anchor points
|
// Anchor points
|
||||||
enum anchor_point_t {
|
enum anchor_point_t {
|
||||||
ANCHOR_TOP_LEFT,
|
ANCHOR_TOP_LEFT,
|
||||||
ANCHOR_TOP_CENTER,
|
ANCHOR_TOP_CENTER,
|
||||||
ANCHOR_TOP_RIGHT,
|
ANCHOR_TOP_RIGHT,
|
||||||
ANCHOR_CENTER_LEFT,
|
ANCHOR_CENTER_LEFT,
|
||||||
ANCHOR_CENTER,
|
ANCHOR_CENTER,
|
||||||
ANCHOR_CENTER_RIGHT,
|
ANCHOR_CENTER_RIGHT,
|
||||||
ANCHOR_BOTTOM_LEFT,
|
ANCHOR_BOTTOM_LEFT,
|
||||||
ANCHOR_BOTTOM_CENTER,
|
ANCHOR_BOTTOM_CENTER,
|
||||||
ANCHOR_BOTTOM_RIGHT,
|
ANCHOR_BOTTOM_RIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#else
|
#else
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct compositor_t compositor_t;
|
typedef struct compositor_t compositor_t;
|
||||||
typedef uint32_t element_handle_t;
|
typedef uint32_t element_handle_t;
|
||||||
|
|
||||||
// If the return type is bool, false means failure.
|
// If the return type is bool, false means failure.
|
||||||
|
|
||||||
// Initialize compositor with given dimensions
|
// Initialize compositor with given dimensions
|
||||||
// nullptr means failure
|
// nullptr means failure
|
||||||
compositor_t* compositor_init(uint32_t width, uint32_t height);
|
compositor_t* compositor_init(uint32_t width, uint32_t height);
|
||||||
|
|
||||||
// Destroy compositor and free resources. Everything dies with it.
|
// Destroy compositor and free resources. Everything dies with it.
|
||||||
void compositor_destroy(compositor_t* comp);
|
void compositor_destroy(compositor_t* comp);
|
||||||
|
|
||||||
// Insert element, returns unique ID
|
// Insert element, returns unique ID
|
||||||
// FB format is ARGB8888 and it always will be
|
// FB format is ARGB8888 and it always will be
|
||||||
// The compositor takes a reference, caller must keep alive until removed
|
// The compositor takes a reference, caller must keep alive until removed
|
||||||
// 0 means failure, valid IDs start from 1
|
// 0 means failure, valid IDs start from 1
|
||||||
element_handle_t compositor_insert(compositor_t* comp,
|
element_handle_t compositor_insert(compositor_t* comp,
|
||||||
const uint32_t* fb,
|
const uint32_t* fb,
|
||||||
uint32_t fb_width,
|
uint32_t fb_width,
|
||||||
uint32_t fb_height,
|
uint32_t fb_height,
|
||||||
int32_t x, int32_t y,
|
int32_t x, int32_t y,
|
||||||
int32_t z_order, float scale,
|
int32_t z_order, float scale,
|
||||||
anchor_point_t anchor);
|
anchor_point_t anchor);
|
||||||
|
|
||||||
// Remove element by ID
|
// Remove element by ID
|
||||||
bool compositor_remove(compositor_t* comp, element_handle_t id);
|
bool compositor_remove(compositor_t* comp, element_handle_t id);
|
||||||
|
|
||||||
// Setters, immediate effect
|
// Setters, immediate effect
|
||||||
bool compositor_set_position(compositor_t* comp, element_handle_t id,
|
bool compositor_set_position(compositor_t* comp, element_handle_t id,
|
||||||
int32_t x, int32_t y);
|
int32_t x, int32_t y);
|
||||||
bool compositor_set_zorder(compositor_t* comp, element_handle_t id,
|
bool compositor_set_zorder(compositor_t* comp, element_handle_t id,
|
||||||
int32_t z_order);
|
int32_t z_order);
|
||||||
bool compositor_set_scale(compositor_t* comp, element_handle_t id,
|
bool compositor_set_scale(compositor_t* comp, element_handle_t id,
|
||||||
float scale);
|
float scale);
|
||||||
bool compositor_set_visible(compositor_t* comp, element_handle_t id,
|
bool compositor_set_visible(compositor_t* comp, element_handle_t id,
|
||||||
bool visible);
|
bool visible);
|
||||||
bool compositor_set_alpha(compositor_t* comp, element_handle_t id,
|
bool compositor_set_alpha(compositor_t* comp, element_handle_t id,
|
||||||
float alpha);
|
float alpha);
|
||||||
|
|
||||||
// Advances all animations by a given time delta.
|
// Advances all animations by a given time delta.
|
||||||
void compositor_tick(compositor_t* comp, float delta_time_sec);
|
void compositor_tick(compositor_t* comp, float delta_time_sec);
|
||||||
|
|
||||||
// Sets a target state to advance over time by tick()
|
// Sets a target state to advance over time by tick()
|
||||||
// Queue means the animation will start after the previous animation completed
|
// Queue means the animation will start after the previous animation completed
|
||||||
// Not queueing will play the animation alongside any current animation
|
// Not queueing will play the animation alongside any current animation
|
||||||
// You cannot stack the same type of animation without queueing, the second call will cancel the first call.
|
// You cannot stack the same type of animation without queueing, the second call will cancel the first call.
|
||||||
bool compositor_animate_move(compositor_t* comp, element_handle_t id,
|
bool compositor_animate_move(compositor_t* comp, element_handle_t id,
|
||||||
int32_t to_x, int32_t to_y,
|
int32_t to_x, int32_t to_y,
|
||||||
float duration_sec, easing_t easing, bool queue, bool parallel);
|
float duration_sec, easing_t easing, bool queue, bool parallel);
|
||||||
bool compositor_animate_fade(compositor_t* comp, element_handle_t id,
|
bool compositor_animate_fade(compositor_t* comp, element_handle_t id,
|
||||||
float to_alpha,
|
float to_alpha,
|
||||||
float duration_sec, easing_t easing, bool queue, bool parallel);
|
float duration_sec, easing_t easing, bool queue, bool parallel);
|
||||||
bool compositor_animate_scale(compositor_t* comp, element_handle_t id,
|
bool compositor_animate_scale(compositor_t* comp, element_handle_t id,
|
||||||
float to_scale,
|
float to_scale,
|
||||||
float duration_sec, easing_t easing, bool queue, bool parallel);
|
float duration_sec, easing_t easing, bool queue, bool parallel);
|
||||||
|
|
||||||
// Immediately finish all animations on a specific object (optionally only N queued, 0 = all), or every object.
|
// Immediately finish all animations on a specific object (optionally only N queued, 0 = all), or every object.
|
||||||
bool compositor_animation_finish_element(compositor_t* comp, element_handle_t id, int num_queued /*0=all*/);
|
bool compositor_animation_finish_element(compositor_t* comp, element_handle_t id, int num_queued /*0=all*/);
|
||||||
void compositor_animation_finish_all(compositor_t* comp);
|
void compositor_animation_finish_all(compositor_t* comp);
|
||||||
|
|
||||||
// Returns true if any element is animating or has queued animations.
|
// Returns true if any element is animating or has queued animations.
|
||||||
bool compositor_is_animating(compositor_t* comp);
|
bool compositor_is_animating(compositor_t* comp);
|
||||||
|
|
||||||
// Returns true if the specified element is animating or has queued animations.
|
// Returns true if the specified element is animating or has queued animations.
|
||||||
bool compositor_is_element_animating(compositor_t* comp, element_handle_t id);
|
bool compositor_is_element_animating(compositor_t* comp, element_handle_t id);
|
||||||
|
|
||||||
// Compose all visible elements and return framebuffer pointer
|
// Compose all visible elements and return framebuffer pointer
|
||||||
const uint32_t* compositor_compose(compositor_t* comp);
|
const uint32_t* compositor_compose(compositor_t* comp);
|
||||||
|
|
||||||
// Mark an element's content as dirty for redrawing
|
// Mark an element's content as dirty for redrawing
|
||||||
bool compositor_mark_content_dirty(compositor_t* comp, element_handle_t id);
|
bool compositor_mark_content_dirty(compositor_t* comp, element_handle_t id);
|
||||||
void compositor_mark_all_dirty(compositor_t* comp);
|
void compositor_mark_all_dirty(compositor_t* comp);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // COMPOSITOR_H
|
#endif // COMPOSITOR_H
|
||||||
+24
-24
@@ -1,24 +1,24 @@
|
|||||||
// image.h
|
// image.h
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef IMAGE_H
|
#ifndef IMAGE_H
|
||||||
#define IMAGE_H
|
#define IMAGE_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#else
|
#else
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Loads an image file as ARGB8888. Returns pointer to pixel data, sets width/height. NULL on failure.
|
// Loads an image file as ARGB8888. Returns pointer to pixel data, sets width/height. NULL on failure.
|
||||||
uint32_t* image_load(const char* filename, int* width, int* height);
|
uint32_t* image_load(const char* filename, int* width, int* height);
|
||||||
// Loads and resizes an image file to desired dimensions as ARGB8888. Returns pointer to pixel data. NULL on failure.
|
// Loads and resizes an image file to desired dimensions as ARGB8888. Returns pointer to pixel data. NULL on failure.
|
||||||
uint32_t* image_load_resized(const char* filename, int desired_width, int desired_height);
|
uint32_t* image_load_resized(const char* filename, int desired_width, int desired_height);
|
||||||
// Frees image data.
|
// Frees image data.
|
||||||
void image_free(uint32_t* data);
|
void image_free(uint32_t* data);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // IMAGE_H
|
#endif // IMAGE_H
|
||||||
+44
-44
@@ -1,45 +1,45 @@
|
|||||||
// text.h
|
// text.h
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef TEXT_RENDERER_H
|
#ifndef TEXT_RENDERER_H
|
||||||
#define TEXT_RENDERER_H
|
#define TEXT_RENDERER_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct text_t;
|
struct text_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#else
|
#else
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// initializes a new text renderer with given width and height
|
// initializes a new text renderer with given width and height
|
||||||
text_t* text_init(int width, int height);
|
text_t* text_init(int width, int height);
|
||||||
// shares cache, prefer when using the same font to save memory
|
// shares cache, prefer when using the same font to save memory
|
||||||
text_t* text_init_shared(text_t* source, int width, int height);
|
text_t* text_init_shared(text_t* source, int width, int height);
|
||||||
// destroys the text renderer and frees resources
|
// destroys the text renderer and frees resources
|
||||||
void text_destroy(text_t* text);
|
void text_destroy(text_t* text);
|
||||||
// pushes new text buffer to be rendered, can be stacked
|
// pushes new text buffer to be rendered, can be stacked
|
||||||
void text_push(text_t* text,
|
void text_push(text_t* text,
|
||||||
const char* str,
|
const char* str,
|
||||||
float speed,
|
float speed,
|
||||||
bool outline);
|
bool outline);
|
||||||
// load font and set as current font
|
// load font and set as current font
|
||||||
void text_set_font(text_t* text, const char* font_path, float size);
|
void text_set_font(text_t* text, const char* font_path, float size);
|
||||||
// finish whole current text buffer immediately
|
// finish whole current text buffer immediately
|
||||||
void text_show_all_current(text_t* text);
|
void text_show_all_current(text_t* text);
|
||||||
// returns true if text is still animating
|
// returns true if text is still animating
|
||||||
bool text_is_animating(text_t* text);
|
bool text_is_animating(text_t* text);
|
||||||
// advance, call every frame, returns true when it touched the framebuffer
|
// advance, call every frame, returns true when it touched the framebuffer
|
||||||
bool text_nextframe(text_t* text, float deltatime);
|
bool text_nextframe(text_t* text, float deltatime);
|
||||||
// gets the framebuffer of the text renderer
|
// gets the framebuffer of the text renderer
|
||||||
const uint32_t* text_getfb(text_t* text);
|
const uint32_t* text_getfb(text_t* text);
|
||||||
// clears the text buffer and the frame buffer
|
// clears the text buffer and the frame buffer
|
||||||
void text_clear(text_t* text);
|
void text_clear(text_t* text);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // TEXT_RENDERER_H
|
#endif // TEXT_RENDERER_H
|
||||||
+77
-77
@@ -1,77 +1,77 @@
|
|||||||
// window.h
|
// window.h
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef WINDOW_H
|
#ifndef WINDOW_H
|
||||||
#define WINDOW_H
|
#define WINDOW_H
|
||||||
|
|
||||||
typedef struct window_t window_t;
|
typedef struct window_t window_t;
|
||||||
|
|
||||||
enum KeyCode {
|
enum KeyCode {
|
||||||
// Letters
|
// Letters
|
||||||
KEY_A = 4,
|
KEY_A = 4,
|
||||||
KEY_D = 7,
|
KEY_D = 7,
|
||||||
KEY_S = 22,
|
KEY_S = 22,
|
||||||
KEY_W = 26,
|
KEY_W = 26,
|
||||||
|
|
||||||
// Numbers
|
// Numbers
|
||||||
KEY_1 = 30,
|
KEY_1 = 30,
|
||||||
KEY_0 = 39,
|
KEY_0 = 39,
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
KEY_RETURN = 40,
|
KEY_RETURN = 40,
|
||||||
KEY_ESCAPE = 41,
|
KEY_ESCAPE = 41,
|
||||||
KEY_BACKSPACE = 42,
|
KEY_BACKSPACE = 42,
|
||||||
KEY_TAB = 43,
|
KEY_TAB = 43,
|
||||||
KEY_SPACE = 44,
|
KEY_SPACE = 44,
|
||||||
|
|
||||||
// Modifiers
|
// Modifiers
|
||||||
KEY_LCTRL = 224,
|
KEY_LCTRL = 224,
|
||||||
KEY_LSHIFT = 225,
|
KEY_LSHIFT = 225,
|
||||||
KEY_RCTRL = 228,
|
KEY_RCTRL = 228,
|
||||||
KEY_RSHIFT = 229,
|
KEY_RSHIFT = 229,
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
KEY_RIGHT = 79,
|
KEY_RIGHT = 79,
|
||||||
KEY_LEFT = 80,
|
KEY_LEFT = 80,
|
||||||
KEY_DOWN = 81,
|
KEY_DOWN = 81,
|
||||||
KEY_UP = 82,
|
KEY_UP = 82,
|
||||||
|
|
||||||
// Function Keys
|
// Function Keys
|
||||||
KEY_F1 = 58,
|
KEY_F1 = 58,
|
||||||
KEY_F2 = 59
|
KEY_F2 = 59
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MouseButton {
|
enum MouseButton {
|
||||||
MOUSE_BUTTON_LEFT = 1,
|
MOUSE_BUTTON_LEFT = 1,
|
||||||
MOUSE_BUTTON_MIDDLE = 2,
|
MOUSE_BUTTON_MIDDLE = 2,
|
||||||
MOUSE_BUTTON_RIGHT = 3
|
MOUSE_BUTTON_RIGHT = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#else
|
#else
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct { int x, y; } mouse_pos_t;
|
typedef struct { int x, y; } mouse_pos_t;
|
||||||
|
|
||||||
// creates a window with given width, height and title, title can be utf8
|
// creates a window with given width, height and title, title can be utf8
|
||||||
window_t* window_create(int width, int height, const char* title);
|
window_t* window_create(int width, int height, const char* title);
|
||||||
// destroys the window
|
// destroys the window
|
||||||
void window_destroy(window_t* s);
|
void window_destroy(window_t* s);
|
||||||
// polls window events, returns true if the window should close
|
// polls window events, returns true if the window should close
|
||||||
bool window_pollevent(window_t* s);
|
bool window_pollevent(window_t* s);
|
||||||
// blits the given framebuffer to the window. must be the same size as the window and ARGB8888 format
|
// blits the given framebuffer to the window. must be the same size as the window and ARGB8888 format
|
||||||
void window_blit(window_t* s, const uint32_t* framebuffer);
|
void window_blit(window_t* s, const uint32_t* framebuffer);
|
||||||
// returns true if the given key is currently held down
|
// returns true if the given key is currently held down
|
||||||
bool window_is_key_down(window_t* s, int key_scancode);
|
bool window_is_key_down(window_t* s, int key_scancode);
|
||||||
// returns true if the given mouse button is currently held down
|
// returns true if the given mouse button is currently held down
|
||||||
bool window_is_mouse_button_down(window_t* s, int button_index);
|
bool window_is_mouse_button_down(window_t* s, int button_index);
|
||||||
// gets the current mouse position in window coordinates
|
// gets the current mouse position in window coordinates
|
||||||
mouse_pos_t window_get_mouse_position(window_t* s);
|
mouse_pos_t window_get_mouse_position(window_t* s);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // WINDOW_H
|
#endif // WINDOW_H
|
||||||
+343
-343
@@ -1,343 +1,343 @@
|
|||||||
// audio.cpp
|
// audio.cpp
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#define MADPCM_IMPLEMENTATION
|
#define MADPCM_IMPLEMENTATION
|
||||||
#include "madpcm.h"
|
#include "madpcm.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#define AHEAD_BLOCKS 10
|
#define AHEAD_BLOCKS 10
|
||||||
|
|
||||||
struct AudioDevice {
|
struct AudioDevice {
|
||||||
SDL_AudioDeviceID id = 0;
|
SDL_AudioDeviceID id = 0;
|
||||||
AudioDevice() = default;
|
AudioDevice() = default;
|
||||||
explicit AudioDevice(SDL_AudioDeviceID id) : id(id) {}
|
explicit AudioDevice(SDL_AudioDeviceID id) : id(id) {}
|
||||||
~AudioDevice() { if (id) SDL_CloseAudioDevice(id); }
|
~AudioDevice() { if (id) SDL_CloseAudioDevice(id); }
|
||||||
AudioDevice(const AudioDevice&) = delete;
|
AudioDevice(const AudioDevice&) = delete;
|
||||||
AudioDevice& operator=(const AudioDevice&) = delete;
|
AudioDevice& operator=(const AudioDevice&) = delete;
|
||||||
AudioDevice(AudioDevice&& o) noexcept : id(o.id) { o.id = 0; }
|
AudioDevice(AudioDevice&& o) noexcept : id(o.id) { o.id = 0; }
|
||||||
AudioDevice& operator=(AudioDevice&& o) noexcept {
|
AudioDevice& operator=(AudioDevice&& o) noexcept {
|
||||||
if (this != &o) { if (id) SDL_CloseAudioDevice(id); id = o.id; o.id = 0; }
|
if (this != &o) { if (id) SDL_CloseAudioDevice(id); id = o.id; o.id = 0; }
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
operator bool() const { return id != 0; }
|
operator bool() const { return id != 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Stream {
|
struct Stream {
|
||||||
uint32_t id = 0;
|
uint32_t id = 0;
|
||||||
AudioDevice device;
|
AudioDevice device;
|
||||||
const uint8_t* data = nullptr;
|
const uint8_t* data = nullptr;
|
||||||
int total_blocks = 0;
|
int total_blocks = 0;
|
||||||
int current_block = 0;
|
int current_block = 0;
|
||||||
int channels = 0;
|
int channels = 0;
|
||||||
bool loop = false;
|
bool loop = false;
|
||||||
bool paused = false;
|
bool paused = false;
|
||||||
|
|
||||||
float volume = 1.0f;
|
float volume = 1.0f;
|
||||||
float volume_from = 1.0f;
|
float volume_from = 1.0f;
|
||||||
float volume_to = 1.0f;
|
float volume_to = 1.0f;
|
||||||
float volume_anim_elapsed = 0.0f;
|
float volume_anim_elapsed = 0.0f;
|
||||||
float volume_anim_duration = 0.0f;
|
float volume_anim_duration = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RiffHeader {
|
struct RiffHeader {
|
||||||
char riff[4];
|
char riff[4];
|
||||||
uint32_t fileSize;
|
uint32_t fileSize;
|
||||||
char wave[4];
|
char wave[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct audio_t {
|
struct audio_t {
|
||||||
std::unordered_map<uint32_t, Stream> streams;
|
std::unordered_map<uint32_t, Stream> streams;
|
||||||
uint32_t next_id = 1;
|
uint32_t next_id = 1;
|
||||||
|
|
||||||
float global_volume = 1.0f;
|
float global_volume = 1.0f;
|
||||||
float global_volume_from = 1.0f;
|
float global_volume_from = 1.0f;
|
||||||
float global_volume_to = 1.0f;
|
float global_volume_to = 1.0f;
|
||||||
float global_volume_anim_elapsed = 0.0f;
|
float global_volume_anim_elapsed = 0.0f;
|
||||||
float global_volume_anim_duration = 0.0f;
|
float global_volume_anim_duration = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
static float clampf(float v, float lo, float hi) {
|
static float clampf(float v, float lo, float hi) {
|
||||||
return v < lo ? lo : v > hi ? hi : v;
|
return v < lo ? lo : v > hi ? hi : v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode one block, scale by combined volume, queue to device.
|
// Decode one block, scale by combined volume, queue to device.
|
||||||
// Returns false if the stream has ended (non-looping, EOF).
|
// Returns false if the stream has ended (non-looping, EOF).
|
||||||
static bool pump_block(audio_t* audio, Stream& s) {
|
static bool pump_block(audio_t* audio, Stream& s) {
|
||||||
int block_size = (s.channels == 2) ? MADPCM_STEREO_SIZE : MADPCM_MONO_SIZE;
|
int block_size = (s.channels == 2) ? MADPCM_STEREO_SIZE : MADPCM_MONO_SIZE;
|
||||||
|
|
||||||
if (s.current_block >= s.total_blocks) {
|
if (s.current_block >= s.total_blocks) {
|
||||||
if (s.loop) s.current_block = 0;
|
if (s.loop) s.current_block = 0;
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t* block = s.data + s.current_block * block_size;
|
const uint8_t* block = s.data + s.current_block * block_size;
|
||||||
int16_t pcm[MADPCM_BLOCK_SAMPLES * 2];
|
int16_t pcm[MADPCM_BLOCK_SAMPLES * 2];
|
||||||
int sample_count;
|
int sample_count;
|
||||||
|
|
||||||
if (s.channels == 2) {
|
if (s.channels == 2) {
|
||||||
int16_t L[MADPCM_BLOCK_SAMPLES], R[MADPCM_BLOCK_SAMPLES];
|
int16_t L[MADPCM_BLOCK_SAMPLES], R[MADPCM_BLOCK_SAMPLES];
|
||||||
madpcm_decode_block_stereo(block, L, R);
|
madpcm_decode_block_stereo(block, L, R);
|
||||||
for (int i = 0; i < MADPCM_BLOCK_SAMPLES; i++) {
|
for (int i = 0; i < MADPCM_BLOCK_SAMPLES; i++) {
|
||||||
pcm[i * 2] = L[i];
|
pcm[i * 2] = L[i];
|
||||||
pcm[i * 2 + 1] = R[i];
|
pcm[i * 2 + 1] = R[i];
|
||||||
}
|
}
|
||||||
sample_count = MADPCM_BLOCK_SAMPLES * 2;
|
sample_count = MADPCM_BLOCK_SAMPLES * 2;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
madpcm_decode_block(block, pcm);
|
madpcm_decode_block(block, pcm);
|
||||||
sample_count = MADPCM_BLOCK_SAMPLES;
|
sample_count = MADPCM_BLOCK_SAMPLES;
|
||||||
}
|
}
|
||||||
|
|
||||||
float vol = clampf(s.volume * audio->global_volume, 0.0f, 1.0f);
|
float vol = clampf(s.volume * audio->global_volume, 0.0f, 1.0f);
|
||||||
for (int i = 0; i < sample_count; i++)
|
for (int i = 0; i < sample_count; i++)
|
||||||
pcm[i] = static_cast<int16_t>(pcm[i] * vol);
|
pcm[i] = static_cast<int16_t>(pcm[i] * vol);
|
||||||
|
|
||||||
SDL_QueueAudio(s.device.id, pcm, static_cast<uint32_t>(sample_count * sizeof(int16_t)));
|
SDL_QueueAudio(s.device.id, pcm, static_cast<uint32_t>(sample_count * sizeof(int16_t)));
|
||||||
s.current_block++;
|
s.current_block++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
audio_t* audio_init() {
|
audio_t* audio_init() {
|
||||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) return nullptr;
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) return nullptr;
|
||||||
return new audio_t{};
|
return new audio_t{};
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_destroy(audio_t* audio) {
|
void audio_destroy(audio_t* audio) {
|
||||||
if (!audio) return;
|
if (!audio) return;
|
||||||
// streams destructor closes all SDL devices via RAII
|
// streams destructor closes all SDL devices via RAII
|
||||||
delete audio;
|
delete audio;
|
||||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* audio_load_madpcm(const char* filename, int* out_size, int* out_channels, int* out_rate, int* out_total_samples) {
|
uint8_t* audio_load_madpcm(const char* filename, int* out_size, int* out_channels, int* out_rate, int* out_total_samples) {
|
||||||
FILE* f = fopen(filename, "rb");
|
FILE* f = fopen(filename, "rb");
|
||||||
if (!f) return nullptr;
|
if (!f) return nullptr;
|
||||||
|
|
||||||
RiffHeader h;
|
RiffHeader h;
|
||||||
if (fread(&h, sizeof(h), 1, f) != 1 ||
|
if (fread(&h, sizeof(h), 1, f) != 1 ||
|
||||||
memcmp(h.riff, "RIFF", 4) != 0 ||
|
memcmp(h.riff, "RIFF", 4) != 0 ||
|
||||||
memcmp(h.wave, "WAVE", 4) != 0) {
|
memcmp(h.wave, "WAVE", 4) != 0) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
char chunkId[4];
|
char chunkId[4];
|
||||||
uint32_t chunkSize;
|
uint32_t chunkSize;
|
||||||
int fmtFound = 0;
|
int fmtFound = 0;
|
||||||
uint16_t audioFmt = 0;
|
uint16_t audioFmt = 0;
|
||||||
uint16_t numChannels = 0;
|
uint16_t numChannels = 0;
|
||||||
uint32_t dataLen = 0;
|
uint32_t dataLen = 0;
|
||||||
int rate = 0;
|
int rate = 0;
|
||||||
uint8_t* data = nullptr;
|
uint8_t* data = nullptr;
|
||||||
|
|
||||||
while (fread(chunkId, 1, 4, f) == 4) {
|
while (fread(chunkId, 1, 4, f) == 4) {
|
||||||
if (fread(&chunkSize, 4, 1, f) != 1) break;
|
if (fread(&chunkSize, 4, 1, f) != 1) break;
|
||||||
|
|
||||||
if (memcmp(chunkId, "fmt ", 4) == 0) {
|
if (memcmp(chunkId, "fmt ", 4) == 0) {
|
||||||
if (fread(&audioFmt, 2, 1, f) != 1) break;
|
if (fread(&audioFmt, 2, 1, f) != 1) break;
|
||||||
if (fread(&numChannels, 2, 1, f) != 1) break;
|
if (fread(&numChannels, 2, 1, f) != 1) break;
|
||||||
if (fread(&rate, 4, 1, f) != 1) break;
|
if (fread(&rate, 4, 1, f) != 1) break;
|
||||||
fseek(f, chunkSize - 8, SEEK_CUR);
|
fseek(f, chunkSize - 8, SEEK_CUR);
|
||||||
fmtFound = 1;
|
fmtFound = 1;
|
||||||
}
|
}
|
||||||
else if (memcmp(chunkId, "data", 4) == 0) {
|
else if (memcmp(chunkId, "data", 4) == 0) {
|
||||||
dataLen = chunkSize;
|
dataLen = chunkSize;
|
||||||
data = static_cast<uint8_t*>(malloc(dataLen));
|
data = static_cast<uint8_t*>(malloc(dataLen));
|
||||||
if (fread(data, 1, dataLen, f) != dataLen) {
|
if (fread(data, 1, dataLen, f) != dataLen) {
|
||||||
free(data);
|
free(data);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fseek(f, chunkSize, SEEK_CUR);
|
fseek(f, chunkSize, SEEK_CUR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
if (!fmtFound || audioFmt != MADPCM_WAVE_FORMAT || !data) {
|
if (!fmtFound || audioFmt != MADPCM_WAVE_FORMAT || !data) {
|
||||||
if (data) free(data);
|
if (data) free(data);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out_channels) *out_channels = static_cast<int>(numChannels);
|
if (out_channels) *out_channels = static_cast<int>(numChannels);
|
||||||
if (out_size) *out_size = static_cast<int>(dataLen);
|
if (out_size) *out_size = static_cast<int>(dataLen);
|
||||||
if (out_rate) *out_rate = rate;
|
if (out_rate) *out_rate = rate;
|
||||||
|
|
||||||
if (out_total_samples) {
|
if (out_total_samples) {
|
||||||
int blockSize = (numChannels == 2) ? MADPCM_STEREO_SIZE : MADPCM_MONO_SIZE;
|
int blockSize = (numChannels == 2) ? MADPCM_STEREO_SIZE : MADPCM_MONO_SIZE;
|
||||||
int numBlocks = dataLen / blockSize;
|
int numBlocks = dataLen / blockSize;
|
||||||
*out_total_samples = numBlocks * MADPCM_BLOCK_SAMPLES;
|
*out_total_samples = numBlocks * MADPCM_BLOCK_SAMPLES;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_free_data(uint8_t* data) {
|
void audio_free_data(uint8_t* data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_stream_id_t audio_play(audio_t* audio, const uint8_t* data, int size,
|
audio_stream_id_t audio_play(audio_t* audio, const uint8_t* data, int size,
|
||||||
int channels, int rate, bool loop) {
|
int channels, int rate, bool loop) {
|
||||||
if (!audio || !data) return 0;
|
if (!audio || !data) return 0;
|
||||||
|
|
||||||
SDL_AudioSpec spec{};
|
SDL_AudioSpec spec{};
|
||||||
spec.freq = rate;
|
spec.freq = rate;
|
||||||
spec.format = AUDIO_S16SYS;
|
spec.format = AUDIO_S16SYS;
|
||||||
spec.channels = static_cast<uint8_t>(channels);
|
spec.channels = static_cast<uint8_t>(channels);
|
||||||
spec.samples = MADPCM_BLOCK_SAMPLES;
|
spec.samples = MADPCM_BLOCK_SAMPLES;
|
||||||
|
|
||||||
SDL_AudioDeviceID dev = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, 0);
|
SDL_AudioDeviceID dev = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, 0);
|
||||||
if (!dev) return 0;
|
if (!dev) return 0;
|
||||||
|
|
||||||
int block_size = (channels == 2) ? MADPCM_STEREO_SIZE : MADPCM_MONO_SIZE;
|
int block_size = (channels == 2) ? MADPCM_STEREO_SIZE : MADPCM_MONO_SIZE;
|
||||||
int total_blocks = size / block_size;
|
int total_blocks = size / block_size;
|
||||||
uint32_t id = audio->next_id++;
|
uint32_t id = audio->next_id++;
|
||||||
|
|
||||||
Stream& s = audio->streams[id];
|
Stream& s = audio->streams[id];
|
||||||
s.id = id;
|
s.id = id;
|
||||||
s.device = AudioDevice(dev);
|
s.device = AudioDevice(dev);
|
||||||
s.data = data;
|
s.data = data;
|
||||||
s.total_blocks = total_blocks;
|
s.total_blocks = total_blocks;
|
||||||
s.channels = channels;
|
s.channels = channels;
|
||||||
s.loop = loop;
|
s.loop = loop;
|
||||||
|
|
||||||
for (int i = 0; i < AHEAD_BLOCKS; i++)
|
for (int i = 0; i < AHEAD_BLOCKS; i++)
|
||||||
if (!pump_block(audio, s)) break;
|
if (!pump_block(audio, s)) break;
|
||||||
|
|
||||||
SDL_PauseAudioDevice(dev, 0);
|
SDL_PauseAudioDevice(dev, 0);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audio_stop(audio_t* audio, audio_stream_id_t id) {
|
bool audio_stop(audio_t* audio, audio_stream_id_t id) {
|
||||||
if (!audio) return false;
|
if (!audio) return false;
|
||||||
return audio->streams.erase(id) > 0; // destructor closes the device
|
return audio->streams.erase(id) > 0; // destructor closes the device
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audio_pause(audio_t* audio, audio_stream_id_t id) {
|
bool audio_pause(audio_t* audio, audio_stream_id_t id) {
|
||||||
if (!audio) return false;
|
if (!audio) return false;
|
||||||
auto it = audio->streams.find(id);
|
auto it = audio->streams.find(id);
|
||||||
if (it == audio->streams.end()) return false;
|
if (it == audio->streams.end()) return false;
|
||||||
Stream& s = it->second;
|
Stream& s = it->second;
|
||||||
if (!s.paused) {
|
if (!s.paused) {
|
||||||
SDL_PauseAudioDevice(s.device.id, 1);
|
SDL_PauseAudioDevice(s.device.id, 1);
|
||||||
s.paused = true;
|
s.paused = true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audio_resume(audio_t* audio, audio_stream_id_t id) {
|
bool audio_resume(audio_t* audio, audio_stream_id_t id) {
|
||||||
if (!audio) return false;
|
if (!audio) return false;
|
||||||
auto it = audio->streams.find(id);
|
auto it = audio->streams.find(id);
|
||||||
if (it == audio->streams.end()) return false;
|
if (it == audio->streams.end()) return false;
|
||||||
Stream& s = it->second;
|
Stream& s = it->second;
|
||||||
if (s.paused) {
|
if (s.paused) {
|
||||||
SDL_PauseAudioDevice(s.device.id, 0);
|
SDL_PauseAudioDevice(s.device.id, 0);
|
||||||
s.paused = false;
|
s.paused = false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audio_is_active(audio_t* audio, audio_stream_id_t id) {
|
bool audio_is_active(audio_t* audio, audio_stream_id_t id) {
|
||||||
if (!audio) return false;
|
if (!audio) return false;
|
||||||
return audio->streams.count(id) > 0;
|
return audio->streams.count(id) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audio_is_playing(audio_t* audio, audio_stream_id_t id) {
|
bool audio_is_playing(audio_t* audio, audio_stream_id_t id) {
|
||||||
if (!audio) return false;
|
if (!audio) return false;
|
||||||
auto it = audio->streams.find(id);
|
auto it = audio->streams.find(id);
|
||||||
if (it == audio->streams.end()) return false;
|
if (it == audio->streams.end()) return false;
|
||||||
return !it->second.paused;
|
return !it->second.paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audio_set_volume(audio_t* audio, audio_stream_id_t id, float volume) {
|
bool audio_set_volume(audio_t* audio, audio_stream_id_t id, float volume) {
|
||||||
if (!audio) return false;
|
if (!audio) return false;
|
||||||
auto it = audio->streams.find(id);
|
auto it = audio->streams.find(id);
|
||||||
if (it == audio->streams.end()) return false;
|
if (it == audio->streams.end()) return false;
|
||||||
Stream& s = it->second;
|
Stream& s = it->second;
|
||||||
s.volume = clampf(volume, 0.0f, 1.0f);
|
s.volume = clampf(volume, 0.0f, 1.0f);
|
||||||
s.volume_anim_duration = 0.0f;
|
s.volume_anim_duration = 0.0f;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audio_animate_volume(audio_t* audio, audio_stream_id_t id,
|
bool audio_animate_volume(audio_t* audio, audio_stream_id_t id,
|
||||||
float to_volume, float duration_sec) {
|
float to_volume, float duration_sec) {
|
||||||
if (!audio) return false;
|
if (!audio) return false;
|
||||||
auto it = audio->streams.find(id);
|
auto it = audio->streams.find(id);
|
||||||
if (it == audio->streams.end()) return false;
|
if (it == audio->streams.end()) return false;
|
||||||
Stream& s = it->second;
|
Stream& s = it->second;
|
||||||
s.volume_from = s.volume;
|
s.volume_from = s.volume;
|
||||||
s.volume_to = clampf(to_volume, 0.0f, 1.0f);
|
s.volume_to = clampf(to_volume, 0.0f, 1.0f);
|
||||||
s.volume_anim_elapsed = 0.0f;
|
s.volume_anim_elapsed = 0.0f;
|
||||||
s.volume_anim_duration = duration_sec;
|
s.volume_anim_duration = duration_sec;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_set_global_volume(audio_t* audio, float volume) {
|
void audio_set_global_volume(audio_t* audio, float volume) {
|
||||||
if (!audio) return;
|
if (!audio) return;
|
||||||
audio->global_volume = clampf(volume, 0.0f, 1.0f);
|
audio->global_volume = clampf(volume, 0.0f, 1.0f);
|
||||||
audio->global_volume_anim_duration = 0.0f;
|
audio->global_volume_anim_duration = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_animate_global_volume(audio_t* audio,
|
void audio_animate_global_volume(audio_t* audio,
|
||||||
float to_volume, float duration_sec) {
|
float to_volume, float duration_sec) {
|
||||||
if (!audio) return;
|
if (!audio) return;
|
||||||
audio->global_volume_from = audio->global_volume;
|
audio->global_volume_from = audio->global_volume;
|
||||||
audio->global_volume_to = clampf(to_volume, 0.0f, 1.0f);
|
audio->global_volume_to = clampf(to_volume, 0.0f, 1.0f);
|
||||||
audio->global_volume_anim_elapsed = 0.0f;
|
audio->global_volume_anim_elapsed = 0.0f;
|
||||||
audio->global_volume_anim_duration = duration_sec;
|
audio->global_volume_anim_duration = duration_sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_tick(audio_t* audio, float delta_time_sec) {
|
void audio_tick(audio_t* audio, float delta_time_sec) {
|
||||||
if (!audio) return;
|
if (!audio) return;
|
||||||
|
|
||||||
// Advance global volume animation
|
// Advance global volume animation
|
||||||
if (audio->global_volume_anim_duration > 0.0f) {
|
if (audio->global_volume_anim_duration > 0.0f) {
|
||||||
audio->global_volume_anim_elapsed += delta_time_sec;
|
audio->global_volume_anim_elapsed += delta_time_sec;
|
||||||
float t = audio->global_volume_anim_elapsed / audio->global_volume_anim_duration;
|
float t = audio->global_volume_anim_elapsed / audio->global_volume_anim_duration;
|
||||||
if (t >= 1.0f) { t = 1.0f; audio->global_volume_anim_duration = 0.0f; }
|
if (t >= 1.0f) { t = 1.0f; audio->global_volume_anim_duration = 0.0f; }
|
||||||
audio->global_volume = audio->global_volume_from +
|
audio->global_volume = audio->global_volume_from +
|
||||||
(audio->global_volume_to - audio->global_volume_from) * t;
|
(audio->global_volume_to - audio->global_volume_from) * t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect IDs of streams to remove after iteration to avoid invalidating the map
|
// Collect IDs of streams to remove after iteration to avoid invalidating the map
|
||||||
std::vector<uint32_t> to_remove;
|
std::vector<uint32_t> to_remove;
|
||||||
|
|
||||||
for (auto& [id, s] : audio->streams) {
|
for (auto& [id, s] : audio->streams) {
|
||||||
// Advance per-stream volume animation
|
// Advance per-stream volume animation
|
||||||
if (s.volume_anim_duration > 0.0f) {
|
if (s.volume_anim_duration > 0.0f) {
|
||||||
s.volume_anim_elapsed += delta_time_sec;
|
s.volume_anim_elapsed += delta_time_sec;
|
||||||
float t = s.volume_anim_elapsed / s.volume_anim_duration;
|
float t = s.volume_anim_elapsed / s.volume_anim_duration;
|
||||||
if (t >= 1.0f) { t = 1.0f; s.volume_anim_duration = 0.0f; }
|
if (t >= 1.0f) { t = 1.0f; s.volume_anim_duration = 0.0f; }
|
||||||
s.volume = s.volume_from + (s.volume_to - s.volume_from) * t;
|
s.volume = s.volume_from + (s.volume_to - s.volume_from) * t;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.paused) continue;
|
if (s.paused) continue;
|
||||||
|
|
||||||
size_t target_bytes = AHEAD_BLOCKS * MADPCM_BLOCK_SAMPLES
|
size_t target_bytes = AHEAD_BLOCKS * MADPCM_BLOCK_SAMPLES
|
||||||
* s.channels * sizeof(int16_t);
|
* s.channels * sizeof(int16_t);
|
||||||
|
|
||||||
while (SDL_GetQueuedAudioSize(s.device.id) < target_bytes) {
|
while (SDL_GetQueuedAudioSize(s.device.id) < target_bytes) {
|
||||||
if (!pump_block(audio, s)) {
|
if (!pump_block(audio, s)) {
|
||||||
// Non-looping EOF — wait for queue to fully drain then remove
|
// Non-looping EOF — wait for queue to fully drain then remove
|
||||||
if (SDL_GetQueuedAudioSize(s.device.id) == 0)
|
if (SDL_GetQueuedAudioSize(s.device.id) == 0)
|
||||||
to_remove.push_back(id);
|
to_remove.push_back(id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t id : to_remove)
|
for (uint32_t id : to_remove)
|
||||||
audio->streams.erase(id);
|
audio->streams.erase(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
+1185
-1185
File diff suppressed because it is too large.
Load diff
+74
-74
@@ -1,74 +1,74 @@
|
|||||||
#pragma warning(push, 0)
|
#pragma warning(push, 0)
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||||
#include <stb_image_resize2.h>
|
#include <stb_image_resize2.h>
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <image.h>
|
#include <image.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
uint32_t* image_load(const char* filename, int* width, int* height) {
|
uint32_t* image_load(const char* filename, int* width, int* height) {
|
||||||
int n;
|
int n;
|
||||||
unsigned char* data = stbi_load(filename, width, height, &n, 4); // force 4 channels (RGBA)
|
unsigned char* data = stbi_load(filename, width, height, &n, 4); // force 4 channels (RGBA)
|
||||||
if (!data) return nullptr;
|
if (!data) return nullptr;
|
||||||
|
|
||||||
// Convert RGBA to ARGB
|
// Convert RGBA to ARGB
|
||||||
uint32_t* out = (uint32_t*)malloc(*width * *height * sizeof(uint32_t));
|
uint32_t* out = (uint32_t*)malloc(*width * *height * sizeof(uint32_t));
|
||||||
if (!out) {
|
if (!out) {
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < *width * *height; ++i) {
|
for (int i = 0; i < *width * *height; ++i) {
|
||||||
uint8_t r = data[4*i + 0];
|
uint8_t r = data[4*i + 0];
|
||||||
uint8_t g = data[4*i + 1];
|
uint8_t g = data[4*i + 1];
|
||||||
uint8_t b = data[4*i + 2];
|
uint8_t b = data[4*i + 2];
|
||||||
uint8_t a = data[4*i + 3];
|
uint8_t a = data[4*i + 3];
|
||||||
out[i] = (a << 24) | (r << 16) | (g << 8) | b;
|
out[i] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||||
}
|
}
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t* image_load_resized(const char* filename, int desired_width, int desired_height) {
|
uint32_t* image_load_resized(const char* filename, int desired_width, int desired_height) {
|
||||||
int original_width, original_height, n;
|
int original_width, original_height, n;
|
||||||
unsigned char* data = stbi_load(filename, &original_width, &original_height, &n, 4);
|
unsigned char* data = stbi_load(filename, &original_width, &original_height, &n, 4);
|
||||||
if (!data) return nullptr;
|
if (!data) return nullptr;
|
||||||
|
|
||||||
// Allocate buffer for resized image
|
// Allocate buffer for resized image
|
||||||
uint32_t* resized = (uint32_t*)malloc(desired_width * desired_height * 4);
|
uint32_t* resized = (uint32_t*)malloc(desired_width * desired_height * 4);
|
||||||
if (!resized) {
|
if (!resized) {
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize the image
|
// Resize the image
|
||||||
stbir_resize_uint8_linear(data, original_width, original_height, 0,
|
stbir_resize_uint8_linear(data, original_width, original_height, 0,
|
||||||
(unsigned char* )resized, desired_width, desired_height, 0,
|
(unsigned char* )resized, desired_width, desired_height, 0,
|
||||||
STBIR_RGBA);
|
STBIR_RGBA);
|
||||||
|
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
|
|
||||||
// In-place conversion from RGBA to ARGB
|
// In-place conversion from RGBA to ARGB
|
||||||
// We iterate backwards to ensure we don't overwrite bytes before reading them,
|
// We iterate backwards to ensure we don't overwrite bytes before reading them,
|
||||||
// though since we are working within the same 4-byte boundaries, forward works too!
|
// though since we are working within the same 4-byte boundaries, forward works too!
|
||||||
for (int i = 0; i < desired_width * desired_height; ++i) {
|
for (int i = 0; i < desired_width * desired_height; ++i) {
|
||||||
uint8_t* pixel = (uint8_t*)&resized[i];
|
uint8_t* pixel = (uint8_t*)&resized[i];
|
||||||
uint8_t r = pixel[0];
|
uint8_t r = pixel[0];
|
||||||
uint8_t g = pixel[1];
|
uint8_t g = pixel[1];
|
||||||
uint8_t b = pixel[2];
|
uint8_t b = pixel[2];
|
||||||
uint8_t a = pixel[3];
|
uint8_t a = pixel[3];
|
||||||
|
|
||||||
resized[i] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
resized[i] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||||
}
|
}
|
||||||
|
|
||||||
return resized;
|
return resized;
|
||||||
}
|
}
|
||||||
|
|
||||||
void image_free(uint32_t* data) {
|
void image_free(uint32_t* data) {
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
+659
-659
File diff suppressed because it is too large.
Load diff
+798
-798
File diff suppressed because it is too large.
Load diff
+214
-214
@@ -1,214 +1,214 @@
|
|||||||
// window.cpp
|
// window.cpp
|
||||||
// This file implements the windowing and input system using SDL2.
|
// This file implements the windowing and input system using SDL2.
|
||||||
// It provides a simple API for creating a window, blitting pixel data, and handling input events.
|
// It provides a simple API for creating a window, blitting pixel data, and handling input events.
|
||||||
// Since Microsoft is retarded and forces us to live in 1985, we use a thread to manage the window
|
// Since Microsoft is retarded and forces us to live in 1985, we use a thread to manage the window
|
||||||
// to avoid freezing the app during window move or resize operations. Of course, normal OSes don't need that.
|
// to avoid freezing the app during window move or resize operations. Of course, normal OSes don't need that.
|
||||||
#include <window.h>
|
#include <window.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct window_t {
|
struct window_t {
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
SDL_Renderer* renderer;
|
SDL_Renderer* renderer;
|
||||||
SDL_Texture* texture;
|
SDL_Texture* texture;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
|
||||||
const uint8_t* keyboard_state;
|
const uint8_t* keyboard_state;
|
||||||
int mouse_x;
|
int mouse_x;
|
||||||
int mouse_y;
|
int mouse_y;
|
||||||
uint32_t mouse_buttons;
|
uint32_t mouse_buttons;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::thread worker;
|
std::thread worker;
|
||||||
std::atomic<bool> running{true};
|
std::atomic<bool> running{true};
|
||||||
std::atomic<bool> quit_requested{false};
|
std::atomic<bool> quit_requested{false};
|
||||||
|
|
||||||
std::mutex buffer_mutex;
|
std::mutex buffer_mutex;
|
||||||
uint32_t* shared_fb;
|
uint32_t* shared_fb;
|
||||||
|
|
||||||
std::mutex input_mutex;
|
std::mutex input_mutex;
|
||||||
uint8_t kb_copy[SDL_NUM_SCANCODES];
|
uint8_t kb_copy[SDL_NUM_SCANCODES];
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// Internal helper for cleanup
|
// Internal helper for cleanup
|
||||||
static void window_cleanup(window_t* s) {
|
static void window_cleanup(window_t* s) {
|
||||||
if (!s) return;
|
if (!s) return;
|
||||||
if (s->texture) SDL_DestroyTexture(s->texture);
|
if (s->texture) SDL_DestroyTexture(s->texture);
|
||||||
if (s->renderer) SDL_DestroyRenderer(s->renderer);
|
if (s->renderer) SDL_DestroyRenderer(s->renderer);
|
||||||
if (s->window) SDL_DestroyWindow(s->window);
|
if (s->window) SDL_DestroyWindow(s->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Internal function to perform the actual blit and present on the worker thread
|
// Internal function to perform the actual blit and present on the worker thread
|
||||||
static void win32_internal_render(window_t* s) {
|
static void win32_internal_render(window_t* s) {
|
||||||
if (!s->renderer || !s->texture) return;
|
if (!s->renderer || !s->texture) return;
|
||||||
|
|
||||||
void* pixels;
|
void* pixels;
|
||||||
int pitch;
|
int pitch;
|
||||||
if (SDL_LockTexture(s->texture, nullptr, &pixels, &pitch) == 0) {
|
if (SDL_LockTexture(s->texture, nullptr, &pixels, &pitch) == 0) {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(s->buffer_mutex);
|
std::lock_guard<std::mutex> lock(s->buffer_mutex);
|
||||||
std::memcpy(pixels, s->shared_fb, s->width * s->height * 4);
|
std::memcpy(pixels, s->shared_fb, s->width * s->height * 4);
|
||||||
}
|
}
|
||||||
SDL_UnlockTexture(s->texture);
|
SDL_UnlockTexture(s->texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_RenderClear(s->renderer);
|
SDL_RenderClear(s->renderer);
|
||||||
SDL_RenderCopy(s->renderer, s->texture, nullptr, nullptr);
|
SDL_RenderCopy(s->renderer, s->texture, nullptr, nullptr);
|
||||||
SDL_RenderPresent(s->renderer);
|
SDL_RenderPresent(s->renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// THE FIX: This callback is triggered by SDL even when Windows is in a modal drag loop.
|
// THE FIX: This callback is triggered by SDL even when Windows is in a modal drag loop.
|
||||||
static int SDLCALL win32_event_watch(void* userdata, SDL_Event* event) {
|
static int SDLCALL win32_event_watch(void* userdata, SDL_Event* event) {
|
||||||
window_t* s = (window_t*)userdata;
|
window_t* s = (window_t*)userdata;
|
||||||
if (event->type == SDL_WINDOWEVENT) {
|
if (event->type == SDL_WINDOWEVENT) {
|
||||||
if (event->window.event == SDL_WINDOWEVENT_MOVED ||
|
if (event->window.event == SDL_WINDOWEVENT_MOVED ||
|
||||||
event->window.event == SDL_WINDOWEVENT_RESIZED ||
|
event->window.event == SDL_WINDOWEVENT_RESIZED ||
|
||||||
event->window.event == SDL_WINDOWEVENT_EXPOSED) {
|
event->window.event == SDL_WINDOWEVENT_EXPOSED) {
|
||||||
win32_internal_render(s);
|
win32_internal_render(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void win32_window_worker(window_t* s, const char* title) {
|
static void win32_window_worker(window_t* s, const char* title) {
|
||||||
SDL_InitSubSystem(SDL_INIT_VIDEO);
|
SDL_InitSubSystem(SDL_INIT_VIDEO);
|
||||||
|
|
||||||
s->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, s->width, s->height, 0);
|
s->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, s->width, s->height, 0);
|
||||||
s->renderer = SDL_CreateRenderer(s->window, -1, SDL_RENDERER_ACCELERATED);
|
s->renderer = SDL_CreateRenderer(s->window, -1, SDL_RENDERER_ACCELERATED);
|
||||||
s->texture = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, s->width, s->height);
|
s->texture = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, s->width, s->height);
|
||||||
|
|
||||||
// Register the watch to handle rendering during the drag-loop
|
// Register the watch to handle rendering during the drag-loop
|
||||||
SDL_AddEventWatch(win32_event_watch, s);
|
SDL_AddEventWatch(win32_event_watch, s);
|
||||||
|
|
||||||
while (s->running) {
|
while (s->running) {
|
||||||
SDL_Event ev;
|
SDL_Event ev;
|
||||||
// Use a timeout so we don't hog CPU but stay responsive
|
// Use a timeout so we don't hog CPU but stay responsive
|
||||||
if (SDL_WaitEventTimeout(&ev, 8)) {
|
if (SDL_WaitEventTimeout(&ev, 8)) {
|
||||||
if (ev.type == SDL_QUIT) s->quit_requested = true;
|
if (ev.type == SDL_QUIT) s->quit_requested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync input state for the main thread
|
// Sync input state for the main thread
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(s->input_mutex);
|
std::lock_guard<std::mutex> lock(s->input_mutex);
|
||||||
const uint8_t* state = SDL_GetKeyboardState(nullptr);
|
const uint8_t* state = SDL_GetKeyboardState(nullptr);
|
||||||
std::memcpy(s->kb_copy, state, SDL_NUM_SCANCODES);
|
std::memcpy(s->kb_copy, state, SDL_NUM_SCANCODES);
|
||||||
s->mouse_buttons = SDL_GetMouseState(&s->mouse_x, &s->mouse_y);
|
s->mouse_buttons = SDL_GetMouseState(&s->mouse_x, &s->mouse_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal frame render
|
// Normal frame render
|
||||||
win32_internal_render(s);
|
win32_internal_render(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_DelEventWatch(win32_event_watch, s);
|
SDL_DelEventWatch(win32_event_watch, s);
|
||||||
window_cleanup(s);
|
window_cleanup(s);
|
||||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
window_t* window_create(int width, int height, const char* title) {
|
window_t* window_create(int width, int height, const char* title) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
window_t* s = new window_t{};
|
window_t* s = new window_t{};
|
||||||
s->width = width;
|
s->width = width;
|
||||||
s->height = height;
|
s->height = height;
|
||||||
s->shared_fb = new uint32_t[width * height];
|
s->shared_fb = new uint32_t[width * height];
|
||||||
s->keyboard_state = s->kb_copy;
|
s->keyboard_state = s->kb_copy;
|
||||||
|
|
||||||
// Spawn the worker thread which owns the SDL context
|
// Spawn the worker thread which owns the SDL context
|
||||||
s->worker = std::thread(win32_window_worker, s, title);
|
s->worker = std::thread(win32_window_worker, s, title);
|
||||||
|
|
||||||
// Wait briefly for the worker to initialize window/renderer pointers
|
// Wait briefly for the worker to initialize window/renderer pointers
|
||||||
while (s->running && !s->renderer) { std::this_thread::yield(); }
|
while (s->running && !s->renderer) { std::this_thread::yield(); }
|
||||||
return s;
|
return s;
|
||||||
#else
|
#else
|
||||||
// Original non-threaded implementation for Linux/others
|
// Original non-threaded implementation for Linux/others
|
||||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) return nullptr;
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) return nullptr;
|
||||||
window_t* s = new window_t{};
|
window_t* s = new window_t{};
|
||||||
s->width = width;
|
s->width = width;
|
||||||
s->height = height;
|
s->height = height;
|
||||||
s->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 0);
|
s->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 0);
|
||||||
s->renderer = SDL_CreateRenderer(s->window, -1, SDL_RENDERER_ACCELERATED);
|
s->renderer = SDL_CreateRenderer(s->window, -1, SDL_RENDERER_ACCELERATED);
|
||||||
s->texture = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
s->texture = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||||
s->keyboard_state = SDL_GetKeyboardState(nullptr);
|
s->keyboard_state = SDL_GetKeyboardState(nullptr);
|
||||||
return s;
|
return s;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void window_destroy(window_t* s) {
|
void window_destroy(window_t* s) {
|
||||||
if (!s) return;
|
if (!s) return;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
s->running = false;
|
s->running = false;
|
||||||
if (s->worker.joinable()) s->worker.join();
|
if (s->worker.joinable()) s->worker.join();
|
||||||
delete[] s->shared_fb;
|
delete[] s->shared_fb;
|
||||||
#else
|
#else
|
||||||
window_cleanup(s);
|
window_cleanup(s);
|
||||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||||
#endif
|
#endif
|
||||||
delete s;
|
delete s;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool window_pollevent(window_t* s) {
|
bool window_pollevent(window_t* s) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Main thread just checks the status reported by the worker
|
// Main thread just checks the status reported by the worker
|
||||||
std::lock_guard<std::mutex> lock(s->input_mutex);
|
std::lock_guard<std::mutex> lock(s->input_mutex);
|
||||||
return s->quit_requested;
|
return s->quit_requested;
|
||||||
#else
|
#else
|
||||||
SDL_Event ev;
|
SDL_Event ev;
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
while (SDL_PollEvent(&ev)) {
|
while (SDL_PollEvent(&ev)) {
|
||||||
if (ev.type == SDL_QUIT) quit = true;
|
if (ev.type == SDL_QUIT) quit = true;
|
||||||
}
|
}
|
||||||
s->mouse_buttons = SDL_GetMouseState(&s->mouse_x, &s->mouse_y);
|
s->mouse_buttons = SDL_GetMouseState(&s->mouse_x, &s->mouse_y);
|
||||||
return quit;
|
return quit;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void window_blit(window_t* s, const uint32_t* fb) {
|
void window_blit(window_t* s, const uint32_t* fb) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Just update the shared buffer; the worker thread will pick it up and present it
|
// Just update the shared buffer; the worker thread will pick it up and present it
|
||||||
std::lock_guard<std::mutex> lock(s->buffer_mutex);
|
std::lock_guard<std::mutex> lock(s->buffer_mutex);
|
||||||
std::memcpy(s->shared_fb, fb, s->width * s->height * 4);
|
std::memcpy(s->shared_fb, fb, s->width * s->height * 4);
|
||||||
#else
|
#else
|
||||||
// Original immediate blit logic
|
// Original immediate blit logic
|
||||||
void* pixels; int pitch;
|
void* pixels; int pitch;
|
||||||
SDL_LockTexture(s->texture, nullptr, &pixels, &pitch);
|
SDL_LockTexture(s->texture, nullptr, &pixels, &pitch);
|
||||||
std::memcpy(pixels, fb, s->width * s->height * 4);
|
std::memcpy(pixels, fb, s->width * s->height * 4);
|
||||||
SDL_UnlockTexture(s->texture);
|
SDL_UnlockTexture(s->texture);
|
||||||
SDL_RenderClear(s->renderer);
|
SDL_RenderClear(s->renderer);
|
||||||
SDL_RenderCopy(s->renderer, s->texture, nullptr, nullptr);
|
SDL_RenderCopy(s->renderer, s->texture, nullptr, nullptr);
|
||||||
SDL_RenderPresent(s->renderer);
|
SDL_RenderPresent(s->renderer);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input getters remain unchanged as they read from the struct's state fields
|
// Input getters remain unchanged as they read from the struct's state fields
|
||||||
bool window_is_key_down(window_t* s, int sc) {
|
bool window_is_key_down(window_t* s, int sc) {
|
||||||
if (sc < 0 || sc >= SDL_NUM_SCANCODES) return false;
|
if (sc < 0 || sc >= SDL_NUM_SCANCODES) return false;
|
||||||
return s->keyboard_state[sc] != 0;
|
return s->keyboard_state[sc] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool window_is_mouse_button_down(window_t* s, int button) {
|
bool window_is_mouse_button_down(window_t* s, int button) {
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case MOUSE_BUTTON_LEFT: return s->mouse_buttons & SDL_BUTTON_LMASK;
|
case MOUSE_BUTTON_LEFT: return s->mouse_buttons & SDL_BUTTON_LMASK;
|
||||||
case MOUSE_BUTTON_MIDDLE: return s->mouse_buttons & SDL_BUTTON_MMASK;
|
case MOUSE_BUTTON_MIDDLE: return s->mouse_buttons & SDL_BUTTON_MMASK;
|
||||||
case MOUSE_BUTTON_RIGHT: return s->mouse_buttons & SDL_BUTTON_RMASK;
|
case MOUSE_BUTTON_RIGHT: return s->mouse_buttons & SDL_BUTTON_RMASK;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mouse_pos_t window_get_mouse_position(window_t* s) {
|
mouse_pos_t window_get_mouse_position(window_t* s) {
|
||||||
return { s->mouse_x, s->mouse_y };
|
return { s->mouse_x, s->mouse_y };
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user