const char *SDLKeycodeToName(SDL_Keycode keycode) {
    switch(keycode) {
        case SDLK_UNKNOWN:                return "SDLK_UNKNOWN"; break;
        case SDLK_RETURN:                 return "SDLK_RETURN"; break;
        case SDLK_ESCAPE:                 return "SDLK_ESCAPE"; break;
        case SDLK_BACKSPACE:              return "SDLK_BACKSPACE"; break;
        case SDLK_TAB:                    return "SDLK_TAB"; break;
        case SDLK_SPACE:                  return "SDLK_SPACE"; break;
        case SDLK_EXCLAIM:                return "SDLK_EXCLAIM"; break;
        case SDLK_DBLAPOSTROPHE:          return "SDLK_DBLAPOSTROPHE"; break;
        case SDLK_HASH:                   return "SDLK_HASH"; break;
        case SDLK_DOLLAR:                 return "SDLK_DOLLAR"; break;
        case SDLK_PERCENT:                return "SDLK_PERCENT"; break;
        case SDLK_AMPERSAND:              return "SDLK_AMPERSAND"; break;
        case SDLK_APOSTROPHE:             return "SDLK_APOSTROPHE"; break;
        case SDLK_LEFTPAREN:              return "SDLK_LEFTPAREN"; break;
        case SDLK_RIGHTPAREN:             return "SDLK_RIGHTPAREN"; break;
        case SDLK_ASTERISK:               return "SDLK_ASTERISK"; break;
        case SDLK_PLUS:                   return "SDLK_PLUS"; break;
        case SDLK_COMMA:                  return "SDLK_COMMA"; break;
        case SDLK_MINUS:                  return "SDLK_MINUS"; break;
        case SDLK_PERIOD:                 return "SDLK_PERIOD"; break;
        case SDLK_SLASH:                  return "SDLK_SLASH"; break;
        case SDLK_0:                      return "SDLK_0"; break;
        case SDLK_1:                      return "SDLK_1"; break;
        case SDLK_2:                      return "SDLK_2"; break;
        case SDLK_3:                      return "SDLK_3"; break;
        case SDLK_4:                      return "SDLK_4"; break;
        case SDLK_5:                      return "SDLK_5"; break;
        case SDLK_6:                      return "SDLK_6"; break;
        case SDLK_7:                      return "SDLK_7"; break;
        case SDLK_8:                      return "SDLK_8"; break;
        case SDLK_9:                      return "SDLK_9"; break;
        case SDLK_COLON:                  return "SDLK_COLON"; break;
        case SDLK_SEMICOLON:              return "SDLK_SEMICOLON"; break;
        case SDLK_LESS:                   return "SDLK_LESS"; break;
        case SDLK_EQUALS:                 return "SDLK_EQUALS"; break;
        case SDLK_GREATER:                return "SDLK_GREATER"; break;
        case SDLK_QUESTION:               return "SDLK_QUESTION"; break;
        case SDLK_AT:                     return "SDLK_AT"; break;
        case SDLK_LEFTBRACKET:            return "SDLK_LEFTBRACKET"; break;
        case SDLK_BACKSLASH:              return "SDLK_BACKSLASH"; break;
        case SDLK_RIGHTBRACKET:           return "SDLK_RIGHTBRACKET"; break;
        case SDLK_CARET:                  return "SDLK_CARET"; break;
        case SDLK_UNDERSCORE:             return "SDLK_UNDERSCORE"; break;
        case SDLK_GRAVE:                  return "SDLK_GRAVE"; break;
        case SDLK_A:                      return "SDLK_A"; break;
        case SDLK_B:                      return "SDLK_B"; break;
        case SDLK_C:                      return "SDLK_C"; break;
        case SDLK_D:                      return "SDLK_D"; break;
        case SDLK_E:                      return "SDLK_E"; break;
        case SDLK_F:                      return "SDLK_F"; break;
        case SDLK_G:                      return "SDLK_G"; break;
        case SDLK_H:                      return "SDLK_H"; break;
        case SDLK_I:                      return "SDLK_I"; break;
        case SDLK_J:                      return "SDLK_J"; break;
        case SDLK_K:                      return "SDLK_K"; break;
        case SDLK_L:                      return "SDLK_L"; break;
        case SDLK_M:                      return "SDLK_M"; break;
        case SDLK_N:                      return "SDLK_N"; break;
        case SDLK_O:                      return "SDLK_O"; break;
        case SDLK_P:                      return "SDLK_P"; break;
        case SDLK_Q:                      return "SDLK_Q"; break;
        case SDLK_R:                      return "SDLK_R"; break;
        case SDLK_S:                      return "SDLK_S"; break;
        case SDLK_T:                      return "SDLK_T"; break;
        case SDLK_U:                      return "SDLK_U"; break;
        case SDLK_V:                      return "SDLK_V"; break;
        case SDLK_W:                      return "SDLK_W"; break;
        case SDLK_X:                      return "SDLK_X"; break;
        case SDLK_Y:                      return "SDLK_Y"; break;
        case SDLK_Z:                      return "SDLK_Z"; break;
        case SDLK_LEFTBRACE:              return "SDLK_LEFTBRACE"; break;
        case SDLK_PIPE:                   return "SDLK_PIPE"; break;
        case SDLK_RIGHTBRACE:             return "SDLK_RIGHTBRACE"; break;
        case SDLK_TILDE:                  return "SDLK_TILDE"; break;
        case SDLK_DELETE:                 return "SDLK_DELETE"; break;
        case SDLK_PLUSMINUS:              return "SDLK_PLUSMINUS"; break;
        case SDLK_CAPSLOCK:               return "SDLK_CAPSLOCK"; break;
        case SDLK_F1:                     return "SDLK_F1"; break;
        case SDLK_F2:                     return "SDLK_F2"; break;
        case SDLK_F3:                     return "SDLK_F3"; break;
        case SDLK_F4:                     return "SDLK_F4"; break;
        case SDLK_F5:                     return "SDLK_F5"; break;
        case SDLK_F6:                     return "SDLK_F6"; break;
        case SDLK_F7:                     return "SDLK_F7"; break;
        case SDLK_F8:                     return "SDLK_F8"; break;
        case SDLK_F9:                     return "SDLK_F9"; break;
        case SDLK_F10:                    return "SDLK_F10"; break;
        case SDLK_F11:                    return "SDLK_F11"; break;
        case SDLK_F12:                    return "SDLK_F12"; break;
        case SDLK_PRINTSCREEN:            return "SDLK_PRINTSCREEN"; break;
        case SDLK_SCROLLLOCK:             return "SDLK_SCROLLLOCK"; break;
        case SDLK_PAUSE:                  return "SDLK_PAUSE"; break;
        case SDLK_INSERT:                 return "SDLK_INSERT"; break;
        case SDLK_HOME:                   return "SDLK_HOME"; break;
        case SDLK_PAGEUP:                 return "SDLK_PAGEUP"; break;
        case SDLK_END:                    return "SDLK_END"; break;
        case SDLK_PAGEDOWN:               return "SDLK_PAGEDOWN"; break;
        case SDLK_RIGHT:                  return "SDLK_RIGHT"; break;
        case SDLK_LEFT:                   return "SDLK_LEFT"; break;
        case SDLK_DOWN:                   return "SDLK_DOWN"; break;
        case SDLK_UP:                     return "SDLK_UP"; break;
        case SDLK_NUMLOCKCLEAR:           return "SDLK_NUMLOCKCLEAR"; break;
        case SDLK_KP_DIVIDE:              return "SDLK_KP_DIVIDE"; break;
        case SDLK_KP_MULTIPLY:            return "SDLK_KP_MULTIPLY"; break;
        case SDLK_KP_MINUS:               return "SDLK_KP_MINUS"; break;
        case SDLK_KP_PLUS:                return "SDLK_KP_PLUS"; break;
        case SDLK_KP_ENTER:               return "SDLK_KP_ENTER"; break;
        case SDLK_KP_1:                   return "SDLK_KP_1"; break;
        case SDLK_KP_2:                   return "SDLK_KP_2"; break;
        case SDLK_KP_3:                   return "SDLK_KP_3"; break;
        case SDLK_KP_4:                   return "SDLK_KP_4"; break;
        case SDLK_KP_5:                   return "SDLK_KP_5"; break;
        case SDLK_KP_6:                   return "SDLK_KP_6"; break;
        case SDLK_KP_7:                   return "SDLK_KP_7"; break;
        case SDLK_KP_8:                   return "SDLK_KP_8"; break;
        case SDLK_KP_9:                   return "SDLK_KP_9"; break;
        case SDLK_KP_0:                   return "SDLK_KP_0"; break;
        case SDLK_KP_PERIOD:              return "SDLK_KP_PERIOD"; break;
        case SDLK_APPLICATION:            return "SDLK_APPLICATION"; break;
        case SDLK_POWER:                  return "SDLK_POWER"; break;
        case SDLK_KP_EQUALS:              return "SDLK_KP_EQUALS"; break;
        case SDLK_F13:                    return "SDLK_F13"; break;
        case SDLK_F14:                    return "SDLK_F14"; break;
        case SDLK_F15:                    return "SDLK_F15"; break;
        case SDLK_F16:                    return "SDLK_F16"; break;
        case SDLK_F17:                    return "SDLK_F17"; break;
        case SDLK_F18:                    return "SDLK_F18"; break;
        case SDLK_F19:                    return "SDLK_F19"; break;
        case SDLK_F20:                    return "SDLK_F20"; break;
        case SDLK_F21:                    return "SDLK_F21"; break;
        case SDLK_F22:                    return "SDLK_F22"; break;
        case SDLK_F23:                    return "SDLK_F23"; break;
        case SDLK_F24:                    return "SDLK_F24"; break;
        case SDLK_EXECUTE:                return "SDLK_EXECUTE"; break;
        case SDLK_HELP:                   return "SDLK_HELP"; break;
        case SDLK_MENU:                   return "SDLK_MENU"; break;
        case SDLK_SELECT:                 return "SDLK_SELECT"; break;
        case SDLK_STOP:                   return "SDLK_STOP"; break;
        case SDLK_AGAIN:                  return "SDLK_AGAIN"; break;
        case SDLK_UNDO:                   return "SDLK_UNDO"; break;
        case SDLK_CUT:                    return "SDLK_CUT"; break;
        case SDLK_COPY:                   return "SDLK_COPY"; break;
        case SDLK_PASTE:                  return "SDLK_PASTE"; break;
        case SDLK_FIND:                   return "SDLK_FIND"; break;
        case SDLK_MUTE:                   return "SDLK_MUTE"; break;
        case SDLK_VOLUMEUP:               return "SDLK_VOLUMEUP"; break;
        case SDLK_VOLUMEDOWN:             return "SDLK_VOLUMEDOWN"; break;
        case SDLK_KP_COMMA:               return "SDLK_KP_COMMA"; break;
        case SDLK_KP_EQUALSAS400:         return "SDLK_KP_EQUALSAS400"; break;
        case SDLK_ALTERASE:               return "SDLK_ALTERASE"; break;
        case SDLK_SYSREQ:                 return "SDLK_SYSREQ"; break;
        case SDLK_CANCEL:                 return "SDLK_CANCEL"; break;
        case SDLK_CLEAR:                  return "SDLK_CLEAR"; break;
        case SDLK_PRIOR:                  return "SDLK_PRIOR"; break;
        case SDLK_RETURN2:                return "SDLK_RETURN2"; break;
        case SDLK_SEPARATOR:              return "SDLK_SEPARATOR"; break;
        case SDLK_OUT:                    return "SDLK_OUT"; break;
        case SDLK_OPER:                   return "SDLK_OPER"; break;
        case SDLK_CLEARAGAIN:             return "SDLK_CLEARAGAIN"; break;
        case SDLK_CRSEL:                  return "SDLK_CRSEL"; break;
        case SDLK_EXSEL:                  return "SDLK_EXSEL"; break;
        case SDLK_KP_00:                  return "SDLK_KP_00"; break;
        case SDLK_KP_000:                 return "SDLK_KP_000"; break;
        case SDLK_THOUSANDSSEPARATOR:     return "SDLK_THOUSANDSSEPARATOR"; break;
        case SDLK_DECIMALSEPARATOR:       return "SDLK_DECIMALSEPARATOR"; break;
        case SDLK_CURRENCYUNIT:           return "SDLK_CURRENCYUNIT"; break;
        case SDLK_CURRENCYSUBUNIT:        return "SDLK_CURRENCYSUBUNIT"; break;
        case SDLK_KP_LEFTPAREN:           return "SDLK_KP_LEFTPAREN"; break;
        case SDLK_KP_RIGHTPAREN:          return "SDLK_KP_RIGHTPAREN"; break;
        case SDLK_KP_LEFTBRACE:           return "SDLK_KP_LEFTBRACE"; break;
        case SDLK_KP_RIGHTBRACE:          return "SDLK_KP_RIGHTBRACE"; break;
        case SDLK_KP_TAB:                 return "SDLK_KP_TAB"; break;
        case SDLK_KP_BACKSPACE:           return "SDLK_KP_BACKSPACE"; break;
        case SDLK_KP_A:                   return "SDLK_KP_A"; break;
        case SDLK_KP_B:                   return "SDLK_KP_B"; break;
        case SDLK_KP_C:                   return "SDLK_KP_C"; break;
        case SDLK_KP_D:                   return "SDLK_KP_D"; break;
        case SDLK_KP_E:                   return "SDLK_KP_E"; break;
        case SDLK_KP_F:                   return "SDLK_KP_F"; break;
        case SDLK_KP_XOR:                 return "SDLK_KP_XOR"; break;
        case SDLK_KP_POWER:               return "SDLK_KP_POWER"; break;
        case SDLK_KP_PERCENT:             return "SDLK_KP_PERCENT"; break;
        case SDLK_KP_LESS:                return "SDLK_KP_LESS"; break;
        case SDLK_KP_GREATER:             return "SDLK_KP_GREATER"; break;
        case SDLK_KP_AMPERSAND:           return "SDLK_KP_AMPERSAND"; break;
        case SDLK_KP_DBLAMPERSAND:        return "SDLK_KP_DBLAMPERSAND"; break;
        case SDLK_KP_VERTICALBAR:         return "SDLK_KP_VERTICALBAR"; break;
        case SDLK_KP_DBLVERTICALBAR:      return "SDLK_KP_DBLVERTICALBAR"; break;
        case SDLK_KP_COLON:               return "SDLK_KP_COLON"; break;
        case SDLK_KP_HASH:                return "SDLK_KP_HASH"; break;
        case SDLK_KP_SPACE:               return "SDLK_KP_SPACE"; break;
        case SDLK_KP_AT:                  return "SDLK_KP_AT"; break;
        case SDLK_KP_EXCLAM:              return "SDLK_KP_EXCLAM"; break;
        case SDLK_KP_MEMSTORE:            return "SDLK_KP_MEMSTORE"; break;
        case SDLK_KP_MEMRECALL:           return "SDLK_KP_MEMRECALL"; break;
        case SDLK_KP_MEMCLEAR:            return "SDLK_KP_MEMCLEAR"; break;
        case SDLK_KP_MEMADD:              return "SDLK_KP_MEMADD"; break;
        case SDLK_KP_MEMSUBTRACT:         return "SDLK_KP_MEMSUBTRACT"; break;
        case SDLK_KP_MEMMULTIPLY:         return "SDLK_KP_MEMMULTIPLY"; break;
        case SDLK_KP_MEMDIVIDE:           return "SDLK_KP_MEMDIVIDE"; break;
        case SDLK_KP_PLUSMINUS:           return "SDLK_KP_PLUSMINUS"; break;
        case SDLK_KP_CLEAR:               return "SDLK_KP_CLEAR"; break;
        case SDLK_KP_CLEARENTRY:          return "SDLK_KP_CLEARENTRY"; break;
        case SDLK_KP_BINARY:              return "SDLK_KP_BINARY"; break;
        case SDLK_KP_OCTAL:               return "SDLK_KP_OCTAL"; break;
        case SDLK_KP_DECIMAL:             return "SDLK_KP_DECIMAL"; break;
        case SDLK_KP_HEXADECIMAL:         return "SDLK_KP_HEXADECIMAL"; break;
        case SDLK_LCTRL:                  return "SDLK_LCTRL"; break;
        case SDLK_LSHIFT:                 return "SDLK_LSHIFT"; break;
        case SDLK_LALT:                   return "SDLK_LALT"; break;
        case SDLK_LGUI:                   return "SDLK_LGUI"; break;
        case SDLK_RCTRL:                  return "SDLK_RCTRL"; break;
        case SDLK_RSHIFT:                 return "SDLK_RSHIFT"; break;
        case SDLK_RALT:                   return "SDLK_RALT"; break;
        case SDLK_RGUI:                   return "SDLK_RGUI"; break;
        case SDLK_MODE:                   return "SDLK_MODE"; break;
        case SDLK_SLEEP:                  return "SDLK_SLEEP"; break;
        case SDLK_WAKE:                   return "SDLK_WAKE"; break;
        case SDLK_CHANNEL_INCREMENT:      return "SDLK_CHANNEL_INCREMENT"; break;
        case SDLK_CHANNEL_DECREMENT:      return "SDLK_CHANNEL_DECREMENT"; break;
        case SDLK_MEDIA_PLAY:             return "SDLK_MEDIA_PLAY"; break;
        case SDLK_MEDIA_PAUSE:            return "SDLK_MEDIA_PAUSE"; break;
        case SDLK_MEDIA_RECORD:           return "SDLK_MEDIA_RECORD"; break;
        case SDLK_MEDIA_FAST_FORWARD:     return "SDLK_MEDIA_FAST_FORWARD"; break;
        case SDLK_MEDIA_REWIND:           return "SDLK_MEDIA_REWIND"; break;
        case SDLK_MEDIA_NEXT_TRACK:       return "SDLK_MEDIA_NEXT_TRACK"; break;
        case SDLK_MEDIA_PREVIOUS_TRACK:   return "SDLK_MEDIA_PREVIOUS_TRACK"; break;
        case SDLK_MEDIA_STOP:             return "SDLK_MEDIA_STOP"; break;
        case SDLK_MEDIA_EJECT:            return "SDLK_MEDIA_EJECT"; break;
        case SDLK_MEDIA_PLAY_PAUSE:       return "SDLK_MEDIA_PLAY_PAUSE"; break;
        case SDLK_MEDIA_SELECT:           return "SDLK_MEDIA_SELECT"; break;
        case SDLK_AC_NEW:                 return "SDLK_AC_NEW"; break;
        case SDLK_AC_OPEN:                return "SDLK_AC_OPEN"; break;
        case SDLK_AC_CLOSE:               return "SDLK_AC_CLOSE"; break;
        case SDLK_AC_EXIT:                return "SDLK_AC_EXIT"; break;
        case SDLK_AC_SAVE:                return "SDLK_AC_SAVE"; break;
        case SDLK_AC_PRINT:               return "SDLK_AC_PRINT"; break;
        case SDLK_AC_PROPERTIES:          return "SDLK_AC_PROPERTIES"; break;
        case SDLK_AC_SEARCH:              return "SDLK_AC_SEARCH"; break;
        case SDLK_AC_HOME:                return "SDLK_AC_HOME"; break;
        case SDLK_AC_BACK:                return "SDLK_AC_BACK"; break;
        case SDLK_AC_FORWARD:             return "SDLK_AC_FORWARD"; break;
        case SDLK_AC_STOP:                return "SDLK_AC_STOP"; break;
        case SDLK_AC_REFRESH:             return "SDLK_AC_REFRESH"; break;
        case SDLK_AC_BOOKMARKS:           return "SDLK_AC_BOOKMARKS"; break;
        case SDLK_SOFTLEFT:               return "SDLK_SOFTLEFT"; break;
        case SDLK_SOFTRIGHT:              return "SDLK_SOFTRIGHT"; break;
        case SDLK_CALL:                   return "SDLK_CALL"; break;
        case SDLK_ENDCALL:                return "SDLK_ENDCALL"; break;
        case SDLK_LEFT_TAB:               return "SDLK_LEFT_TAB"; break;
        case SDLK_LEVEL5_SHIFT:           return "SDLK_LEVEL5_SHIFT"; break;
        case SDLK_MULTI_KEY_COMPOSE:      return "SDLK_MULTI_KEY_COMPOSE"; break;
        case SDLK_LMETA:                  return "SDLK_LMETA"; break;
        case SDLK_RMETA:                  return "SDLK_RMETA"; break;
        case SDLK_LHYPER:                 return "SDLK_LHYPER"; break;
        case SDLK_RHYPER:                 return "SDLK_RHYPER"; break;
        default: return "SDLK_INVALID";
    }
}

