minecraft-pi-docker/mods/src/compat.c

334 lines
10 KiB
C

#define _GNU_SOURCE
#include <time.h>
#include <unistd.h>
#include <FreeImage.h>
#include <SDL/SDL.h>
#include <SDL/SDL_syswm.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <X11/Xlib.h>
#include <libcore/libcore.h>
#include "extra.h"
static Display *x11_display;
static EGLDisplay egl_display;
static Window x11_window;
static Window x11_root_window;
static EGLConfig egl_config;
static int window_loaded = 0;
static EGLContext egl_context;
static EGLSurface egl_surface;
static SDL_Surface *sdl_surface;
HOOK(eglGetDisplay, EGLDisplay, (__attribute__((unused)) NativeDisplayType native_display)) {
// Handled In ensure_x11_window()
return 0;
}
// Get Reference To X Window
static void ensure_x11_window() {
if (!window_loaded) {
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWMInfo(&info);
x11_display = info.info.x11.display;
x11_window = info.info.x11.window;
x11_root_window = RootWindow(x11_display, DefaultScreen(x11_display));
ensure_eglGetDisplay();
egl_display = (*real_eglGetDisplay)(x11_display);
window_loaded = 1;
}
}
// Handled In SDL_WM_SetCaption
HOOK(eglInitialize, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLint *major, __attribute__((unused)) EGLint *minor)) {
return EGL_TRUE;
}
// Handled In SDL_WM_SetCaption
HOOK(eglChooseConfig, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLint const *attrib_list, __attribute__((unused)) EGLConfig *configs, __attribute__((unused)) EGLint config_size, __attribute__((unused)) EGLint *num_config)) {
return EGL_TRUE;
}
// Handled In SDL_WM_SetCaption
HOOK(eglBindAPI, EGLBoolean, (__attribute__((unused)) EGLenum api)) {
return EGL_TRUE;
}
// Handled In SDL_WM_SetCaption
HOOK(eglCreateContext, EGLContext, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLConfig config, __attribute__((unused)) EGLContext share_context, __attribute__((unused)) EGLint const *attrib_list)) {
return 0;
}
// Handled In SDL_WM_SetCaption
HOOK(eglCreateWindowSurface, EGLSurface, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLConfig config, __attribute__((unused)) NativeWindowType native_window, __attribute__((unused)) EGLint const *attrib_list)) {
return 0;
}
// Handled In SDL_WM_SetCaption
HOOK(eglMakeCurrent, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface draw, __attribute__((unused)) EGLSurface read, __attribute__((unused)) EGLContext context)) {
return EGL_TRUE;
}
// Handled In SDL_Quit
HOOK(eglDestroySurface, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface surface)) {
return EGL_TRUE;
}
// Handled In SDL_Quit
HOOK(eglDestroyContext, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLContext context)) {
return EGL_TRUE;
}
// Handled In SDL_Quit
HOOK(eglTerminate, EGLBoolean, (__attribute__((unused)) EGLDisplay display)) {
return EGL_TRUE;
}
// Handled In SDL_WM_SetCaption
HOOK(SDL_SetVideoMode, SDL_Surface *, (__attribute__((unused)) int width, __attribute__((unused)) int height, __attribute__((unused)) int bpp, __attribute__((unused)) uint32_t flags)) {
// Return Value Is Only Used For A NULL-Check
return (SDL_Surface *) 1;
}
// EGL Config
EGLint const set_attrib_list[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE
};
#define WINDOW_VIDEO_FLAGS SDL_RESIZABLE
#define FULLSCREEN_VIDEO_FLAGS SDL_FULLSCREEN
#define BPP 32
// Init EGL
HOOK(SDL_WM_SetCaption, void, (const char *title, const char *icon)) {
// Enable Unicode
SDL_EnableUNICODE(SDL_ENABLE);
FreeImage_Initialise(0);
ensure_SDL_SetVideoMode();
sdl_surface = (*real_SDL_SetVideoMode)(848, 480, BPP, WINDOW_VIDEO_FLAGS);
ensure_SDL_WM_SetCaption();
(*real_SDL_WM_SetCaption)(title, icon);
ensure_x11_window();
ensure_eglInitialize();
(*real_eglInitialize)(egl_display, NULL, NULL);
EGLint number_of_config;
ensure_eglChooseConfig();
(*real_eglChooseConfig)(egl_display, set_attrib_list, &egl_config, 1, &number_of_config);
ensure_eglBindAPI();
(*real_eglBindAPI)(EGL_OPENGL_ES_API);
ensure_eglCreateContext();
egl_context = (*real_eglCreateContext)(egl_display, egl_config, EGL_NO_CONTEXT, NULL);
ensure_eglCreateWindowSurface();
egl_surface = (*real_eglCreateWindowSurface)(egl_display, egl_config, x11_window, NULL);
ensure_eglMakeCurrent();
(*real_eglMakeCurrent)(egl_display, egl_surface, egl_surface, egl_context);
eglSwapInterval(egl_display, 1);
}
HOOK(eglSwapBuffers, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface surface)) {
ensure_eglSwapBuffers();
EGLBoolean ret = (*real_eglSwapBuffers)(egl_display, egl_surface);
return ret;
}
static void resize(int width, int height, int fullscreen) {
uint32_t flags = fullscreen ? FULLSCREEN_VIDEO_FLAGS : WINDOW_VIDEO_FLAGS;
ensure_SDL_SetVideoMode();
sdl_surface = (*real_SDL_SetVideoMode)(width, height, BPP, flags);
// OpenGL state modification for resizing
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glOrthox(0, width, 0, height, -1, 1);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
}
static int is_fullscreen = 0;
static int old_width = 0;
static int old_height = 0;
static void push_resize_event(int width, int height) {
SDL_ResizeEvent resize;
resize.type = SDL_VIDEORESIZE;
resize.w = width;
resize.h = height;
SDL_Event event;
event.type = SDL_VIDEORESIZE;
event.resize = resize;
SDL_PushEvent(&event);
}
static void toggle_fullscreen() {
if (is_fullscreen) {
push_resize_event(old_width, old_height);
old_width = 0;
old_height = 0;
} else {
old_width = sdl_surface->w;
old_height = sdl_surface->h;
Screen *screen = DefaultScreenOfDisplay(x11_display);
push_resize_event(WidthOfScreen(screen), HeightOfScreen(screen));
}
is_fullscreen = !is_fullscreen;
}
// 4 (Year + 1 (Hyphen) + 2 (Month) + 1 (Hyphen) + 2 (Day) + 1 (Underscore) + 2 (Hour) + 1 (Period) + 2 (Minute) + 1 (Period) + 2 (Second) + 1 (Terminator)
#define TIME_SIZE 20
static void screenshot() {
time_t rawtime;
struct tm *timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
char time[TIME_SIZE];
strftime(time, TIME_SIZE, "%Y-%m-%d_%H.%M.%S", timeinfo);
char *screenshots = NULL;
asprintf(&screenshots, "%s/.minecraft/screenshots", getenv("HOME"));
int num = 1;
char *file = NULL;
asprintf(&file, "%s/%s.png", screenshots, time);
while (access(file, F_OK) != -1) {
asprintf(&file, "%s/%s-%i.png", screenshots, time, num);
num++;
}
int line_size = sdl_surface->w * 3;
int size = sdl_surface->h * line_size;
unsigned char pixels[size];
glReadPixels(0, 0, sdl_surface->w, sdl_surface->h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
// Swap Red And Blue
for (int i = 0; i < (size / 3); i++) {
int pixel = i * 3;
int red = pixels[pixel];
int blue = pixels[pixel + 2];
pixels[pixel] = blue;
pixels[pixel + 2] = red;
}
#endif
FIBITMAP *image = FreeImage_ConvertFromRawBits(pixels, sdl_surface->w, sdl_surface->h, line_size, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, 0);
if (!FreeImage_Save(FIF_PNG, image, file, 0)) {
fprintf(stderr, "Screenshot Failed: %s\n", file);
} else {
fprintf(stderr, "Screenshot Saved: %s\n", file);
}
FreeImage_Unload(image);
free(file);
free(screenshots);
}
HOOK(SDL_PollEvent, int, (SDL_Event *event)) {
// Poll Events
ensure_SDL_PollEvent();
int ret = (*real_SDL_PollEvent)(event);
// Resize EGL
if (event != NULL && ret == 1) {
int handled = 0;
if (event->type == SDL_VIDEORESIZE) {
resize(event->resize.w, event->resize.h, is_fullscreen);
handled = 1;
} else if (event->type == SDL_KEYDOWN) {
if (event->key.keysym.sym == SDLK_F11) {
toggle_fullscreen();
handled = 1;
} else if (event->key.keysym.sym == SDLK_F2) {
screenshot();
handled = 1;
} else {
key_press((char) event->key.keysym.unicode);
}
}
if (handled) {
// Event Was Handled
return SDL_PollEvent(event);
}
}
return ret;
}
// Terminate EGL
HOOK(SDL_Quit, void, ()) {
ensure_SDL_Quit();
(*real_SDL_Quit)();
ensure_eglDestroyContext();
(*real_eglDestroyContext)(egl_display, egl_context);
ensure_eglDestroySurface();
(*real_eglDestroySurface)(egl_display, egl_surface);
ensure_eglTerminate();
(*real_eglTerminate)(egl_display);
}
HOOK(XTranslateCoordinates, int, (Display *display, Window src_w, Window dest_w, int src_x, int src_y, int *dest_x_return, int *dest_y_return, Window *child_return)) {
ensure_XTranslateCoordinates();
if (window_loaded) {
return (*real_XTranslateCoordinates)(x11_display, x11_window, x11_root_window, src_x, src_y, dest_x_return, dest_y_return, child_return);
} else {
return (*real_XTranslateCoordinates)(display, src_w, dest_w, src_x, src_y, dest_x_return, dest_y_return, child_return);
}
}
HOOK(XGetWindowAttributes, int, (Display *display, Window w, XWindowAttributes *window_attributes_return)) {
ensure_XGetWindowAttributes();
if (window_loaded) {
return (*real_XGetWindowAttributes)(x11_display, x11_window, window_attributes_return);
} else {
return (*real_XGetWindowAttributes)(display, w, window_attributes_return);
}
}
#include <stdlib.h>
// Use VirGL
__attribute__((constructor)) static void init() {
setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
setenv("GALLIUM_DRIVER", "virpipe", 1);
}