#include <SDL.h>#include "muscles.h" int main(int argc, char **argv) { return run();} #define SWR_MAX_WIDTH 2048#define SWR_MAX_HEIGHT 2048 struct { u8 r, g, b;} back_color; SDL_Window *window = nullptr;static SDL_Renderer *renderer = nullptr; static SDL_Surface *sf_cache = nullptr;static SDL_Renderer *soft_renderer = nullptr; std::vector<SDL_Cursor*> cursors;CursorType cursor = CursorDefault; bool capture = false;int dpi_w = 0, dpi_h = 0; void *sdl_get_hw_renderer() { return renderer;} void sdl_set_cursor(CursorType type) { if (type == cursor) return; cursor = type; SDL_SetCursor(cursors[cursor]);} void sdl_get_dpi(int& w, int& h) { w = dpi_w; h = dpi_h;} void sdl_log_string(const char *msg) { SDL_Log(msg);} void sdl_log_last_error() { const char *msg = SDL_GetError(); if (msg && strlen(msg)) SDL_Log(msg);} Renderer sdl_acquire_sw_renderer(int w, int h) { if (w > SWR_MAX_WIDTH || h > SWR_MAX_HEIGHT) return nullptr; sf_cache->w = w; sf_cache->h = h; sf_cache->pitch = w * 4; return soft_renderer;} Texture sdl_bake_sw_render() { return SDL_CreateTextureFromSurface(renderer, sf_cache);} bool sdl_init(const char *title, int width, int height, RGBA bg_color) { int res = SDL_Init(SDL_INIT_VIDEO); if (res != 0) { SDL_Log("SDL_Init() failed (%d)", res); return false; } window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_RESIZABLE); if (!window) { SDL_Log("SDL_CreateWindow() failed"); return false; } SDL_StartTextInput(); cursors.resize(NumCursors); cursors[CursorDefault] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); cursors[CursorClick] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); cursors[CursorEdit] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); cursors[CursorPan] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); cursors[CursorResizeNorthSouth] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); cursors[CursorResizeWestEast] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); cursors[CursorResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); cursors[CursorResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); sf_cache = (SDL_Surface*)sdl_create_surface(nullptr, SWR_MAX_WIDTH, SWR_MAX_HEIGHT); soft_renderer = SDL_CreateSoftwareRenderer(sf_cache); float hdpi, vdpi; SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(window), nullptr, &hdpi, &vdpi); dpi_w = (int)hdpi; dpi_h = (int)vdpi; renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (!renderer) { SDL_Log("SDL_CreateRenderer() failed"); return false; } back_color.r = (int)(bg_color.r * 255.0f); back_color.g = (int)(bg_color.g * 255.0f); back_color.b = (int)(bg_color.b * 255.0f); SDL_SetRenderDrawColor(renderer, back_color.r, back_color.g, back_color.b, 255); return true;} bool sdl_poll_input(Input& input) { input.ch = 0; input.last_key = 0; input.action = false; input.lclick = false; input.rclick = false; input.scroll_x = 0; input.scroll_y = 0; input.prev_x = input.mouse_x; input.prev_y = input.mouse_y; SDL_GetMouseState(&input.mouse_x, &input.mouse_y); if (input.lmouse || input.rmouse) input.held++; else input.held = 0; if (input.del) input.del++; if (input.back) input.back++; if (input.esc) input.esc++; if (input.enter) input.enter++; if (input.tab) input.tab++; if (input.home) input.home++; if (input.end) input.end++; if (input.pgup) input.pgup++; if (input.pgdown) input.pgdown++; if (input.left) input.left++; if (input.down) input.down++; if (input.right) input.right++; if (input.up) input.up++; if (input.action_timer > 0) input.action_timer--; bool quit = false; SDL_Event event; while (!quit && SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: quit = true; break; case SDL_TEXTINPUT: input.ch = event.text.text[0]; break; case SDL_KEYDOWN: { auto key = event.key.keysym.sym; input.last_key = key; if (key == SDLK_LCTRL) input.lctrl = true; if (key == SDLK_RCTRL) input.rctrl = true; if (key == SDLK_LSHIFT) input.lshift = true; if (key == SDLK_RSHIFT) input.rshift = true; switch (key) { case SDLK_DELETE: input.del = 1; break; case SDLK_BACKSPACE: input.back = 1; break; case SDLK_ESCAPE: input.esc = 1; break; case SDLK_RETURN: input.enter = 1; break; case SDLK_TAB: input.tab = 1; break; case SDLK_HOME: input.home = 1; break; case SDLK_END: input.end = 1; break; case SDLK_PAGEUP: input.pgup = 1; break; case SDLK_PAGEDOWN: input.pgdown = 1; break; case SDLK_LEFT: input.left = 1; break; case SDLK_DOWN: input.down = 1; break; case SDLK_RIGHT: input.right = 1; break; case SDLK_UP: input.up = 1; break; } break; } case SDL_KEYUP: { auto key = event.key.keysym.sym; if (key == SDLK_LCTRL) input.lctrl = false; if (key == SDLK_RCTRL) input.rctrl = false; if (key == SDLK_LSHIFT) input.lshift = false; if (key == SDLK_RSHIFT) input.rshift = false; switch (key) { case SDLK_DELETE: input.del = 0; break; case SDLK_BACKSPACE: input.back = 0; break; case SDLK_ESCAPE: input.esc = 0; break; case SDLK_RETURN: input.enter = 0; break; case SDLK_TAB: input.tab = 0; break; case SDLK_HOME: input.home = 0; break; case SDLK_END: input.end = 0; break; case SDLK_PAGEUP: input.pgup = 0; break; case SDLK_PAGEDOWN: input.pgdown = 0; break; case SDLK_LEFT: input.left = 0; break; case SDLK_DOWN: input.down = 0; break; case SDLK_RIGHT: input.right = 0; break; case SDLK_UP: input.up = 0; break; } break; } case SDL_MOUSEBUTTONDOWN: if (event.button.button == SDL_BUTTON_LEFT) input.lclick = true; if (event.button.button == SDL_BUTTON_RIGHT) input.rclick = true; if (input.lclick) input.lmouse = true; if (input.rclick) input.rmouse = true; input.click_x = input.mouse_x; input.click_y = input.mouse_y; break; case SDL_MOUSEBUTTONUP: { if (event.button.button == SDL_BUTTON_LEFT) input.lmouse = false; if (event.button.button == SDL_BUTTON_RIGHT) input.rmouse = false; if (!input.prevent_action && event.button.button == SDL_BUTTON_LEFT) { double dx = input.mouse_x - input.click_x; double dy = input.mouse_y - input.click_y; if (sqrt(dx*dx + dy*dy) < input.action_circle) { dx = input.mouse_x - input.action_x; dy = input.mouse_y - input.action_y; if (sqrt(dx*dx + dy*dy) < input.action_circle) input.double_click = input.action_timer > 0; else input.double_click = false; input.action = true; input.action_x = input.mouse_x; input.action_y = input.mouse_y; if (!input.double_click) input.action_timer = input.action_timeout; } } input.prevent_action = false; break; } case SDL_MOUSEWHEEL: input.scroll_x = event.wheel.x; input.scroll_y = event.wheel.y; break; } } return quit;} void sdl_update_camera(Camera& camera) { SDL_GetRendererOutputSize(renderer, &camera.center_x, &camera.center_y); camera.center_x /= 2; camera.center_y /= 2;} void sdl_acquire_mouse() { if (!capture) { SDL_CaptureMouse((SDL_bool)true); capture = true; }}void sdl_release_mouse() { if (capture) { SDL_CaptureMouse((SDL_bool)false); capture = false; }} void sdl_copy(std::string& string) { SDL_SetClipboardText(string.c_str());} int sdl_paste_into(std::string& string, int offset, int (*filterer)(char*, int)) { if (!SDL_HasClipboardText()) return 0; char *sdl_text = SDL_GetClipboardText(); int len = strlen(sdl_text); if (filterer) len = filterer(sdl_text, len); string.insert(offset, sdl_text, len); SDL_free(sdl_text); return len;} Surface sdl_create_surface(u32 *data, int w, int h) { return (Surface)(data ? SDL_CreateRGBSurfaceFrom(data, w, h, 32, w * 4, 0xff000000, 0xff0000, 0xff00, 0xff) : SDL_CreateRGBSurface(0, w, h, 32, 0xff000000, 0xff0000, 0xff00, 0xff) );} Texture sdl_create_texture(u32 *data, int w, int h) { SDL_Texture *tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, w, h); SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); if (data) { void *bits = nullptr; int pitch; SDL_LockTexture(tex, nullptr, &bits, &pitch); memcpy(bits, data, w * h * 4); SDL_UnlockTexture(tex); } return (Texture)tex;} void sdl_get_texture_size(Texture tex, int *w, int *h) { SDL_QueryTexture((SDL_Texture*)tex, nullptr, nullptr, w, h);} // The 'renderer' parameter is ignored, it is there so that this function can be used as a drop-in replacement for SDL_RenderCopy() if necessary.void sdl_sw_apply_texture(SDL_Renderer *r, SDL_Texture *tex, SDL_Rect *src, SDL_Rect *dst) { int canv_w = sf_cache->w; int canv_h = sf_cache->h; if (dst->x < 0 || dst->y < 0 || dst->w <= 0 || dst->h <= 0 || dst->x >= canv_w || dst->y >= canv_h) return; int in_w, in_h, pitch; SDL_QueryTexture(tex, nullptr, nullptr, &in_w, &in_h); int src_w, src_h; if (src) { src_w = src->w; src_h = src->h; } else { src_w = in_w; src_h = in_h; } u32 *data = nullptr; SDL_LockTexture(tex, src, (void**)&data, &pitch); if (!data) return; int out_w = dst->w; int out_h = dst->h; if (dst->x + out_w > canv_w) out_w = canv_w - dst->x; if (dst->y + out_h > canv_h) out_h = canv_h - dst->y; double x_ratio = (double)dst->w / (double)src_w; double y_ratio = (double)dst->h / (double)src_h; u8 *out = &((u8*)sf_cache->pixels)[4 * (dst->y * canv_w + dst->x)]; for (int i = 0; i < out_h; i++) { u8 *temp = out; for (int j = 0; j < out_w; j++, out += 4) { int x = (double)j / x_ratio; int y = (double)i / y_ratio; u8 *in = &((u8*)data)[4 * (y * in_w + x)]; int a = in[0]; int b = 255 - a; out[0] = (u8)((a * 255 + b * (int)out[0]) / 255); out[1] = (u8)((a * (int)in[1] + b * (int)out[1]) / 255); out[2] = (u8)((a * (int)in[2] + b * (int)out[2]) / 255); out[3] = (u8)((a * (int)in[3] + b * (int)out[3]) / 255); } out = &temp[canv_w * 4]; } SDL_UnlockTexture(tex);} void sdl_apply_texture(Texture tex, Rect_Int& dst, Rect_Int *src, Renderer rdr) { Renderer r = rdr ? (SDL_Renderer*)rdr : renderer; if (r != renderer) sdl_sw_apply_texture(nullptr, (SDL_Texture*)tex, (SDL_Rect*)src, (SDL_Rect*)&dst); else SDL_RenderCopy(renderer, (SDL_Texture*)tex, (SDL_Rect*)src, (SDL_Rect*)&dst);} void sdl_apply_texture(Texture tex, Rect& dst, Rect_Int *src, Renderer rdr) { SDL_Rect dst_fixed = {(int)(dst.x + 0.5), (int)(dst.y + 0.5), (int)(dst.w + 0.5), (int)(dst.h + 0.5)}; Renderer r = rdr ? (SDL_Renderer*)rdr : renderer; if (r != renderer) sdl_sw_apply_texture(nullptr, (SDL_Texture*)tex, (SDL_Rect*)src, &dst_fixed); else SDL_RenderCopy(renderer, (SDL_Texture*)tex, (SDL_Rect*)src, &dst_fixed);} void sdl_draw_rect(Rect_Int& rect, RGBA& color, Renderer rdr) { SDL_Renderer *r = rdr ? (SDL_Renderer*)rdr : renderer; SDL_SetRenderDrawColor(r, (u8)(color.r * 255.0), (u8)(color.g * 255.0), (u8)(color.b * 255.0), (u8)(color.a * 255.0)); SDL_RenderFillRect(r, (SDL_Rect*)&rect);} void sdl_draw_rect(Rect& rect, RGBA& color, Renderer rdr) { SDL_Rect rect_fixed = {(int)(rect.x + 0.5), (int)(rect.y + 0.5), (int)(rect.w + 0.5), (int)(rect.h + 0.5)}; SDL_Renderer *r = rdr ? (SDL_Renderer*)rdr : renderer; SDL_SetRenderDrawColor(r, (u8)(color.r * 255.0), (u8)(color.g * 255.0), (u8)(color.b * 255.0), (u8)(color.a * 255.0)); SDL_RenderFillRect(r, &rect_fixed);} void sdl_draw_rect_clipped(Rect_Int& rect, Render_Clip& clip, RGBA& color, Renderer rdr) { Rect_Int box = rect; bool escaped_w = false; bool escaped_h = false; if ((clip.flags & CLIP_TOP) && box.y < clip.y_lower) { box.h -= clip.y_lower - box.y; box.y = clip.y_lower; } if ((clip.flags & CLIP_BOTTOM) && box.y + box.h >= clip.y_upper) { box.h = clip.y_upper - box.y; escaped_h = box.y >= clip.y_upper; } if ((clip.flags & CLIP_LEFT) && box.x < clip.x_lower) { box.w -= clip.x_lower - box.x; box.x = clip.x_lower; } if ((clip.flags & CLIP_RIGHT) && box.x + box.w >= clip.x_upper) { box.w = clip.x_upper - box.x; escaped_w = box.x >= clip.x_upper; } if (box.w > 0 && box.h > 0 && !escaped_w && !escaped_h) { SDL_Renderer *r = rdr ? (SDL_Renderer*)rdr : renderer; SDL_SetRenderDrawColor(r, (u8)(color.r * 255.0), (u8)(color.g * 255.0), (u8)(color.b * 255.0), (u8)(color.a * 255.0)); SDL_RenderFillRect(r, (SDL_Rect*)&box); }} void sdl_draw_rect_clipped(Rect& rect, Render_Clip& clip, RGBA& color, Renderer rdr) { Rect_Int rect_fixed = {(int)(rect.x + 0.5), (int)(rect.y + 0.5), (int)(rect.w + 0.5), (int)(rect.h + 0.5)}; sdl_draw_rect_clipped(rect_fixed, clip, color, rdr);} void sdl_clear() { SDL_SetRenderDrawColor(renderer, back_color.r, back_color.g, back_color.b, 255); SDL_RenderClear(renderer);} void sdl_render() { SDL_RenderPresent(renderer);} void sdl_close() { #define DESTROY(obj, destroy_func) \ if (obj) { \ destroy_func(obj); \ obj = nullptr; \ } DESTROY(renderer, SDL_DestroyRenderer) DESTROY(window, SDL_DestroyWindow) DESTROY(sf_cache, SDL_FreeSurface) DESTROY(soft_renderer, SDL_DestroyRenderer) for (auto& c : cursors) { SDL_FreeCursor(c); c = nullptr; } SDL_Quit();} void *sdl_lock_texture(Texture tex) { int pitch; void *data = nullptr; SDL_LockTexture((SDL_Texture*)tex, nullptr, &data, &pitch); return data;} void sdl_unlock_texture(Texture tex) { SDL_UnlockTexture((SDL_Texture*)tex);} void sdl_destroy_texture(Texture *tex) { if (*tex) { SDL_DestroyTexture((SDL_Texture*)*tex); *tex = nullptr; }}