struct { String string; EventKind value; } MouseConversionTable[] = {
    {"mousex1", EVENT_MOUSE_X1},
    {"mousex2", EVENT_MOUSE_X2},
    {"mouseleft", EVENT_MOUSE_LEFT},
    {"mouseright", EVENT_MOUSE_RIGHT},
    {"mousemiddle", EVENT_MOUSE_MIDDLE},
};

struct { String string; SDL_Keycode value; } SDLKeycodeConversionTable[] = {
    {"enter", SDLK_RETURN},
    {"escape", SDLK_ESCAPE},
    {"backspace", SDLK_BACKSPACE},
    {"tab", SDLK_TAB},
    {"space", SDLK_SPACE},
    {"minus",  SDLK_MINUS},
    {"period",  SDLK_PERIOD},
    {"slash",  SDLK_SLASH},
    {"0", SDLK_0},
    {"1", SDLK_1},
    {"2", SDLK_2},
    {"3", SDLK_3},
    {"4", SDLK_4},
    {"5", SDLK_5},
    {"6", SDLK_6},
    {"7", SDLK_7},
    {"8", SDLK_8},
    {"9", SDLK_9},
    {"semicolon",  SDLK_SEMICOLON},
    {"less",  SDLK_LESS},
    {"equals",  SDLK_EQUALS},
    {"greater",  SDLK_GREATER},
    {"leftbracket",  SDLK_LEFTBRACKET},
    {"backslash",  SDLK_BACKSLASH},
    {"rightbracket", SDLK_RIGHTBRACKET},
    {"grave",  SDLK_GRAVE},
    {"a", SDLK_A},
    {"b", SDLK_B},
    {"c", SDLK_C},
    {"d", SDLK_D},
    {"e", SDLK_E},
    {"f", SDLK_F},
    {"g", SDLK_G},
    {"h", SDLK_H},
    {"i", SDLK_I},
    {"j", SDLK_J},
    {"k", SDLK_K},
    {"l", SDLK_L},
    {"m", SDLK_M},
    {"n", SDLK_N},
    {"o", SDLK_O},
    {"p", SDLK_P},
    {"q", SDLK_Q},
    {"r", SDLK_R},
    {"s", SDLK_S},
    {"t", SDLK_T},
    {"u", SDLK_U},
    {"v", SDLK_V},
    {"w", SDLK_W},
    {"x", SDLK_X},
    {"y", SDLK_Y},
    {"z", SDLK_Z},
    {"delete", SDLK_DELETE},
    {"capslock", SDLK_CAPSLOCK},
    {"f1", SDLK_F1},
    {"f2", SDLK_F2},
    {"f3", SDLK_F3},
    {"f4", SDLK_F4},
    {"f5", SDLK_F5},
    {"f6", SDLK_F6},
    {"f7", SDLK_F7},
    {"f8", SDLK_F8},
    {"f9", SDLK_F9},
    {"f10", SDLK_F10},
    {"f11", SDLK_F11},
    {"f12", SDLK_F12},
    {"insert", SDLK_INSERT},
    {"home", SDLK_HOME},
    {"pageup", SDLK_PAGEUP},
    {"end", SDLK_END},
    {"pagedown", SDLK_PAGEDOWN},
    {"right", SDLK_RIGHT},
    {"left", SDLK_LEFT},
    {"down", SDLK_DOWN},
    {"up", SDLK_UP},
};

