327 lines
9.9 KiB
C
327 lines
9.9 KiB
C
#include <unistd.h>
|
|
|
|
#include <SDL/SDL.h>
|
|
#include <GLES/gl.h>
|
|
|
|
#ifndef MCPI_SERVER_MODE
|
|
#define GLFW_INCLUDE_NONE
|
|
#include <GLFW/glfw3.h>
|
|
#endif // #ifndef MCPI_SERVER_MODE
|
|
|
|
#include <libreborn/libreborn.h>
|
|
#include <libreborn/media-layer/core.h>
|
|
#include <libreborn/media-layer/internal.h>
|
|
|
|
// GLFW Code Not Needed In Server Mode
|
|
#ifndef MCPI_SERVER_MODE
|
|
|
|
static GLFWwindow *glfw_window;
|
|
|
|
// Handle GLFW Error
|
|
static void glfw_error(__attribute__((unused)) int error, const char *description) {
|
|
WARN("GLFW Error: %s", description);
|
|
}
|
|
|
|
// Convert GLFW Key To SDL Key
|
|
static SDLKey glfw_key_to_sdl_key(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;
|
|
// Hotbar
|
|
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;
|
|
// 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;
|
|
// Fullscreen
|
|
case GLFW_KEY_F11:
|
|
return SDLK_F11;
|
|
// Screenshot
|
|
case GLFW_KEY_F2:
|
|
return SDLK_F2;
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// Pass Character Event
|
|
static void character_event(char c) {
|
|
// SDL_UserEvent Is Never Used In MCPI, So It Is Repurposed For Character Events
|
|
SDL_Event event;
|
|
event.type = SDL_USEREVENT;
|
|
event.user.code = (int) c;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
// Pass Key Presses To SDL
|
|
static void glfw_key(__attribute__((unused)) GLFWwindow *window, int key, int scancode, int action, __attribute__((unused)) int mods) {
|
|
SDL_Event event;
|
|
int up = action == GLFW_RELEASE;
|
|
event.type = up ? SDL_KEYUP : SDL_KEYDOWN;
|
|
event.key.state = up ? SDL_RELEASED : SDL_PRESSED;
|
|
event.key.keysym.scancode = scancode;
|
|
event.key.keysym.mod = KMOD_NONE;
|
|
event.key.keysym.sym = glfw_key_to_sdl_key(key);
|
|
SDL_PushEvent(&event);
|
|
if (key == GLFW_KEY_BACKSPACE && !up) {
|
|
character_event((char) '\b');
|
|
}
|
|
}
|
|
|
|
// Pass Text To Minecraft
|
|
static void glfw_char(__attribute__((unused)) GLFWwindow *window, unsigned int codepoint) {
|
|
character_event((char) codepoint);
|
|
}
|
|
|
|
static double last_mouse_x = 0;
|
|
static double last_mouse_y = 0;
|
|
static int ignore_relative_mouse = 1;
|
|
|
|
// Pass Mouse Movement To SDL
|
|
static void glfw_motion(__attribute__((unused)) GLFWwindow *window, double xpos, double ypos) {
|
|
SDL_Event event;
|
|
event.type = SDL_MOUSEMOTION;
|
|
event.motion.x = xpos;
|
|
event.motion.y = ypos;
|
|
event.motion.xrel = !ignore_relative_mouse ? (xpos - last_mouse_x) : 0;
|
|
event.motion.yrel = !ignore_relative_mouse ? (ypos - last_mouse_y) : 0;
|
|
ignore_relative_mouse = 0;
|
|
last_mouse_x = xpos;
|
|
last_mouse_y = ypos;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
// Create And Push SDL Mouse Click Event
|
|
static void click_event(int button, int up) {
|
|
SDL_Event event;
|
|
event.type = up ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN;
|
|
event.button.x = last_mouse_x;
|
|
event.button.y = last_mouse_y;
|
|
event.button.state = up ? SDL_RELEASED : SDL_PRESSED;
|
|
event.button.button = button;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
// Pass Mouse Click To SDL
|
|
static void glfw_click(__attribute__((unused)) GLFWwindow *window, int button, int action, __attribute__((unused)) int mods) {
|
|
int up = action == GLFW_RELEASE;
|
|
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);
|
|
}
|
|
|
|
// Pass Mouse Scroll To SDL
|
|
static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute__((unused)) double xoffset, double yoffset) {
|
|
if (yoffset != 0) {
|
|
int sdl_button = yoffset > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN;
|
|
click_event(sdl_button, 0);
|
|
click_event(sdl_button, 1);
|
|
}
|
|
}
|
|
|
|
#endif // #ifndef MCPI_SERVER_MODE
|
|
|
|
// Init GLFW
|
|
void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) {
|
|
// Don't Enable GLFW In Server Mode
|
|
#ifndef MCPI_SERVER_MODE
|
|
glfwSetErrorCallback(glfw_error);
|
|
|
|
if (!glfwInit()) {
|
|
ERR("%s", "Unable To Initialize GLFW");
|
|
}
|
|
|
|
// Create OpenGL ES 1.1 Context
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
|
|
// Extra Settings
|
|
glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE);
|
|
glfwWindowHint(GLFW_ALPHA_BITS, 0); // Fix Transparent Window On Wayland
|
|
|
|
glfw_window = glfwCreateWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, title, NULL, NULL);
|
|
if (!glfw_window) {
|
|
ERR("%s", "Unable To Create GLFW Window");
|
|
}
|
|
|
|
// Don't Process Events In Server Mode
|
|
glfwSetKeyCallback(glfw_window, glfw_key);
|
|
glfwSetCharCallback(glfw_window, glfw_char);
|
|
glfwSetCursorPosCallback(glfw_window, glfw_motion);
|
|
glfwSetMouseButtonCallback(glfw_window, glfw_click);
|
|
glfwSetScrollCallback(glfw_window, glfw_scroll);
|
|
|
|
glfwMakeContextCurrent(glfw_window);
|
|
#else // #ifndef MCPI_SERVER_MODE
|
|
(void) title; // Mark As Used
|
|
#endif // #ifndef MCPI_SERVER_MODE
|
|
}
|
|
|
|
void media_swap_buffers() {
|
|
#ifndef MCPI_SERVER_MODE
|
|
// Don't Swap Buffers In A Context-Less Window
|
|
glfwSwapBuffers(glfw_window);
|
|
#endif // #ifndef MCPI_SERVER_MODE
|
|
}
|
|
|
|
// Fullscreen Not Needed In Server Mode
|
|
#ifndef MCPI_SERVER_MODE
|
|
static int is_fullscreen = 0;
|
|
|
|
// Old Size And Position To Use When Exiting Fullscreen
|
|
static int old_width = -1;
|
|
static int old_height = -1;
|
|
static int old_x = -1;
|
|
static int old_y = -1;
|
|
|
|
// Toggle Fullscreen
|
|
void media_toggle_fullscreen() {
|
|
if (is_fullscreen) {
|
|
glfwSetWindowMonitor(glfw_window, NULL, old_x, old_y, old_width, old_height, GLFW_DONT_CARE);
|
|
|
|
old_width = -1;
|
|
old_height = -1;
|
|
old_x = -1;
|
|
old_y = -1;
|
|
} else {
|
|
glfwGetWindowSize(glfw_window, &old_width, &old_height);
|
|
glfwGetWindowPos(glfw_window, &old_x, &old_y);
|
|
|
|
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
|
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
|
|
|
|
glfwSetWindowMonitor(glfw_window, monitor, 0, 0, mode->width, mode->height, GLFW_DONT_CARE);
|
|
}
|
|
is_fullscreen = !is_fullscreen;
|
|
}
|
|
#else // #ifndef MCPI_SERVER_MODE
|
|
void media_toggle_fullscreen() {
|
|
}
|
|
#endif // #ifndef MCPI_SERVER_MODE
|
|
|
|
// Intercept SDL Events
|
|
void _media_handle_SDL_PollEvent() {
|
|
// GLFW Is Disabled In Server Mode
|
|
#ifndef MCPI_SERVER_MODE
|
|
// Process GLFW Events
|
|
glfwPollEvents();
|
|
|
|
// Close Window
|
|
if (glfwWindowShouldClose(glfw_window)) {
|
|
SDL_Event event;
|
|
event.type = SDL_QUIT;
|
|
SDL_PushEvent(&event);
|
|
glfwSetWindowShouldClose(glfw_window, GLFW_FALSE);
|
|
}
|
|
#endif // #ifndef MCPI_SERVER_MODE
|
|
}
|
|
|
|
// Terminate GLFW
|
|
void media_cleanup() {
|
|
// GLFW Is Disabled In Server Mode
|
|
#ifndef MCPI_SERVER_MODE
|
|
glfwDestroyWindow(glfw_window);
|
|
glfwTerminate();
|
|
#endif // #ifndef MCPI_SERVER_MODE
|
|
}
|
|
|
|
#ifdef MCPI_SERVER_MODE
|
|
static SDL_GrabMode fake_grab_mode = SDL_GRAB_OFF;
|
|
#endif // #ifdef MCPI_SERVER_MODE
|
|
|
|
// Fix SDL Cursor Visibility/Grabbing
|
|
SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode) {
|
|
#ifdef MCPI_SERVER_MODE
|
|
// Don't Grab Input In Server Mode
|
|
if (mode != SDL_GRAB_QUERY) {
|
|
fake_grab_mode = mode;
|
|
}
|
|
return fake_grab_mode;
|
|
#else // #ifdef MCPI_SERVER_MODE
|
|
if (mode != SDL_GRAB_QUERY && mode != SDL_WM_GrabInput(SDL_GRAB_QUERY)) {
|
|
glfwSetInputMode(glfw_window, GLFW_CURSOR, mode == SDL_GRAB_OFF ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
|
|
#if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3)
|
|
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, mode == SDL_GRAB_OFF ? GLFW_FALSE : GLFW_TRUE);
|
|
#endif // #if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3)
|
|
|
|
// Reset Last Mouse Position
|
|
ignore_relative_mouse = 1;
|
|
}
|
|
return mode == SDL_GRAB_QUERY ? (glfwGetInputMode(glfw_window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL ? SDL_GRAB_OFF : SDL_GRAB_ON) : mode;
|
|
#endif // #ifdef MCPI_SERVER_MODE
|
|
}
|
|
|
|
// Stub SDL Cursor Visibility
|
|
int SDL_ShowCursor(int toggle) {
|
|
#ifdef MCPI_SERVER_MODE
|
|
return toggle == SDL_QUERY ? (fake_grab_mode == SDL_GRAB_OFF ? SDL_ENABLE : SDL_DISABLE) : toggle;
|
|
#else // #ifdef MCPI_SERVER_MODE
|
|
return toggle == SDL_QUERY ? (glfwGetInputMode(glfw_window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL ? SDL_ENABLE : SDL_DISABLE) : toggle;
|
|
#endif // #ifdef MCPI_SERVER_MODE
|
|
}
|
|
|
|
// Get Framebuffer Size
|
|
void media_get_framebuffer_size(int *width, int *height) {
|
|
#ifndef MCPI_SERVER_MODE
|
|
if (glfw_window != NULL) {
|
|
glfwGetFramebufferSize(glfw_window, width, height);
|
|
return;
|
|
}
|
|
#endif // #ifndef MCPI_SERVER_MODE
|
|
*width = DEFAULT_WIDTH;
|
|
*height = DEFAULT_HEIGHT;
|
|
}
|