initial commit
This commit is contained in:
@@ -0,0 +1,366 @@
|
||||
#include "client/input.h"
|
||||
#include "client/video.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <string.h>
|
||||
|
||||
static uint8_t key_held[SKELE_KEY_COUNT];
|
||||
static uint8_t key_down[SKELE_KEY_COUNT];
|
||||
|
||||
static SDL_Gamepad *pads[SKELE_MAX_PADS];
|
||||
static uint8_t pad_btn_held[SKELE_MAX_PADS][SKELE_PAD_BUTTON_COUNT];
|
||||
static uint8_t pad_btn_down[SKELE_MAX_PADS][SKELE_PAD_BUTTON_COUNT];
|
||||
static float pad_axes[SKELE_MAX_PADS][SKELE_PAD_AXIS_COUNT];
|
||||
|
||||
static int32_t mouse_dx = 0;
|
||||
static int32_t mouse_dy = 0;
|
||||
|
||||
static skele_key_t scancode_to_key(SDL_Scancode sc)
|
||||
{
|
||||
switch (sc) {
|
||||
case SDL_SCANCODE_A:
|
||||
return SKELE_KEY_A;
|
||||
case SDL_SCANCODE_B:
|
||||
return SKELE_KEY_B;
|
||||
case SDL_SCANCODE_C:
|
||||
return SKELE_KEY_C;
|
||||
case SDL_SCANCODE_D:
|
||||
return SKELE_KEY_D;
|
||||
case SDL_SCANCODE_E:
|
||||
return SKELE_KEY_E;
|
||||
case SDL_SCANCODE_F:
|
||||
return SKELE_KEY_F;
|
||||
case SDL_SCANCODE_G:
|
||||
return SKELE_KEY_G;
|
||||
case SDL_SCANCODE_H:
|
||||
return SKELE_KEY_H;
|
||||
case SDL_SCANCODE_I:
|
||||
return SKELE_KEY_I;
|
||||
case SDL_SCANCODE_J:
|
||||
return SKELE_KEY_J;
|
||||
case SDL_SCANCODE_K:
|
||||
return SKELE_KEY_K;
|
||||
case SDL_SCANCODE_L:
|
||||
return SKELE_KEY_L;
|
||||
case SDL_SCANCODE_M:
|
||||
return SKELE_KEY_M;
|
||||
case SDL_SCANCODE_N:
|
||||
return SKELE_KEY_N;
|
||||
case SDL_SCANCODE_O:
|
||||
return SKELE_KEY_O;
|
||||
case SDL_SCANCODE_P:
|
||||
return SKELE_KEY_P;
|
||||
case SDL_SCANCODE_Q:
|
||||
return SKELE_KEY_Q;
|
||||
case SDL_SCANCODE_R:
|
||||
return SKELE_KEY_R;
|
||||
case SDL_SCANCODE_S:
|
||||
return SKELE_KEY_S;
|
||||
case SDL_SCANCODE_T:
|
||||
return SKELE_KEY_T;
|
||||
case SDL_SCANCODE_U:
|
||||
return SKELE_KEY_U;
|
||||
case SDL_SCANCODE_V:
|
||||
return SKELE_KEY_V;
|
||||
case SDL_SCANCODE_W:
|
||||
return SKELE_KEY_W;
|
||||
case SDL_SCANCODE_X:
|
||||
return SKELE_KEY_X;
|
||||
case SDL_SCANCODE_Y:
|
||||
return SKELE_KEY_Y;
|
||||
case SDL_SCANCODE_Z:
|
||||
return SKELE_KEY_Z;
|
||||
case SDL_SCANCODE_0:
|
||||
return SKELE_KEY_0;
|
||||
case SDL_SCANCODE_1:
|
||||
return SKELE_KEY_1;
|
||||
case SDL_SCANCODE_2:
|
||||
return SKELE_KEY_2;
|
||||
case SDL_SCANCODE_3:
|
||||
return SKELE_KEY_3;
|
||||
case SDL_SCANCODE_4:
|
||||
return SKELE_KEY_4;
|
||||
case SDL_SCANCODE_5:
|
||||
return SKELE_KEY_5;
|
||||
case SDL_SCANCODE_6:
|
||||
return SKELE_KEY_6;
|
||||
case SDL_SCANCODE_7:
|
||||
return SKELE_KEY_7;
|
||||
case SDL_SCANCODE_8:
|
||||
return SKELE_KEY_8;
|
||||
case SDL_SCANCODE_9:
|
||||
return SKELE_KEY_9;
|
||||
case SDL_SCANCODE_F1:
|
||||
return SKELE_KEY_F1;
|
||||
case SDL_SCANCODE_F2:
|
||||
return SKELE_KEY_F2;
|
||||
case SDL_SCANCODE_F3:
|
||||
return SKELE_KEY_F3;
|
||||
case SDL_SCANCODE_F4:
|
||||
return SKELE_KEY_F4;
|
||||
case SDL_SCANCODE_F5:
|
||||
return SKELE_KEY_F5;
|
||||
case SDL_SCANCODE_F6:
|
||||
return SKELE_KEY_F6;
|
||||
case SDL_SCANCODE_F7:
|
||||
return SKELE_KEY_F7;
|
||||
case SDL_SCANCODE_F8:
|
||||
return SKELE_KEY_F8;
|
||||
case SDL_SCANCODE_F9:
|
||||
return SKELE_KEY_F9;
|
||||
case SDL_SCANCODE_F10:
|
||||
return SKELE_KEY_F10;
|
||||
case SDL_SCANCODE_F11:
|
||||
return SKELE_KEY_F11;
|
||||
case SDL_SCANCODE_F12:
|
||||
return SKELE_KEY_F12;
|
||||
case SDL_SCANCODE_SPACE:
|
||||
return SKELE_KEY_SPACE;
|
||||
case SDL_SCANCODE_RETURN:
|
||||
return SKELE_KEY_ENTER;
|
||||
case SDL_SCANCODE_ESCAPE:
|
||||
return SKELE_KEY_ESCAPE;
|
||||
case SDL_SCANCODE_TAB:
|
||||
return SKELE_KEY_TAB;
|
||||
case SDL_SCANCODE_BACKSPACE:
|
||||
return SKELE_KEY_BACKSPACE;
|
||||
case SDL_SCANCODE_LSHIFT:
|
||||
return SKELE_KEY_LSHIFT;
|
||||
case SDL_SCANCODE_RSHIFT:
|
||||
return SKELE_KEY_RSHIFT;
|
||||
case SDL_SCANCODE_LCTRL:
|
||||
return SKELE_KEY_LCTRL;
|
||||
case SDL_SCANCODE_RCTRL:
|
||||
return SKELE_KEY_RCTRL;
|
||||
case SDL_SCANCODE_LALT:
|
||||
return SKELE_KEY_LALT;
|
||||
case SDL_SCANCODE_RALT:
|
||||
return SKELE_KEY_RALT;
|
||||
case SDL_SCANCODE_UP:
|
||||
return SKELE_KEY_UP;
|
||||
case SDL_SCANCODE_DOWN:
|
||||
return SKELE_KEY_DOWN;
|
||||
case SDL_SCANCODE_LEFT:
|
||||
return SKELE_KEY_LEFT;
|
||||
case SDL_SCANCODE_RIGHT:
|
||||
return SKELE_KEY_RIGHT;
|
||||
default:
|
||||
return SKELE_KEY_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static skele_pad_button_t sdl_btn_to_pad(SDL_GamepadButton btn)
|
||||
{
|
||||
switch (btn) {
|
||||
case SDL_GAMEPAD_BUTTON_SOUTH:
|
||||
return SKELE_PAD_A;
|
||||
case SDL_GAMEPAD_BUTTON_EAST:
|
||||
return SKELE_PAD_B;
|
||||
case SDL_GAMEPAD_BUTTON_WEST:
|
||||
return SKELE_PAD_X;
|
||||
case SDL_GAMEPAD_BUTTON_NORTH:
|
||||
return SKELE_PAD_Y;
|
||||
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||
return SKELE_PAD_LB;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||
return SKELE_PAD_RB;
|
||||
case SDL_GAMEPAD_BUTTON_LEFT_STICK:
|
||||
return SKELE_PAD_LSTICK;
|
||||
case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
|
||||
return SKELE_PAD_RSTICK;
|
||||
case SDL_GAMEPAD_BUTTON_START:
|
||||
return SKELE_PAD_START;
|
||||
case SDL_GAMEPAD_BUTTON_BACK:
|
||||
return SKELE_PAD_BACK;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||
return SKELE_PAD_DPAD_UP;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
return SKELE_PAD_DPAD_DOWN;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
return SKELE_PAD_DPAD_LEFT;
|
||||
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
return SKELE_PAD_DPAD_RIGHT;
|
||||
default:
|
||||
return SKELE_PAD_BUTTON_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
static int8_t pad_slot(SDL_JoystickID id)
|
||||
{
|
||||
uint8_t i;
|
||||
for (i = 0; i < SKELE_MAX_PADS; i++)
|
||||
if (pads[i] && SDL_GetGamepadID(pads[i]) == id)
|
||||
return (int8_t)i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int8_t free_slot(void)
|
||||
{
|
||||
uint8_t i;
|
||||
for (i = 0; i < SKELE_MAX_PADS; i++)
|
||||
if (!pads[i])
|
||||
return (int8_t)i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void update_axes(uint8_t slot)
|
||||
{
|
||||
pad_axes[slot][SKELE_PAD_AXIS_LX] =
|
||||
SDL_GetGamepadAxis(pads[slot], SDL_GAMEPAD_AXIS_LEFTX) / 32767.0f;
|
||||
pad_axes[slot][SKELE_PAD_AXIS_LY] =
|
||||
SDL_GetGamepadAxis(pads[slot], SDL_GAMEPAD_AXIS_LEFTY) / 32767.0f;
|
||||
pad_axes[slot][SKELE_PAD_AXIS_RX] =
|
||||
SDL_GetGamepadAxis(pads[slot], SDL_GAMEPAD_AXIS_RIGHTX) / 32767.0f;
|
||||
pad_axes[slot][SKELE_PAD_AXIS_RY] =
|
||||
SDL_GetGamepadAxis(pads[slot], SDL_GAMEPAD_AXIS_RIGHTY) / 32767.0f;
|
||||
pad_axes[slot][SKELE_PAD_AXIS_LT] =
|
||||
SDL_GetGamepadAxis(pads[slot], SDL_GAMEPAD_AXIS_LEFT_TRIGGER) /
|
||||
32767.0f;
|
||||
pad_axes[slot][SKELE_PAD_AXIS_RT] =
|
||||
SDL_GetGamepadAxis(pads[slot], SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) /
|
||||
32767.0f;
|
||||
}
|
||||
|
||||
uint8_t skele_input_poll(void)
|
||||
{
|
||||
SDL_Event e;
|
||||
skele_key_t key;
|
||||
int8_t slot;
|
||||
uint8_t i;
|
||||
skele_pad_button_t btn;
|
||||
|
||||
memset(key_down, 0, sizeof(key_down));
|
||||
memset(pad_btn_down, 0, sizeof(pad_btn_down));
|
||||
mouse_dx = 0;
|
||||
mouse_dy = 0;
|
||||
|
||||
while (SDL_PollEvent(&e)) {
|
||||
switch (e.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
return 0;
|
||||
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
mouse_dx += (int32_t)e.motion.xrel;
|
||||
mouse_dy += (int32_t)e.motion.yrel;
|
||||
break;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
if (e.key.repeat)
|
||||
break;
|
||||
switch (e.key.scancode) {
|
||||
case SDL_SCANCODE_F11:
|
||||
skele_video_toggle_fullscreen();
|
||||
break;
|
||||
case SDL_SCANCODE_F4:
|
||||
skele_video_cycle_scale();
|
||||
break;
|
||||
default:
|
||||
key = scancode_to_key(e.key.scancode);
|
||||
if (key != SKELE_KEY_UNKNOWN) {
|
||||
key_down[key] = 1;
|
||||
key_held[key] = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_KEY_UP:
|
||||
key = scancode_to_key(e.key.scancode);
|
||||
if (key != SKELE_KEY_UNKNOWN)
|
||||
key_held[key] = 0;
|
||||
break;
|
||||
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
slot = free_slot();
|
||||
if (slot >= 0) {
|
||||
pads[slot] = SDL_OpenGamepad(e.gdevice.which);
|
||||
if (pads[slot])
|
||||
memset(pad_btn_held[slot], 0,
|
||||
sizeof(pad_btn_held[slot]));
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
slot = pad_slot(e.gdevice.which);
|
||||
if (slot >= 0) {
|
||||
SDL_CloseGamepad(pads[slot]);
|
||||
pads[slot] = NULL;
|
||||
memset(pad_btn_held[slot], 0,
|
||||
sizeof(pad_btn_held[slot]));
|
||||
memset(pad_axes[slot], 0,
|
||||
sizeof(pad_axes[slot]));
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
slot = pad_slot(e.gbutton.which);
|
||||
btn =
|
||||
sdl_btn_to_pad((SDL_GamepadButton)e.gbutton.button);
|
||||
if (slot >= 0 && btn != SKELE_PAD_BUTTON_COUNT) {
|
||||
pad_btn_down[slot][btn] = 1;
|
||||
pad_btn_held[slot][btn] = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
slot = pad_slot(e.gbutton.which);
|
||||
btn =
|
||||
sdl_btn_to_pad((SDL_GamepadButton)e.gbutton.button);
|
||||
if (slot >= 0 && btn != SKELE_PAD_BUTTON_COUNT)
|
||||
pad_btn_held[slot][btn] = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < SKELE_MAX_PADS; i++)
|
||||
if (pads[i])
|
||||
update_axes(i);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t skele_key_down(skele_key_t key)
|
||||
{
|
||||
return key < SKELE_KEY_COUNT ? key_down[key] : 0;
|
||||
}
|
||||
|
||||
uint8_t skele_key_held(skele_key_t key)
|
||||
{
|
||||
return key < SKELE_KEY_COUNT ? key_held[key] : 0;
|
||||
}
|
||||
|
||||
uint8_t skele_pad_connected(uint8_t pad)
|
||||
{
|
||||
return pad < SKELE_MAX_PADS ? pads[pad] != NULL : 0;
|
||||
}
|
||||
|
||||
uint8_t skele_pad_button_down(uint8_t pad, skele_pad_button_t btn)
|
||||
{
|
||||
return (pad < SKELE_MAX_PADS && btn < SKELE_PAD_BUTTON_COUNT)
|
||||
? pad_btn_down[pad][btn]
|
||||
: 0;
|
||||
}
|
||||
|
||||
uint8_t skele_pad_button_held(uint8_t pad, skele_pad_button_t btn)
|
||||
{
|
||||
return (pad < SKELE_MAX_PADS && btn < SKELE_PAD_BUTTON_COUNT)
|
||||
? pad_btn_held[pad][btn]
|
||||
: 0;
|
||||
}
|
||||
|
||||
float skele_pad_axis(uint8_t pad, skele_pad_axis_t axis)
|
||||
{
|
||||
return (pad < SKELE_MAX_PADS && axis < SKELE_PAD_AXIS_COUNT)
|
||||
? pad_axes[pad][axis]
|
||||
: 0.0f;
|
||||
}
|
||||
|
||||
void skele_mouse_delta(int32_t *dx, int32_t *dy)
|
||||
{
|
||||
if (dx)
|
||||
*dx = mouse_dx;
|
||||
if (dy)
|
||||
*dy = mouse_dy;
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
#include "client/video.h"
|
||||
#include "skele.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stk/stk_log.h>
|
||||
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_GLContext gl_ctx = NULL;
|
||||
static uint16_t vid_w = 0;
|
||||
static uint16_t vid_h = 0;
|
||||
static uint8_t cur_scale = 1;
|
||||
static uint8_t fullscreen = 0;
|
||||
|
||||
static uint8_t max_scale(uint16_t rw, uint16_t rh)
|
||||
{
|
||||
SDL_DisplayID display;
|
||||
const SDL_DisplayMode *mode;
|
||||
uint16_t aw, ah;
|
||||
uint8_t scale;
|
||||
|
||||
display =
|
||||
window ? SDL_GetDisplayForWindow(window) : SDL_GetPrimaryDisplay();
|
||||
mode = SDL_GetCurrentDisplayMode(display);
|
||||
if (!mode)
|
||||
return 1;
|
||||
|
||||
aw = (uint16_t)((mode->w * 3) / 4);
|
||||
ah = (uint16_t)((mode->h * 3) / 4);
|
||||
scale = 1;
|
||||
while ((rw * (scale + 1)) <= aw && (rh * (scale + 1)) <= ah)
|
||||
scale++;
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
static void set_scale(uint8_t scale)
|
||||
{
|
||||
SDL_DisplayID display;
|
||||
cur_scale = scale;
|
||||
display = SDL_GetDisplayForWindow(window);
|
||||
SDL_SetWindowSize(window, (int)(vid_w * scale), (int)(vid_h * scale));
|
||||
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(display),
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display));
|
||||
}
|
||||
|
||||
static void toggle_fullscreen(void)
|
||||
{
|
||||
fullscreen = !fullscreen;
|
||||
SDL_SetWindowFullscreen(window, fullscreen);
|
||||
}
|
||||
|
||||
static void cycle_scale(void)
|
||||
{
|
||||
uint8_t next;
|
||||
if (fullscreen)
|
||||
return;
|
||||
next = cur_scale + 1;
|
||||
if (next > max_scale(vid_w, vid_h))
|
||||
next = 1;
|
||||
set_scale(next);
|
||||
}
|
||||
|
||||
uint8_t skele_video_init(skele_video_config_t cfg)
|
||||
{
|
||||
SDL_DisplayID display;
|
||||
SDL_WindowFlags flags = 0;
|
||||
const SDL_DisplayMode *mode;
|
||||
uint16_t window_w, window_h, aw, ah;
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) {
|
||||
stk_log(STK_LOG_ERROR, "video: SDL_Init failed: %s",
|
||||
SDL_GetError());
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, GL_MAJOR);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, GL_MINOR);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
|
||||
SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
flags |= SDL_WINDOW_OPENGL;
|
||||
if (cfg.flags & SKELE_VIDEO_FULLSCREEN)
|
||||
flags |= SDL_WINDOW_FULLSCREEN;
|
||||
if (cfg.flags & SKELE_VIDEO_BORDERLESS)
|
||||
flags |= SDL_WINDOW_BORDERLESS;
|
||||
if (cfg.flags & SKELE_VIDEO_RESIZABLE)
|
||||
flags |= SDL_WINDOW_RESIZABLE;
|
||||
if (cfg.flags & SKELE_VIDEO_HIGHDPI)
|
||||
flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||
|
||||
display = SDL_GetPrimaryDisplay();
|
||||
vid_w =
|
||||
cfg.render_width ? cfg.render_width : SKELE_DEFAULT_RENDER_WIDTH;
|
||||
vid_h =
|
||||
cfg.render_height ? cfg.render_height : SKELE_DEFAULT_RENDER_HEIGHT;
|
||||
|
||||
if (cfg.window_width && cfg.window_height) {
|
||||
window_w = cfg.window_width;
|
||||
window_h = cfg.window_height;
|
||||
cur_scale = 1;
|
||||
} else {
|
||||
mode = SDL_GetCurrentDisplayMode(display);
|
||||
if (!mode) {
|
||||
stk_log(STK_LOG_ERROR,
|
||||
"video: SDL_GetCurrentDisplayMode failed: %s",
|
||||
SDL_GetError());
|
||||
SDL_Quit();
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
aw = (uint16_t)((mode->w * 3) / 4);
|
||||
ah = (uint16_t)((mode->h * 3) / 4);
|
||||
cur_scale = 1;
|
||||
while ((vid_w * (cur_scale + 1)) <= aw &&
|
||||
(vid_h * (cur_scale + 1)) <= ah)
|
||||
cur_scale++;
|
||||
|
||||
window_w = vid_w * cur_scale;
|
||||
window_h = vid_h * cur_scale;
|
||||
}
|
||||
|
||||
window = SDL_CreateWindow("skele", window_w, window_h,
|
||||
flags | SDL_WINDOW_HIDDEN);
|
||||
if (!window) {
|
||||
stk_log(STK_LOG_ERROR, "video: SDL_CreateWindow failed: %s",
|
||||
SDL_GetError());
|
||||
SDL_Quit();
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(display),
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display));
|
||||
|
||||
if (cfg.flags & SKELE_VIDEO_FULLSCREEN)
|
||||
fullscreen = 1;
|
||||
|
||||
gl_ctx = SDL_GL_CreateContext(window);
|
||||
if (!gl_ctx) {
|
||||
stk_log(STK_LOG_ERROR, "video: SDL_GL_CreateContext failed: %s",
|
||||
SDL_GetError());
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
SDL_ShowWindow(window);
|
||||
return SKELE_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void skele_video_shutdown(void)
|
||||
{
|
||||
if (gl_ctx)
|
||||
SDL_GL_DestroyContext(gl_ctx);
|
||||
if (window)
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
gl_ctx = NULL;
|
||||
window = NULL;
|
||||
vid_w = 0;
|
||||
vid_h = 0;
|
||||
cur_scale = 1;
|
||||
fullscreen = 0;
|
||||
}
|
||||
|
||||
void skele_video_present(void) { SDL_GL_SwapWindow(window); }
|
||||
void skele_video_toggle_fullscreen(void) { toggle_fullscreen(); }
|
||||
void skele_video_cycle_scale(void) { cycle_scale(); }
|
||||
|
||||
void skele_video_set_mouse_grab(uint8_t grab)
|
||||
{
|
||||
SDL_SetWindowMouseGrab(window, grab ? true : false);
|
||||
SDL_SetWindowRelativeMouseMode(window, grab ? true : false);
|
||||
}
|
||||
|
||||
void skele_video_set_title(const char *title)
|
||||
{
|
||||
SDL_SetWindowTitle(window, title);
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
#include "client/blit.h"
|
||||
#include "client/palette.h"
|
||||
#include "client/video.h"
|
||||
#include "skele.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdint.h>
|
||||
#include <stk/stk_log.h>
|
||||
#include <string.h>
|
||||
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
static SDL_Texture *texture = NULL;
|
||||
static SDL_Palette *sdl_pal = NULL;
|
||||
static uint16_t vid_w = 0;
|
||||
static uint16_t vid_h = 0;
|
||||
static uint32_t vid_total = 0;
|
||||
static uint8_t cur_scale = 1;
|
||||
static uint8_t fullscreen = 0;
|
||||
|
||||
static uint8_t max_scale(uint16_t rw, uint16_t rh)
|
||||
{
|
||||
SDL_DisplayID display;
|
||||
const SDL_DisplayMode *mode;
|
||||
uint16_t aw, ah;
|
||||
uint8_t scale;
|
||||
|
||||
display =
|
||||
window ? SDL_GetDisplayForWindow(window) : SDL_GetPrimaryDisplay();
|
||||
mode = SDL_GetCurrentDisplayMode(display);
|
||||
if (!mode)
|
||||
return 1;
|
||||
|
||||
aw = (uint16_t)((mode->w * 3) / 4);
|
||||
ah = (uint16_t)((mode->h * 3) / 4);
|
||||
scale = 1;
|
||||
while ((rw * (scale + 1)) <= aw && (rh * (scale + 1)) <= ah)
|
||||
scale++;
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
static void set_scale(uint8_t scale)
|
||||
{
|
||||
SDL_DisplayID display;
|
||||
cur_scale = scale;
|
||||
display = SDL_GetDisplayForWindow(window);
|
||||
SDL_SetWindowSize(window, (int)(vid_w * scale), (int)(vid_h * scale));
|
||||
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(display),
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display));
|
||||
}
|
||||
|
||||
static void toggle_fullscreen(void)
|
||||
{
|
||||
fullscreen = !fullscreen;
|
||||
SDL_SetWindowFullscreen(window, fullscreen);
|
||||
}
|
||||
|
||||
static void cycle_scale(void)
|
||||
{
|
||||
uint8_t next;
|
||||
if (fullscreen)
|
||||
return;
|
||||
next = cur_scale + 1;
|
||||
if (next > max_scale(vid_w, vid_h))
|
||||
next = 1;
|
||||
set_scale(next);
|
||||
}
|
||||
|
||||
uint8_t skele_video_init(skele_video_config_t cfg)
|
||||
{
|
||||
SDL_DisplayID display;
|
||||
SDL_WindowFlags flags = 0;
|
||||
const SDL_DisplayMode *mode;
|
||||
uint16_t window_w, window_h, aw, ah;
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) {
|
||||
stk_log(STK_LOG_ERROR, "video: SDL_Init failed: %s",
|
||||
SDL_GetError());
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
if (cfg.flags & SKELE_VIDEO_FULLSCREEN)
|
||||
flags |= SDL_WINDOW_FULLSCREEN;
|
||||
if (cfg.flags & SKELE_VIDEO_BORDERLESS)
|
||||
flags |= SDL_WINDOW_BORDERLESS;
|
||||
if (cfg.flags & SKELE_VIDEO_RESIZABLE)
|
||||
flags |= SDL_WINDOW_RESIZABLE;
|
||||
if (cfg.flags & SKELE_VIDEO_HIGHDPI)
|
||||
flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;
|
||||
|
||||
display = SDL_GetPrimaryDisplay();
|
||||
vid_w =
|
||||
cfg.render_width ? cfg.render_width : SKELE_DEFAULT_RENDER_WIDTH;
|
||||
vid_h =
|
||||
cfg.render_height ? cfg.render_height : SKELE_DEFAULT_RENDER_HEIGHT;
|
||||
vid_total = (uint32_t)(vid_w * vid_h);
|
||||
|
||||
if (cfg.window_width && cfg.window_height) {
|
||||
window_w = cfg.window_width;
|
||||
window_h = cfg.window_height;
|
||||
cur_scale = 1;
|
||||
} else {
|
||||
mode = SDL_GetCurrentDisplayMode(display);
|
||||
if (!mode) {
|
||||
stk_log(STK_LOG_ERROR,
|
||||
"video: SDL_GetCurrentDisplayMode failed: %s",
|
||||
SDL_GetError());
|
||||
SDL_Quit();
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
aw = (uint16_t)((mode->w * 3) / 4);
|
||||
ah = (uint16_t)((mode->h * 3) / 4);
|
||||
cur_scale = 1;
|
||||
while ((vid_w * (cur_scale + 1)) <= aw &&
|
||||
(vid_h * (cur_scale + 1)) <= ah)
|
||||
cur_scale++;
|
||||
|
||||
window_w = vid_w * cur_scale;
|
||||
window_h = vid_h * cur_scale;
|
||||
}
|
||||
|
||||
window = SDL_CreateWindow("skele", window_w, window_h,
|
||||
flags | SDL_WINDOW_HIDDEN);
|
||||
if (!window) {
|
||||
stk_log(STK_LOG_ERROR, "video: SDL_CreateWindow failed: %s",
|
||||
SDL_GetError());
|
||||
SDL_Quit();
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(display),
|
||||
SDL_WINDOWPOS_CENTERED_DISPLAY(display));
|
||||
|
||||
if (cfg.flags & SKELE_VIDEO_FULLSCREEN)
|
||||
fullscreen = 1;
|
||||
|
||||
renderer = SDL_CreateRenderer(window, NULL);
|
||||
if (!renderer) {
|
||||
stk_log(STK_LOG_ERROR, "video: SDL_CreateRenderer failed: %s",
|
||||
SDL_GetError());
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8,
|
||||
SDL_TEXTUREACCESS_STREAMING, vid_w, vid_h);
|
||||
if (!texture) {
|
||||
stk_log(STK_LOG_ERROR, "video: SDL_CreateTexture failed: %s",
|
||||
SDL_GetError());
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
sdl_pal = SDL_CreatePalette(SKELE_PALETTE_COLORS);
|
||||
if (!sdl_pal) {
|
||||
stk_log(STK_LOG_ERROR, "video: SDL_CreatePalette failed: %s",
|
||||
SDL_GetError());
|
||||
SDL_DestroyTexture(texture);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
SDL_SetTexturePalette(texture, sdl_pal);
|
||||
|
||||
{
|
||||
SDL_Color black[SKELE_PALETTE_COLORS];
|
||||
uint16_t i;
|
||||
memset(black, 0, sizeof(black));
|
||||
for (i = 0; i < SKELE_PALETTE_COLORS; i++)
|
||||
black[i].a = 255;
|
||||
SDL_SetPaletteColors(sdl_pal, black, 0, SKELE_PALETTE_COLORS);
|
||||
}
|
||||
|
||||
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
|
||||
SDL_SetRenderVSync(renderer, 1);
|
||||
SDL_ShowWindow(window);
|
||||
return SKELE_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void skele_video_shutdown(void)
|
||||
{
|
||||
if (sdl_pal)
|
||||
SDL_DestroyPalette(sdl_pal);
|
||||
if (texture)
|
||||
SDL_DestroyTexture(texture);
|
||||
if (renderer)
|
||||
SDL_DestroyRenderer(renderer);
|
||||
if (window)
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
sdl_pal = NULL;
|
||||
texture = NULL;
|
||||
renderer = NULL;
|
||||
window = NULL;
|
||||
vid_w = 0;
|
||||
vid_h = 0;
|
||||
vid_total = 0;
|
||||
cur_scale = 1;
|
||||
fullscreen = 0;
|
||||
}
|
||||
|
||||
void skele_video_present(void) { SDL_RenderPresent(renderer); }
|
||||
void skele_video_toggle_fullscreen(void) { toggle_fullscreen(); }
|
||||
void skele_video_cycle_scale(void) { cycle_scale(); }
|
||||
|
||||
void skele_video_set_mouse_grab(uint8_t grab)
|
||||
{
|
||||
SDL_SetWindowMouseGrab(window, grab ? true : false);
|
||||
SDL_SetWindowRelativeMouseMode(window, grab ? true : false);
|
||||
}
|
||||
|
||||
void skele_palette_set(skele_palette_t pal)
|
||||
{
|
||||
SDL_Color colors[SKELE_PALETTE_COLORS];
|
||||
uint16_t i;
|
||||
uint32_t c;
|
||||
|
||||
for (i = 0; i < SKELE_PALETTE_COLORS; i++) {
|
||||
c = pal[i];
|
||||
colors[i].r = (uint8_t)(c >> 16);
|
||||
colors[i].g = (uint8_t)(c >> 8);
|
||||
colors[i].b = (uint8_t)(c);
|
||||
colors[i].a = 255;
|
||||
}
|
||||
SDL_SetPaletteColors(sdl_pal, colors, 0, SKELE_PALETTE_COLORS);
|
||||
}
|
||||
|
||||
void skele_palette_set_index(uint8_t index, uint32_t color)
|
||||
{
|
||||
SDL_Color c;
|
||||
c.r = (uint8_t)(color >> 16);
|
||||
c.g = (uint8_t)(color >> 8);
|
||||
c.b = (uint8_t)(color);
|
||||
c.a = 255;
|
||||
SDL_SetPaletteColors(sdl_pal, &c, index, 1);
|
||||
}
|
||||
|
||||
void skele_video_blit(uint8_t *pixels)
|
||||
{
|
||||
SDL_UpdateTexture(texture, NULL, pixels, vid_w);
|
||||
SDL_RenderTexture(renderer, texture, NULL, NULL);
|
||||
}
|
||||
|
||||
void skele_video_set_title(const char *title)
|
||||
{
|
||||
SDL_SetWindowTitle(window, title);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#define _POSIX_C_SOURCE 199309L
|
||||
#include "clock.h"
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
static void (*signal_cb)(void) = NULL;
|
||||
|
||||
static void handle_signal(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
if (signal_cb)
|
||||
signal_cb();
|
||||
}
|
||||
|
||||
void skele_clock_init(void (*on_signal)(void))
|
||||
{
|
||||
signal_cb = on_signal;
|
||||
signal(SIGINT, handle_signal);
|
||||
signal(SIGTERM, handle_signal);
|
||||
}
|
||||
|
||||
uint64_t skele_time_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t)(ts.tv_sec * 1000000000ULL + ts.tv_nsec);
|
||||
}
|
||||
|
||||
void skele_sleep_ns(uint64_t ns)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = (time_t)(ns / 1000000000ULL);
|
||||
ts.tv_nsec = (long)(ns % 1000000000ULL);
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "clock.h"
|
||||
#include <windows.h>
|
||||
|
||||
static void (*signal_cb)(void) = NULL;
|
||||
static LARGE_INTEGER perf_freq;
|
||||
|
||||
static BOOL WINAPI handle_ctrl(DWORD type)
|
||||
{
|
||||
(void)type;
|
||||
if (signal_cb)
|
||||
signal_cb();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void skele_clock_init(void (*on_signal)(void))
|
||||
{
|
||||
signal_cb = on_signal;
|
||||
QueryPerformanceFrequency(&perf_freq);
|
||||
SetConsoleCtrlHandler(handle_ctrl, TRUE);
|
||||
}
|
||||
|
||||
uint64_t skele_time_ns(void)
|
||||
{
|
||||
LARGE_INTEGER cnt;
|
||||
QueryPerformanceCounter(&cnt);
|
||||
return (uint64_t)(cnt.QuadPart * 1000000000ULL / perf_freq.QuadPart);
|
||||
}
|
||||
|
||||
void skele_sleep_ns(uint64_t ns)
|
||||
{
|
||||
HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL);
|
||||
LARGE_INTEGER li;
|
||||
li.QuadPart = -(LONGLONG)(ns / 100);
|
||||
SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE);
|
||||
WaitForSingleObject(timer, INFINITE);
|
||||
CloseHandle(timer);
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
#include "skele.h"
|
||||
#include <stk/stk.h>
|
||||
#include <stk/stk_log.h>
|
||||
|
||||
uint64_t skele_tick_ns = 1000000000ULL / SKELE_DEFAULT_TICK_RATE;
|
||||
|
||||
static uint8_t stk_initialized = 0;
|
||||
|
||||
const char *skele_version(void) { return "0.0.0"; }
|
||||
|
||||
void skele_set_tick_rate(uint8_t rate)
|
||||
{
|
||||
skele_tick_ns = 1000000000ULL / (rate ? rate : SKELE_DEFAULT_TICK_RATE);
|
||||
}
|
||||
|
||||
uint8_t skele_init(void) { return SKELE_INIT_SUCCESS; }
|
||||
|
||||
void skele_shutdown(void) {}
|
||||
|
||||
void skele_tick(void) {}
|
||||
|
||||
uint8_t skele_stk_setup(void)
|
||||
{
|
||||
if (stk_initialized)
|
||||
return SKELE_INIT_SUCCESS;
|
||||
|
||||
stk_set_log_prefix("skele");
|
||||
stk_set_module_init_fn("skele_mod_init");
|
||||
stk_set_module_shutdown_fn("skele_mod_shutdown");
|
||||
stk_set_module_name_fn("skele_mod_name");
|
||||
stk_set_module_version_fn("skele_mod_ver");
|
||||
stk_set_module_description_fn("skele_mod_desc");
|
||||
stk_set_module_deps_sym("skele_mod_deps");
|
||||
|
||||
if (stk_init() != STK_INIT_SUCCESS) {
|
||||
stk_log(STK_LOG_ERROR, "failed to initialize stk");
|
||||
return SKELE_INIT_FAILURE;
|
||||
}
|
||||
|
||||
stk_initialized = 1;
|
||||
stk_log(STK_LOG_INFO, "skele v%s", SKELE_VERSION);
|
||||
return SKELE_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void skele_stk_teardown(void)
|
||||
{
|
||||
if (!stk_initialized)
|
||||
return;
|
||||
stk_shutdown();
|
||||
stk_initialized = 0;
|
||||
}
|
||||
Reference in New Issue
Block a user