void FillEventWithBasicData(Event *event) {
    SDL_Keymod mod = SDL_GetModState();
    event->shift   = (mod & SDL_KMOD_SHIFT) != 0;
    event->ctrl    = (mod & SDL_KMOD_CTRL) != 0;
    event->alt     = (mod & SDL_KMOD_ALT) != 0;
    event->super   = (mod & SDL_KMOD_GUI) != 0;

    float xmouse, ymouse;
    SDL_GetMouseState(&xmouse, &ymouse);
    event->xmouse = (int16_t)roundf(DPIScale * xmouse);
    event->ymouse = (int16_t)roundf(DPIScale * ymouse);

    int xwindow, ywindow;
    SDL_GetWindowSizeInPixels(SDLWindow, &xwindow, &ywindow);
    event->xwindow = xwindow;
    event->ywindow = ywindow;
    event->text = "";
}

Event TranslateSDLEvent(SDL_Event *input_event) {
    ProfileFunction();
    Event event = {};
    FillEventWithBasicData(&event);

    switch (input_event->type) {
    case SDL_EVENT_QUIT: {
        event.kind = EVENT_QUIT;
    } break;

    case SDL_EVENT_KEY_DOWN: {
        event.kind             = EVENT_KEY_PRESS;
        SDL_KeyboardEvent &key = input_event->key;
        event.key              = key.key;
    } break;

    case SDL_EVENT_TEXT_INPUT: {
        event.kind                 = EVENT_TEXT_INPUT;
        SDL_TextInputEvent &b      = input_event->text;
        String              string = b.text;
        event.text                 = Intern(&GlobalInternTable, string).data;
    } break;

    case SDL_EVENT_MOUSE_BUTTON_DOWN: {
        SDL_MouseButtonEvent &b = input_event->button;
        event.xmouse = (int16_t)roundf(DPIScale * b.x);
        event.ymouse = (int16_t)roundf(DPIScale * b.y);
        event.clicks = b.clicks;
        if (b.button == SDL_BUTTON_LEFT) {
            event.kind   = EVENT_MOUSE_LEFT;
        } else if (b.button == SDL_BUTTON_RIGHT) {
            event.kind   = EVENT_MOUSE_RIGHT;
        } else if (b.button == SDL_BUTTON_MIDDLE) {
            event.kind   = EVENT_MOUSE_MIDDLE;
        } else if (b.button == SDL_BUTTON_X1) {
            event.kind = EVENT_MOUSE_X1;
        } else if (b.button == SDL_BUTTON_X2) {
            event.kind = EVENT_MOUSE_X2;
        } else {
            event.kind = EVENT_NONE;
            event.clicks = 0;
        }
    } break;

    case SDL_EVENT_MOUSE_BUTTON_UP: {
        SDL_MouseButtonEvent &b = input_event->button;
        event.xmouse = (int16_t)roundf(DPIScale * b.x);
        event.ymouse = (int16_t)roundf(DPIScale * b.y);
        if (b.button == SDL_BUTTON_LEFT) {
            event.kind = EVENT_MOUSE_LEFT_UP;
        } else if (b.button == SDL_BUTTON_RIGHT) {
            event.kind = EVENT_MOUSE_RIGHT_UP;
        } else if (b.button == SDL_BUTTON_MIDDLE) {
            event.kind = EVENT_MOUSE_MIDDLE_UP;
        } else if (b.button == SDL_BUTTON_X1) {
            event.kind = EVENT_MOUSE_X1_UP;
        } else if (b.button == SDL_BUTTON_X2) {
            event.kind = EVENT_MOUSE_X2_UP;
        } else {
            event.kind = EVENT_NONE;
        }
    } break;

    case SDL_EVENT_MOUSE_WHEEL: {
        event.kind             = EVENT_MOUSE_WHEEL;
        SDL_MouseWheelEvent &b = input_event->wheel;
        event.xmouse = (int16_t)roundf(DPIScale * b.mouse_x);
        event.ymouse = (int16_t)roundf(DPIScale * b.mouse_y);
        event.xwheel = b.x;
        event.ywheel = b.y;
    } break;

    case SDL_EVENT_MOUSE_MOTION: {
        event.kind = EVENT_UPDATE;
    } break;

    case SDL_EVENT_DROP_FILE: {
        event.kind            = EVENT_DROP_FILE;
        SDL_DropEvent &b      = input_event->drop;
        String         string = b.data;
        event.text            = Intern(&GlobalInternTable, string).data;
    } break;
    default: {
    };
    }

    return event;
}

