#ifndef WINDOW_H #define WINDOW_H #include "pch.h" #include "utils.h" struct Image { SDL_Texture *texture; Vec2 size; ~Image() { SDL_DestroyTexture(texture); } }; struct Font { TTF_Font *font; bool pixel_art; ~Font() { TTF_CloseFont(font); } }; struct Text { SDL_Texture *texture; Vec2 size; ~Text() { SDL_DestroyTexture(texture); } }; // Image pool inline Pool image_pool; // Font pool inline Pool font_pool; // Text pool inline Pool text_pool; enum Key : uint16_t { Unknown = SDL_SCANCODE_UNKNOWN, A = SDL_SCANCODE_A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, Num0 = SDL_SCANCODE_0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Space = SDL_SCANCODE_SPACE, Enter = SDL_SCANCODE_RETURN, Escape = SDL_SCANCODE_ESCAPE, Tab = SDL_SCANCODE_TAB, Backspace = SDL_SCANCODE_BACKSPACE, LeftShift = SDL_SCANCODE_LSHIFT, RightShift = SDL_SCANCODE_RSHIFT, LeftCtrl = SDL_SCANCODE_LCTRL, RightCtrl = SDL_SCANCODE_RCTRL, LeftAlt = SDL_SCANCODE_LALT, RightAlt = SDL_SCANCODE_RALT, CapsLock = SDL_SCANCODE_CAPSLOCK, Up = SDL_SCANCODE_UP, Down = SDL_SCANCODE_DOWN, Left = SDL_SCANCODE_LEFT, Right = SDL_SCANCODE_RIGHT, Home = SDL_SCANCODE_HOME, End = SDL_SCANCODE_END, PageUp = SDL_SCANCODE_PAGEUP, PageDown = SDL_SCANCODE_PAGEDOWN, Delete = SDL_SCANCODE_DELETE, Insert = SDL_SCANCODE_INSERT, Minus = SDL_SCANCODE_MINUS, Equals = SDL_SCANCODE_EQUALS, LeftBracket = SDL_SCANCODE_LEFTBRACKET, RightBracket = SDL_SCANCODE_RIGHTBRACKET, Semicolon = SDL_SCANCODE_SEMICOLON, Apostrophe = SDL_SCANCODE_APOSTROPHE, Grave = SDL_SCANCODE_GRAVE, Comma = SDL_SCANCODE_COMMA, Period = SDL_SCANCODE_PERIOD, Slash = SDL_SCANCODE_SLASH, Backslash = SDL_SCANCODE_NONUSBACKSLASH, F1 = SDL_SCANCODE_F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, MouseLeft = 513, MouseMiddle, MouseRight }; constexpr std::pair KEY_NAMES[] = { {"a", Key::A}, {"b", Key::B}, {"c", Key::C}, {"d", Key::D}, {"e", Key::E}, {"f", Key::F}, {"g", Key::G}, {"h", Key::H}, {"i", Key::I}, {"j", Key::J}, {"k", Key::K}, {"l", Key::L}, {"m", Key::M}, {"n", Key::N}, {"o", Key::O}, {"p", Key::P}, {"q", Key::Q}, {"r", Key::R}, {"s", Key::S}, {"t", Key::T}, {"u", Key::U}, {"v", Key::V}, {"w", Key::W}, {"x", Key::X}, {"y", Key::Y}, {"z", Key::Z}, {"num0", Key::Num0}, {"num1", Key::Num1}, {"num2", Key::Num2}, {"num3", Key::Num3}, {"num4", Key::Num4}, {"num5", Key::Num5}, {"num6", Key::Num6}, {"num7", Key::Num7}, {"num8", Key::Num8}, {"num9", Key::Num9}, {"space", Key::Space}, {"enter", Key::Enter}, {"escape", Key::Escape}, {"tab", Key::Tab}, {"backspace", Key::Backspace}, {"left_shift", Key::LeftShift}, {"right_shift", Key::RightShift}, {"left_ctrl", Key::LeftCtrl}, {"right_ctrl", Key::RightCtrl}, {"left_alt", Key::LeftAlt}, {"right_alt", Key::RightAlt}, {"capslock", Key::CapsLock}, {"up", Key::Up}, {"down", Key::Down}, {"left", Key::Left}, {"right", Key::Right}, {"home", Key::Home}, {"end", Key::End}, {"page_up", Key::PageUp}, {"page_down", Key::PageDown}, {"delete", Key::Delete}, {"insert", Key::Insert}, {"minus", Key::Minus}, {"equals", Key::Equals}, {"left_bracket", Key::LeftBracket}, {"right_bracket", Key::RightBracket}, {"semicolon", Key::Semicolon}, {"apostrophe", Key::Apostrophe}, {"grave", Key::Grave}, {"comma", Key::Comma}, {"period", Key::Period}, {"slash", Key::Slash}, {"backslash", Key::Backslash}, {"f1", Key::F1}, {"f2", Key::F2}, {"f3", Key::F3}, {"f4", Key::F4}, {"f5", Key::F5}, {"f6", Key::F6}, {"f7", Key::F7}, {"f8", Key::F8}, {"f9", Key::F9}, {"f10", Key::F10}, {"f11", Key::F11}, {"f12", Key::F12}, {"mouse_left", Key::MouseLeft}, {"mouse_middle", Key::MouseMiddle}, {"mouse_right", Key::MouseRight} }; inline Key get_key_from_name(std::string_view name) { for (const auto &[key_name, key] : KEY_NAMES) if (key_name == name) return key; return Key::Unknown; } inline std::unordered_map action_key_map; inline void set_action_mapping(const std::string &action_name, const std::string &key_name) { Key key = get_key_from_name(key_name); if (key != Key::Unknown) action_key_map[action_name] = key; } inline Key get_key(std::string &name) { auto it = action_key_map.find(name); if (it != action_key_map.end()) return it->second; return get_key_from_name(name); } struct Event { enum Type { NONE, QUIT, KEY_UP, KEY_DOWN, TEXT_INPUT, SCROLL, MOUSE_LEAVE, MOUSE_BUTTON_UP, MOUSE_BUTTON_DOWN, MOUSE_MOVE } type; union { struct { Key key; } key; std::string *text_input; Vec2 scroll; struct { Key button; Vec2 position; } mouse_button; struct { Vec2 position; Vec2 delta; } mouse_move; }; }; struct Animation { std::vector frames; float frame_rate; ~Animation() { for (uint64_t frame_id : frames) image_pool.release(frame_id); } }; inline Pool animation_pool; struct Window { public: // title can be free'd by the caller after the window is created, as it's copied internally. Window(Vec2 size, const char *title); void update(Vec2 new_size, const char *new_title); ~Window(); // Rendering functions uint64_t load_image(const char *path, bool pixel_art); Vec2 get_image_size(uint64_t image_id); uint64_t load_font(const char *path, int font_size, bool bold, bool italic, bool underline, bool pixel_art); // text is copied internally, so it can be free'd by the caller after the text is created. uint64_t create_text(uint64_t font_id, const char *text, uint32_t length, Color color); Vec2 get_text_size(uint64_t text_id); bool write(uint64_t text_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha); bool draw(uint64_t image_id, Vec2 position, float z, float angle, Vec2 pivot, Vec2 scale, float alpha); bool draw_rect(Rect rect, Color color, float z, float angle, Vec2 pivot, Vec2 scale, float alpha); void render(); // the rest of the calls only build the instruction list, this actually renders everything added to the screen (following z order) void clear(); // clears the instruction list // Event handling bool poll(Event &event); // returns true if an event was polled, false otherwise // Clipboard get // To be free'd by the caller after use. const char *paste(); // Time uint64_t get_time(); // returns the time in nanoseconds since the window was created uint64_t delta_time(); // returns the time in nanoseconds since the last render call private: // internal data and functions SDL_Window *window; SDL_Renderer *renderer; Vec2 real_size; Vec2 virtual_size; Vec2 offset; float scale; // White pixel texture for drawing colored rectangles SDL_Texture *white_tex; struct RenderInstruction { enum Type { DRAW_IMAGE, DRAW_TEXT, DRAW_RECT } type; float z; float angle; Vec2 pivot; Vec2 scale; float alpha; union { struct { uint64_t image_id; Vec2 position; } draw_image; struct { uint64_t text_id; Vec2 position; } draw_text; struct { Rect rect; Color color; } draw_rect; }; }; std::vector render_instructions; void compute_transform(); bool mouse_in_window(const Vec2 real_position, Vec2 &virtual_position); void draw_letterbox(); }; inline Window window({720, 480}, "Mysth Engine"); #endif