#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "TZR.h" #include /* sources/drain.h */ static void *drain_error(void) { perror("TZR_LoadResourceTyped:drain"); return NULL; } static char *drain(FILE *fp, size_t *size) { if (fseek(fp, 0, SEEK_END) < 0) return drain_error(); *size = ftell(fp); if (fseek(fp, 0, SEEK_SET) < 0) return drain_error(); char *data = malloc(*size); if (data == NULL) return drain_error(); if (fread(data, 1, *size, fp) != *size) return free(data), drain_error(); return data; } /* sources/sdl_error.h */ static int sdl_error(int rv) { SDL_Log("%s\n", SDL_GetError()); return rv; } /* sources/sdl_rect_to_rect.h */ static void TZR_RectFromSDLRect(TZR_Rect *dest, const SDL_Rect *src) { dest->from.x = src->x; dest->from.y = src->y; dest->to.x = src->x + src->w - 1; dest->to.y = src->y + src->h - 1; } /* sources/globals.c */ TZR_Config ___tzr_config = {0}; TZR_Color ___tzr_color = {0, 0.0f, 0.0f, 0.0f, 1.0f}; TZR_Resource *___tzr_resources = NULL; size_t ___tzr_resources_capacity = 0; size_t ___tzr_resources_size = 0; SDL_Window *___tzr_window = NULL; SDL_Renderer *___tzr_renderer = NULL; SDL_Texture *___tzr_target = NULL; unsigned long ___tzr_tick = 0; unsigned long ___tzr_next_time = 0; unsigned long ___tzr_min_dt = 0; int ___tzr_should_quit = 0; int ___tzr_mouse_x = 0; int ___tzr_mouse_y = 0; const char *___tzr_command[___TZR_RES_COUNT] = {0}; TZR_KeyState ___tzr_keystates[SDL_NUM_SCANCODES] = {0}; /* sources/TZR_CycleEvents.c */ void TZR_CycleEvents(void) { TZR_ResourcesWatch(); for (int i = 0; i < SDL_NUM_SCANCODES; i++) { TZR_KeyState *const keystate = &___tzr_keystates[i]; switch (*keystate) { case TZR_KEYSTATE_RELEASE: case TZR_KEYSTATE_UP: *keystate = TZR_KEYSTATE_UP; break; case TZR_KEYSTATE_PRESS: case TZR_KEYSTATE_DOWN: *keystate = TZR_KEYSTATE_DOWN; break; default: break; } } TZR_Event e; while (TZR_PollEvent(&e)) ; } /* sources/TZR_DestroyResource.c */ void TZR_DestroyResource(TZR_Resource *res, int free_path) { if (res->path != NULL && free_path) free(res->path); switch (res->type) { case TZR_RES_RAW: if (res->raw.data != NULL) free(res->raw.data); break; case TZR_RES_IMAGE: if (res->image.ptr != NULL) SDL_DestroyTexture(res->image.ptr); break; default: fprintf(stderr, "unknown resource type %u\n", res->type); break; } } /* sources/TZR_DirectResourceLoad.c */ int TZR_DirectResourceLoad(TZR_Resource *res, const void *data, int size) { switch (res->type) { case TZR_RES_RAW: res->raw.size = size; res->raw.data = malloc(size); if (res->raw.data == NULL) { perror("TZR_DirectResourceLoad"); return -1; } memcpy(res->raw.data, data, size); break; case TZR_RES_IMAGE: { SDL_RWops *const rw = SDL_RWFromConstMem(data, size); if (rw == NULL) return sdl_error(-1); SDL_Surface *const surf = IMG_Load_RW(rw, 0); SDL_RWclose(rw); if (surf == NULL) return sdl_error(-1); SDL_Texture *const tex = SDL_CreateTextureFromSurface(___tzr_renderer, surf); SDL_FreeSurface(surf); if (tex == NULL) return sdl_error(-1); if (SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND) < 0) { SDL_DestroyTexture(tex); return sdl_error(-1); } if (SDL_QueryTexture(tex, NULL, NULL, &res->image.width, &res->image.height)) { SDL_DestroyTexture(tex); return sdl_error(-1); } res->image.ptr = tex; } break; default: fprintf(stderr, "invalid type\n"); return -1; } return 0; } /* sources/TZR_DrawBegin.c */ int TZR_DrawBegin(void) { if (SDL_SetRenderTarget(___tzr_renderer, ___tzr_target) < 0) return sdl_error(-1); return 0; } /* sources/TZR_DrawClear.c */ int TZR_DrawClear(void) { if (SDL_RenderClear(___tzr_renderer) < 0) return sdl_error(-1); return 0; } /* sources/TZR_DrawEnd.c */ int TZR_DrawEnd(void) { if (SDL_SetRenderTarget(___tzr_renderer, NULL) < 0) return sdl_error(-1); if (SDL_SetRenderDrawColor(___tzr_renderer, 0, 0, 0, 255) < 0) return sdl_error(-1); if (SDL_RenderClear(___tzr_renderer) < 0) return sdl_error(-1); int win_w, win_h; SDL_GetWindowSize(___tzr_window, &win_w, &win_h); win_w = win_w ? win_w : 1; win_h = win_h ? win_h : 1; const float ratio_w = win_w / (float)___tzr_config.width; const float ratio_h = win_h / (float)___tzr_config.height; float scale = (ratio_w < ratio_h) ? ratio_w : ratio_h; if (scale > 1.0f && ___tzr_config.pixel_perfect) scale = (int)scale; const int off_x = (win_w - ___tzr_config.width * scale) / 2; const int off_y = (win_h - ___tzr_config.height * scale) / 2; const SDL_Rect dest = { off_x, off_y, ___tzr_config.width * scale, ___tzr_config.height * scale }; if (SDL_RenderCopy(___tzr_renderer, ___tzr_target, NULL, &dest) < 0) return sdl_error(-1); if (___tzr_config.target_fps > 0) { ___tzr_next_time += ___tzr_min_dt; const unsigned long cur_time = SDL_GetTicks64(); if (___tzr_next_time <= cur_time) ___tzr_next_time = cur_time; else SDL_Delay(___tzr_next_time - cur_time); } SDL_RenderPresent(___tzr_renderer); ___tzr_tick += 1; return 0; } /* sources/TZR_DrawImage.c */ int _TZR_DrawImage(const TZR_DrawImageArgs *args) { if (TZR_GetResourceType(args->id) != TZR_RES_IMAGE) { fprintf(stderr, "%u isn't an image\n", args->id); return -1; } TZR_Resource *const res = TZR_GetResourcePointer(args->id); const TZR_Image *const img = &res->image; if (SDL_SetTextureColorMod(img->ptr, ___tzr_color.r * 255, ___tzr_color.g * 255, ___tzr_color.b * 255)) return sdl_error(-1); if (SDL_SetTextureAlphaMod(img->ptr, ___tzr_color.a * 255)) return sdl_error(-1); float scale_x = args->sx; float scale_y = args->sy; int simg_width = (args->w == INT_MIN) ? img->width : args->w; if (simg_width < 0) { simg_width *= -1; scale_x *= -1; } if (simg_width + args->ix > img->width) simg_width = img->width - args->ix; int simg_height = (args->h == INT_MIN) ? img->height : args->h; if (simg_height < 0) { simg_height *= -1; scale_y *= -1; } if (simg_height + args->iy > img->height) simg_height = img->height - args->iy; const int flip = (SDL_FLIP_HORIZONTAL * (scale_x < 0.0f)) | (SDL_FLIP_VERTICAL * (scale_y < 0.0f)); const SDL_Rect src = { args->ix, args->iy, simg_width, simg_height }; const int width = simg_width * fabs(scale_x); const int height = simg_height * fabs(scale_y); const int x = args->x - (scale_x < 0.0f ? width : 0); const int y = args->y - (scale_y < 0.0f ? height : 0); const SDL_Rect dest = { x - (args->center ? (simg_width * scale_x / 2) : 0), y - (args->center ? (simg_height * scale_y / 2) : 0), width, height }; if (SDL_RenderCopyEx(___tzr_renderer, img->ptr, &src, &dest, args->r * 360, NULL, flip) < 0) return sdl_error(-1); TZR_Rect area; TZR_RectFromSDLRect(&area, &dest); return 0; } /* sources/TZR_DrawLine.c */ int TZR_DrawLine(int x0, int y0, int x1, int y1) { if (SDL_RenderDrawLine(___tzr_renderer, x0, y0, x1, y1) < 0) return sdl_error(-1); return 0; } /* sources/TZR_DrawPoint.c */ int TZR_DrawPoint(int x, int y) { if (SDL_RenderDrawPoint(___tzr_renderer, x, y) < 0) return sdl_error(-1); return 0; } /* sources/TZR_DrawRectangle.c */ int TZR_DrawRectangle(bool fill, int x, int y, int w, int h) { const SDL_Rect rect = { x, y, w, h }; if (fill) { if (SDL_RenderFillRect(___tzr_renderer, &rect) < 0) return sdl_error(-1); } else { if (SDL_RenderDrawRect(___tzr_renderer, &rect) < 0) return sdl_error(-1); } return 0; } /* sources/TZR_DrawSetColor.c */ int _TZR_DrawSetColor(const TZR_Color *color) { ___tzr_color.r = (color->r < 0.0f) ? ___tzr_color.r : color->r; ___tzr_color.g = (color->g < 0.0f) ? ___tzr_color.g : color->g; ___tzr_color.b = (color->b < 0.0f) ? ___tzr_color.b : color->b; ___tzr_color.a = (color->a < 0.0f) ? ___tzr_color.a : color->a; const uint8_t r = ___tzr_color.r * 255; const uint8_t g = ___tzr_color.g * 255; const uint8_t b = ___tzr_color.b * 255; const uint8_t a = ___tzr_color.a * 255; if (SDL_SetRenderDrawColor(___tzr_renderer, r, g, b, a) < 0) return sdl_error(-1); return 0; } /* sources/TZR_GetImageHeight.c */ int TZR_GetImageHeight(TZR_Uint id) { return TZR_GetResourcePointer(id)->image.height; } /* sources/TZR_GetImageWidth.c */ int TZR_GetImageWidth(TZR_Uint id) { return TZR_GetResourcePointer(id)->image.width; } /* sources/TZR_GetKeyState.c */ TZR_KeyState TZR_GetKeyState(int scancode) { return ___tzr_keystates[scancode]; } /* sources/TZR_GetRawResource.c */ TZR_Raw *TZR_GetRawResource(TZR_Uint id) { return &TZR_GetResourcePointer(id)->raw; } /* sources/TZR_GetResourcePath.c */ const char *TZR_GetResourcePath(TZR_Uint id) { return TZR_GetResourcePointer(id)->path; } /* sources/TZR_GetResourcePointer.c */ TZR_Resource *TZR_GetResourcePointer(TZR_Uint id) { return &___tzr_resources[id - 1]; } /* sources/TZR_GetResourceType.c */ TZR_ResourceType TZR_GetResourceType(TZR_Uint id) { return TZR_GetResourcePointer(id)->type; } /* sources/TZR_GetTick.c */ unsigned long TZR_GetTick(void) { return ___tzr_tick; } /* sources/TZR_Init.c */ static int _sdl_error(void) { sdl_error(-1); TZR_Quit(); return -1; } int _TZR_Init(const TZR_Config *config) { if (SDL_Init(SDL_INIT_VIDEO) < 0) return _sdl_error(); if (IMG_Init(IMG_INIT_PNG) < 0) return _sdl_error(); memcpy(&___tzr_config, config, sizeof(TZR_Config)); char *const basepath = SDL_GetBasePath(); if (basepath == NULL) return _sdl_error(); const int chdir_rv = chdir(basepath); SDL_free(basepath); if (chdir_rv < 0) return perror("TZR_Init"), -1; ___tzr_window = SDL_CreateWindow("TZR", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, ___tzr_config.width, ___tzr_config.height, SDL_WINDOW_RESIZABLE); if (___tzr_window == NULL) return _sdl_error(); ___tzr_renderer = SDL_CreateRenderer(___tzr_window, -1, SDL_RENDERER_ACCELERATED); if (___tzr_renderer == NULL) return _sdl_error(); ___tzr_target = SDL_CreateTexture(___tzr_renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_TARGET, ___tzr_config.width, ___tzr_config.height); if (___tzr_target == NULL) return _sdl_error(); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 0); ___tzr_tick = 0; if (___tzr_config.target_fps > 0) { ___tzr_min_dt = 1000 / ___tzr_config.target_fps; ___tzr_next_time = SDL_GetTicks64(); } ___tzr_command[TZR_RES_RAW] = getenv("TZR_EDIT_RAW"); if (___tzr_command[TZR_RES_RAW] == NULL) ___tzr_command[TZR_RES_RAW] = "foot nvim"; ___tzr_command[TZR_RES_IMAGE] = getenv("TZR_EDIT_IMAGE"); if (___tzr_command[TZR_RES_IMAGE] == NULL) ___tzr_command[TZR_RES_IMAGE] = "gimp"; return 0; } /* sources/TZR_IsKeyDown.c */ bool TZR_IsKeyDown(int scancode) { const TZR_KeyState state = TZR_GetKeyState(scancode); return (state == TZR_KEYSTATE_DOWN || state == TZR_KEYSTATE_PRESS); } /* sources/TZR_IsKeyPressed.c */ bool TZR_IsKeyPressed(int scancode) { return (TZR_GetKeyState(scancode) == TZR_KEYSTATE_PRESS); } /* sources/TZR_IsKeyReleased.c */ bool TZR_IsKeyReleased(int scancode) { if (TZR_GetKeyState(scancode) == TZR_KEYSTATE_RELEASE) return true; return false; } /* sources/TZR_IsKeyUp.c */ bool TZR_IsKeyUp(int scancode) { const TZR_KeyState state = TZR_GetKeyState(scancode); return (state == TZR_KEYSTATE_UP || state == TZR_KEYSTATE_RELEASE); } /* sources/TZR_LoadResource.c */ static TZR_ResourceType deduce_type(const char *path) { const char *const path_extension = strrchr(path, '.'); if (path_extension == NULL) return TZR_RES_RAW; if (strcasecmp(path_extension, ".bmp") == 0) return TZR_RES_IMAGE; return TZR_RES_RAW; } TZR_Uint TZR_LoadResource(const char *path) { return TZR_LoadResourceTyped(deduce_type(path), path); } /* sources/TZR_LoadResourceFromMemory.c */ static int reserve_ressources(size_t size) { if (size <= ___tzr_resources_capacity) return 0; size_t target = 16; while (target < size * sizeof(TZR_Resource)) target *= 2; TZR_Resource *new_resources; if (___tzr_resources == NULL) new_resources = malloc(target); else new_resources = realloc(___tzr_resources, target); if (new_resources == NULL) { perror("TZR_LoadResourceFromMemory:reserve_ressources"); return 1; } ___tzr_resources = new_resources; return 0; } TZR_Uint TZR_LoadResourceFromMemory(TZR_ResourceType type, const void *data, int size) { if (reserve_ressources(___tzr_resources_size + 1)) { fprintf(stderr, "failed to reserve for new ressource\n"); return 0; } TZR_Resource *const res = &___tzr_resources[___tzr_resources_size]; res->type = type; res->path = NULL; if (TZR_DirectResourceLoad(res, data, size)) return 0; ___tzr_resources_size += 1; return ___tzr_resources_size; } /* sources/TZR_LoadResourceTyped.c */ TZR_Uint TZR_LoadResourceTyped(TZR_ResourceType type, const char *path) { for (TZR_Uint i = 0; i < ___tzr_resources_size; i++) if (___tzr_resources[i].type == type && strcmp(___tzr_resources[i].path, path) == 0) return i; FILE *const fp = fopen(path, "rb"); if (fp == NULL) { perror(path); return 0; } size_t size; char *const data = drain(fp, &size); fclose(fp); if (data == NULL) return 0; const TZR_Uint id = TZR_LoadResourceFromMemory(type, data, size); free(data); if (id == 0) return 0; TZR_Resource *const res = TZR_GetResourcePointer(id); const size_t path_len = strlen(path); struct stat st = {0}; (void)stat(path, &st); res->path = malloc(path_len + 1); res->mtime = 0; if (res->path == NULL) perror("TZR_LoadResourceTyped"); else strcpy(res->path, path); return id; } /* sources/TZR_PollEvent.c */ int TZR_PollEvent(TZR_Event *e) { TZR_Event discard; if (e == NULL) e = &discard; SDL_Event se; while (SDL_PollEvent(&se)) switch (se.type) { case SDL_QUIT: e->type = TZR_EV_QUIT; ___tzr_should_quit = 1; return 1; case SDL_KEYDOWN: if (se.key.repeat) break; e->type = TZR_EV_KEYDOWN; e->button = se.key.keysym.scancode; ___tzr_keystates[e->button] = TZR_KEYSTATE_PRESS; break; case SDL_KEYUP: e->type = TZR_EV_KEYUP; e->button = se.key.keysym.scancode; ___tzr_keystates[e->button] = TZR_KEYSTATE_RELEASE; break; case SDL_MOUSEBUTTONDOWN: ___tzr_mouse_x = se.button.x; ___tzr_mouse_y = se.button.y; break; case SDL_MOUSEBUTTONUP: ___tzr_mouse_x = se.button.x; ___tzr_mouse_y = se.button.y; break; case SDL_MOUSEMOTION: ___tzr_mouse_x = se.motion.x; ___tzr_mouse_y = se.motion.y; break; default: break; } return 0; } /* sources/TZR_Quit.c */ void TZR_Quit(void) { for (size_t i = 0; i < ___tzr_resources_size; i++) TZR_DestroyResource(&___tzr_resources[i], 1); if (___tzr_resources != NULL) free(___tzr_resources); ___tzr_resources = NULL; ___tzr_resources_size = 0; ___tzr_resources_capacity = 0; if (___tzr_target != NULL) { SDL_DestroyTexture(___tzr_target); ___tzr_target = NULL; } if (___tzr_renderer != NULL) { SDL_DestroyRenderer(___tzr_renderer); ___tzr_renderer = NULL; } if (___tzr_window != NULL) { SDL_DestroyWindow(___tzr_window); ___tzr_window = NULL; } IMG_Quit(); SDL_Quit(); } /* sources/TZR_ReloadResource.c */ int TZR_ReloadResource(TZR_Uint id) { TZR_Resource *const res = TZR_GetResourcePointer(id); if (res->path == NULL) { fprintf(stderr, "resource path of %u is NULL\n", id); return -1; } FILE *const fp = fopen(res->path, "rb"); if (fp == NULL) { perror(res->path); return -1; } size_t size; char *const data = drain(fp, &size); fclose(fp); if (data == NULL) return -1; TZR_Resource new_res; memcpy(&new_res, res, sizeof(TZR_Resource)); const int load_rv = TZR_DirectResourceLoad(&new_res, data, size); free(data); if (load_rv) return -1; TZR_DestroyResource(res, 0); memcpy(res, &new_res, sizeof(TZR_Resource)); return 0; } /* sources/TZR_ResourcesWatch.c */ void TZR_ResourcesWatch(void) { for (TZR_Uint i = 0; i < ___tzr_resources_size; i++) { TZR_Resource *const res = TZR_GetResourcePointer(i + 1); if (res->path == NULL) continue; struct stat st = {0}; (void)stat(res->path, &st); if (st.st_mtime != res->mtime) { if (res->mtime != 0) TZR_ReloadResource(i + 1); res->mtime = st.st_mtime; } } } /* sources/TZR_ShouldQuit.c */ int TZR_ShouldQuit(void) { return ___tzr_should_quit; } /* commit hash: 41400167398f1ab5f5e8ee67a76358189085f17f */