inline void AddEvent(Array<Event> *events, Event ev) {
    if (ev.kind == EVENT_TEXT_INPUT && (ev.ctrl || ev.alt || ev.super)) {
        return;
    }
    if (ev.kind == EVENT_NONE) {
        return;
    }
    Add(events, ev);
}

Array<Event> GetEventsForFrame(Allocator allocator) {
    ProfileFunction();
    Array<Event> result = {allocator};
    if (EventPlayback.len) {
        result            = TightCopy(allocator, EventPlayback);
        EventPlayback.len = 0;
    }

    SDL_Event event;
    if (WaitForEvents) {
        SDL_WaitEvent(&event);
        Event ev = TranslateSDLEvent(&event);
        AddEvent(&result, ev);
    }

    while (SDL_PollEvent(&event)) {
        Event ev = TranslateSDLEvent(&event);
        AddEvent(&result, ev);
    }

    if (result.len == 0) {
        Event event = {};
        FillEventWithBasicData(&event);
        event.kind = EVENT_UPDATE;
        Add(&result, event);
    }

    Assert(result.len);
    return result;
}

struct Serializer {
    Buffer *buffer; // for writing
};

void Serialize(Serializer *s, String name, EventKind *kind) {
    RawAppendf(s->buffer, "ev.%S = %s; ", name, EventKindStrings[*kind]);
}

