#include "media.h" // Convert GLFW Key To SDL Key static SDLKey glfw_key_to_sdl_key(const int key) { switch (key) { // Movement case GLFW_KEY_W: return SDLK_w; case GLFW_KEY_A: return SDLK_a; case GLFW_KEY_S: return SDLK_s; case GLFW_KEY_D: return SDLK_d; case GLFW_KEY_SPACE: return SDLK_SPACE; case GLFW_KEY_LEFT_SHIFT: return SDLK_LSHIFT; case GLFW_KEY_RIGHT_SHIFT: return SDLK_RSHIFT; // Inventory case GLFW_KEY_E: return SDLK_e; // Drop Item case GLFW_KEY_Q: return SDLK_q; // Toolbar case GLFW_KEY_1: return SDLK_1; case GLFW_KEY_2: return SDLK_2; case GLFW_KEY_3: return SDLK_3; case GLFW_KEY_4: return SDLK_4; case GLFW_KEY_5: return SDLK_5; case GLFW_KEY_6: return SDLK_6; case GLFW_KEY_7: return SDLK_7; case GLFW_KEY_8: return SDLK_8; case GLFW_KEY_9: return SDLK_9; case GLFW_KEY_0: return SDLK_0; // UI Control case GLFW_KEY_ESCAPE: return SDLK_ESCAPE; case GLFW_KEY_UP: return SDLK_UP; case GLFW_KEY_DOWN: return SDLK_DOWN; case GLFW_KEY_LEFT: return SDLK_LEFT; case GLFW_KEY_RIGHT: return SDLK_RIGHT; case GLFW_KEY_TAB: return SDLK_TAB; case GLFW_KEY_ENTER: return SDLK_RETURN; case GLFW_KEY_BACKSPACE: return SDLK_BACKSPACE; case GLFW_KEY_DELETE: return SDLK_DELETE; // Fullscreen case GLFW_KEY_F11: return SDLK_F11; // Screenshot case GLFW_KEY_F2: return SDLK_F2; // Debug case GLFW_KEY_F3: return SDLK_F3; // Hide GUI case GLFW_KEY_F1: return SDLK_F1; // Third Person case GLFW_KEY_F5: return SDLK_F5; // Chat case GLFW_KEY_T: return SDLK_t; // Unknown default: return SDLK_UNKNOWN; } } // Convert GLFW Key Modifier To SDL Key Modifier static SDLMod glfw_modifier_to_sdl_modifier(const int mods) { int ret = KMOD_NONE; // Control if ((mods & GLFW_MOD_CONTROL) != 0) { ret |= KMOD_CTRL; } // Shift if ((mods & GLFW_MOD_SHIFT) != 0) { ret |= KMOD_SHIFT; } // Alt if ((mods & GLFW_MOD_ALT) != 0) { ret |= KMOD_ALT; } // Return return SDLMod(ret); } // Pass Key Presses To SDL static void glfw_key_raw(int key, int scancode, int action, int mods) { SDL_Event event1; bool up = action == GLFW_RELEASE; event1.type = up ? SDL_KEYUP : SDL_KEYDOWN; event1.key.state = up ? SDL_RELEASED : SDL_PRESSED; event1.key.keysym.scancode = scancode; event1.key.keysym.mod = glfw_modifier_to_sdl_modifier(mods); event1.key.keysym.sym = glfw_key_to_sdl_key(key); media_SDL_PushEvent(&event1); // Allow MCPI To Access Original GLFW Keycode SDL_Event event2; event2.type = SDL_USEREVENT; event2.user.code = USER_EVENT_REAL_KEY; event2.user.data1 = event1.key.state; event2.user.data2 = key; media_SDL_PushEvent(&event2); } static void glfw_key(__attribute__((unused)) GLFWwindow *window, const int key, const int scancode, const int action, const int mods) { if (is_interactable) { glfw_key_raw(key, scancode, action, mods); } } // Pass Text To Minecraft static void character_event(char c) { if (!is_interactable) { return; } // SDL_UserEvent Is Never Used In MCPI, So It Is Repurposed For Character Events SDL_Event event; event.type = SDL_USEREVENT; event.user.code = USER_EVENT_CHARACTER; event.user.data1 = (int) c; media_SDL_PushEvent(&event); } static void codepoint_to_utf8(unsigned char *const buffer, const unsigned int code) { // https://stackoverflow.com/a/42013433/16198887 if (code <= 0x7f) { buffer[0] = code; } else if (code <= 0x7ff) { buffer[0] = 0xc0 | (code >> 6); // 110xxxxx buffer[1] = 0x80 | (code & 0x3f); // 10xxxxxx } else if (code <= 0xffff) { buffer[0] = 0xe0 | (code >> 12); // 1110xxxx buffer[1] = 0x80 | ((code >> 6) & 0x3f); // 10xxxxxx buffer[2] = 0x80 | (code & 0x3f); // 10xxxxxx } else if (code <= 0x10ffff) { buffer[0] = 0xf0 | (code >> 18); // 11110xxx buffer[1] = 0x80 | ((code >> 12) & 0x3f); // 10xxxxxx buffer[2] = 0x80 | ((code >> 6) & 0x3f); // 10xxxxxx buffer[3] = 0x80 | (code & 0x3f); // 10xxxxxx } } static void glfw_char(__attribute__((unused)) GLFWwindow *window, const unsigned int codepoint) { // Convert size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */; char str[str_size] = {}; codepoint_to_utf8((unsigned char *) str, codepoint); char *cp437_str = to_cp437(str); // Send Event for (int i = 0; cp437_str[i] != '\0'; i++) { character_event(cp437_str[i]); } // Free free(cp437_str); } // Convert Screen Coordinates To Pixels static void convert_to_pixels(GLFWwindow *window, double *xpos, double *ypos) { // Skip If Cursor Is Grabbed if (media_SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON && raw_mouse_motion_enabled) { return; } // Get Window Size int window_width; int window_height; glfwGetWindowSize(window, &window_width, &window_height); // Get Framebuffer Size int framebuffer_width; int framebuffer_height; glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height); // Calculate Ratios const double width_ratio = ((double) framebuffer_width) / ((double) window_width); const double height_ratio = ((double) framebuffer_height) / ((double) window_height); // Multiply *xpos *= width_ratio; *ypos *= height_ratio; } // Last Mouse Location static double last_mouse_x = 0; static double last_mouse_y = 0; // Pass Mouse Movement To SDL void _media_glfw_motion(__attribute__((unused)) GLFWwindow *window, double xpos, double ypos) { convert_to_pixels(window, &xpos, &ypos); if (is_interactable) { SDL_Event event; event.type = SDL_MOUSEMOTION; event.motion.x = uint16_t(xpos); event.motion.y = uint16_t(ypos); event.motion.xrel = !ignore_relative_motion ? (xpos - last_mouse_x) : 0; event.motion.yrel = !ignore_relative_motion ? (ypos - last_mouse_y) : 0; media_SDL_PushEvent(&event); } ignore_relative_motion = false; last_mouse_x = xpos; last_mouse_y = ypos; } // Create And Push SDL Mouse Click Event static void click_event(int button, bool up) { SDL_Event event; event.type = up ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN; event.button.x = uint16_t(last_mouse_x); event.button.y = uint16_t(last_mouse_y); event.button.state = up ? SDL_RELEASED : SDL_PRESSED; event.button.button = button; media_SDL_PushEvent(&event); } // Pass Mouse Click To SDL static void glfw_click_raw(const int button, const int action) { const bool up = action == GLFW_RELEASE; const int sdl_button = button == GLFW_MOUSE_BUTTON_RIGHT ? SDL_BUTTON_RIGHT : (button == GLFW_MOUSE_BUTTON_LEFT ? SDL_BUTTON_LEFT : SDL_BUTTON_MIDDLE); click_event(sdl_button, up); } static void glfw_click(__attribute__((unused)) GLFWwindow *window, const int button, const int action, __attribute__((unused)) int mods) { if (is_interactable) { glfw_click_raw(button, action); } } // Pass Mouse Scroll To SDL static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute__((unused)) double xoffset, double yoffset) { if (is_interactable && yoffset != 0) { const int sdl_button = yoffset > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN; click_event(sdl_button, false); click_event(sdl_button, true); } } // Intercept SDL Events void _media_handle_media_SDL_PollEvent() { if (glfw_window) { // Process GLFW Events glfwPollEvents(); // Close Window if (glfwWindowShouldClose(glfw_window)) { SDL_Event event; event.type = SDL_QUIT; media_SDL_PushEvent(&event); glfwSetWindowShouldClose(glfw_window, GLFW_FALSE); } } } // Register Event Listeners void _media_register_event_listeners() { glfwSetKeyCallback(glfw_window, glfw_key); glfwSetCharCallback(glfw_window, glfw_char); glfwSetCursorPosCallback(glfw_window, _media_glfw_motion); glfwSetMouseButtonCallback(glfw_window, glfw_click); glfwSetScrollCallback(glfw_window, glfw_scroll); }