/* ** Copyright (c) 2022 KikooDX ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to ** deal in the Software without restriction, including without limitation the ** rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ** sell copies of the Software, and to permit persons to whom the Software is ** furnished to do so, subject to the following conditions: ** ** The above copyright notice and this permission notice shall be included in ** all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ** IN THE SOFTWARE. */ #ifndef LZY_H_ #define LZY_H_ #ifdef __cplusplus extern "C" { #endif #include #define LZY_UNUSED(x) (void)(x) int LZY_Init(const char *title, int target_fps); void LZY_Quit(void); int LZY_DrawBegin(void); int LZY_DrawEnd(void); void LZY_DrawSetColor(uint8_t r, uint8_t g, uint8_t b); void LZY_DrawSetColorNone(void); int LZY_DrawClear(void); int LZY_DrawRect(int x, int y, unsigned int w, unsigned int h); int LZY_DrawFillRect(int x, int y, unsigned int w, unsigned int h); void LZY_CycleEvents(void); int LZY_KeyDown(unsigned int key); int LZY_ShouldQuit(void); void LZY_Log(const char *msg); const char *LZY_GetError(void); #ifdef FXCG50 #include enum LZY_ScanCode { LZYK_LEFT = KEY_LEFT, LZYK_RIGHT = KEY_RIGHT, LZYK_UP = KEY_UP, LZYK_DOWN = KEY_DOWN, }; #else /* end FXCG50, begin SDL2 */ #include enum LZY_ScanCode { LZYK_LEFT, LZYK_RIGHT, LZYK_UP, LZYK_DOWN, LZYK_COUNT, }; #endif /* SDL2 */ #ifdef __cplusplus } #endif #endif /* LZY_H_ */ /* implementation */ #ifdef LZY_IMPLEMENTATION #undef LZY_IMPLEMENTATION #ifndef LZY_TILE_SIZE #define LZY_TILE_SIZE 16 #endif #ifndef LZY_DISPLAY_WIDTH #define LZY_DISPLAY_WIDTH 396 #endif #ifndef LZY_DISPLAY_HEIGHT #define LZY_DISPLAY_HEIGHT 224 #endif #ifdef FXCG50 #include #include #include #include #include static color_t draw_color = C_BLACK; static int should_quit = 0; static int timer = 0; static volatile int has_ticked = 0; static int timer_callback(volatile int *); int LZY_Init(const char *title, int target_fps) { LZY_UNUSED(title); if (target_fps > 0) { timer = timer_configure(TIMER_ANY, 1000000 / target_fps, GINT_CALL(timer_callback, &has_ticked)); timer_start(timer); } return 0; } static int timer_callback(volatile int *arg) { *arg += 1; return 0; } void LZY_Quit(void) { if (timer) { timer_stop(timer); timer = 0; } } int LZY_DrawBegin(void) { return 0; } int LZY_DrawEnd(void) { dupdate(); while (timer && !has_ticked) sleep(); has_ticked = 0; return 0; } void LZY_DrawSetColor(uint8_t r, uint8_t g, uint8_t b) { draw_color = C_RGB(r / 8, g / 8, b / 8); } void LZY_DrawSetColorNone(void) { draw_color = C_NONE; } int LZY_DrawClear(void) { dclear(draw_color); return 0; } int LZY_DrawRect(int x, int y, unsigned int w, unsigned int h) { if (w < 1 || h < 1) return -1; drect_border(x, y, x + w - 1, y + h - 1, C_NONE, 1, draw_color); return 0; } int LZY_DrawFillRect(int x, int y, unsigned int w, unsigned int h) { if (w < 1 || h < 1) return -1; drect(x, y, x + w - 1, y + h - 1, draw_color); return 0; } void LZY_CycleEvents(void) { clearevents(); should_quit = should_quit || keydown(KEY_EXIT); } int LZY_KeyDown(unsigned int key) { return keydown(key); } int LZY_ShouldQuit(void) { return should_quit; } void LZY_Log(const char *msg) { LZY_UNUSED(msg); } const char *LZY_GetError(void) { return NULL; } #else /* end FXCG50, begin SDL2 */ #include #include static const char *error = NULL; static SDL_Window *window = NULL; static SDL_Renderer *renderer = NULL; static SDL_Texture *target = NULL; static int should_quit = 0; static int input[LZYK_COUNT] = {0}; static int fps_limiter = 0; static uint64_t min_dt; static uint64_t next_time; int LZY_Init(const char *title, int target_fps) { if (SDL_Init(SDL_INIT_VIDEO) < 0) { error = SDL_GetError(); return -1; } SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 0); window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, LZY_DISPLAY_WIDTH, LZY_DISPLAY_HEIGHT, SDL_WINDOW_RESIZABLE); if (window == NULL) { error = SDL_GetError(); return -2; } SDL_SetWindowMinimumSize(window, LZY_DISPLAY_WIDTH, LZY_DISPLAY_HEIGHT); renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); if (renderer == NULL) { error = SDL_GetError(); return -3; } target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_TARGET, LZY_DISPLAY_WIDTH, LZY_DISPLAY_HEIGHT); if (target == NULL) { error = SDL_GetError(); return -4; } fps_limiter = target_fps > 0; if (fps_limiter) { min_dt = 1000 / target_fps; next_time = SDL_GetTicks64(); } return 0; } void LZY_Quit(void) { if (target != NULL) { SDL_DestroyTexture(target); target = NULL; } if (renderer != NULL) { SDL_DestroyRenderer(renderer); renderer = NULL; } if (window != NULL) { SDL_DestroyWindow(window); window = NULL; } SDL_Quit(); } int LZY_DrawBegin(void) { if (fps_limiter) next_time += min_dt; if (SDL_SetRenderTarget(renderer, target) < 0) { error = SDL_GetError(); return -1; } return 0; } int LZY_DrawEnd(void) { int win_w, win_h, off_x, off_y; float ratio_w, ratio_h, scale; SDL_Rect src, dst; if (SDL_SetRenderTarget(renderer, NULL)) { error = SDL_GetError(); return -1; } LZY_DrawSetColor(0x00, 0x00, 0x00); if (LZY_DrawClear()) return -2; SDL_GetWindowSize(window, &win_w, &win_h); ratio_w = (float)win_w / LZY_DISPLAY_WIDTH; ratio_h = (float)win_h / LZY_DISPLAY_HEIGHT; scale = (ratio_w < ratio_h) ? (ratio_w) : (ratio_h); off_x = (win_w - LZY_DISPLAY_WIDTH * scale) / 2; off_y = (win_h - LZY_DISPLAY_HEIGHT * scale) / 2; src.x = 0; src.y = 0; src.w = LZY_DISPLAY_WIDTH; src.h = LZY_DISPLAY_HEIGHT; dst.x = off_x; dst.y = off_y; dst.w = LZY_DISPLAY_WIDTH * scale; dst.h = LZY_DISPLAY_HEIGHT * scale; if (SDL_RenderCopy(renderer, target, &src, &dst) < 0) { error = SDL_GetError(); return -3; } SDL_RenderPresent(renderer); if (fps_limiter) { const uint64_t cur_time = SDL_GetTicks64(); if (next_time <= cur_time) next_time = cur_time; else SDL_Delay(next_time - cur_time); } return 0; } void LZY_DrawSetColor(uint8_t r, uint8_t g, uint8_t b) { SDL_SetRenderDrawColor(renderer, r, g, b, 0xff); } void LZY_DrawSetColorNone(void) { SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00); } int LZY_DrawClear(void) { if (SDL_RenderClear(renderer) < 0) { error = SDL_GetError(); return -1; } return 0; } int LZY_DrawRect(int x, int y, unsigned int w, unsigned int h) { const SDL_Rect rect = {x, y, w, h}; if (w == 0 || h == 0) { error = "rectangle dimensions cannot be 0"; return -1; } if (SDL_RenderDrawRect(renderer, &rect) < 0) { error = SDL_GetError(); return -2; } return 0; } int LZY_DrawFillRect(int x, int y, unsigned int w, unsigned int h) { const SDL_Rect rect = {x, y, w, h}; if (w == 0 || h == 0) { error = "rectangle dimensions cannot be 0"; return -1; } if (SDL_RenderFillRect(renderer, &rect) < 0) { error = SDL_GetError(); return -2; } return 0; } void LZY_CycleEvents(void) { static const SDL_Scancode sc[LZYK_COUNT] = { SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, }; SDL_Event e; while (SDL_PollEvent(&e) != 0) { switch (e.type) { case SDL_QUIT: should_quit = 1; break; case SDL_KEYDOWN: { int i = LZYK_COUNT; while (i-- > 0) { if (sc[i] == e.key.keysym.scancode) { input[i] = 1; break; } } } break; case SDL_KEYUP: { int i = LZYK_COUNT; while (i-- > 0) { if (sc[i] == e.key.keysym.scancode) { input[i] = 0; break; } } } break; default: break; } } } int LZY_KeyDown(unsigned int key) { if (key >= LZYK_COUNT) return 0; return input[key]; } int LZY_ShouldQuit(void) { return should_quit; } void LZY_Log(const char *msg) { SDL_Log(msg); } const char *LZY_GetError(void) { return error; } #endif /* SDL2 */ #endif /* LZY_IMPLEMENTATION */