void Serialize(Serializer *s, String name, Int *datum) {
    if (*datum == 0) {
        return;
    }
    if (name == "key") {
        RawAppendf(s->buffer, "ev.%S = %s; ", name, SDLKeycodeToName((SDL_Keycode)*datum));
    } else {
        RawAppendf(s->buffer, "ev.%S = %lld; ", name, (long long)*datum);
    }
}

void Serialize(Serializer *s, String name, uint32_t *datum) {
    Int d = *datum;
    Serialize(s, name, &d);
    *datum = (uint32_t)d;
}

void Serialize(Serializer *s, String name, uint8_t *datum) {
    Int d = *datum;
    Serialize(s, name, &d);
    *datum = (uint8_t)d;
}

void Serialize(Serializer *s, String name, int16_t *datum) {
    Int d = *datum;
    Serialize(s, name, &d);
    *datum = (int16_t)d;
}

void Serialize(Serializer *s, String name, float *datum) {
    if (*datum == 0.f) {
        return;
    }
    RawAppendf(s->buffer, "ev.%S = %f; ", name, *datum);
}

void Serialize(Serializer *s, String name, char **text) {
    String str = *text;
    if (str.len == 0) {
        return;
    }
    RawAppendf(s->buffer, "ev.%S = \"%S\"; ", name, str);
}

void SerializeBegin(Serializer *s) {
    RawAppendf(s->buffer, "{Event ev = {};");
}

void SerializeEnd(Serializer *s) {
    RawAppendf(s->buffer, "Add(&EventPlayback, ev);}\n");
}

// :Event
void Serialize(Serializer *s, Event *e) {
    SerializeBegin(s);
#define X(TYPE, KIND, NAME) Serialize(s, #NAME, &e->NAME);
    EVENT_FIELDS
#undef X
    SerializeEnd(s);
}

#define Ctrl() event.ctrl
#define Alt() event.alt
#define Shift() event.shift

#define Press(KEY) (event.key == KEY)
#define CtrlPress(KEY) (event.key == KEY && event.ctrl)
#define ShiftPress(KEY) (event.key == KEY && event.shift)
#define AltPress(KEY) (event.key == KEY && event.alt)
#define CtrlShiftPress(KEY) (event.key == KEY && event.ctrl && event.shift)
#define CtrlAltPress(KEY) (event.key == KEY && event.ctrl && event.alt)
#define AltShiftPress(KEY) (event.key == KEY && event.shift && event.alt)
#define MouseVec2() \
    Vec2 { (float)event.xmouse, (float)event.ymouse }
#define MouseVec2I() \
    Vec2I { (Int) event.xmouse, (Int)event.ymouse }
#define Mouse(x) (event.kind == EVENT_MOUSE_##x)
#define MousePress() (Mouse(LEFT) || Mouse(RIGHT) || Mouse(MIDDLE))
#define MouseUp() (Mouse(LEFT_UP) || Mouse(RIGHT_UP) || Mouse(MIDDLE_UP))
