Merge and add requested changes
This commit is contained in:
commit
375c4be7ae
2
dependencies/LIEF/src
vendored
2
dependencies/LIEF/src
vendored
@ -1 +1 @@
|
||||
Subproject commit 16962f2f36a51b2acefad9cec3622f6de5730aa3
|
||||
Subproject commit bae887e095d87e756d1bf4aa4f95a97693a97b62
|
@ -48,6 +48,9 @@
|
||||
* `Click Buttons On Mouse Down` (Enabled By Default)
|
||||
* `3D Dropped Items` (Enabled By Default)
|
||||
* `Render Entity Shadows` (Enabled By Default)
|
||||
* `Render Vignette` (Enabled By Default)
|
||||
* `Increase Render Chunk Size` (Enabled By Default)
|
||||
* `Proper Entity Shading` (Enabled By Default)
|
||||
* Existing Functionality (All Enabled By Default)
|
||||
* `Fix Screen Rendering When Hiding HUD`
|
||||
* `Sanitize Usernames`
|
||||
|
@ -101,22 +101,57 @@ void bootstrap(const options_t &options) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fix MCPI Dependencies
|
||||
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
|
||||
std::string linker;
|
||||
// Configure Preloaded Objects
|
||||
std::vector<std::string> mcpi_ld_preload;
|
||||
{
|
||||
// Log
|
||||
DEBUG("Patching ELF Dependencies...");
|
||||
DEBUG("Locating Mods...");
|
||||
|
||||
// ARM Components
|
||||
mcpi_ld_preload = bootstrap_mods(binary_directory);
|
||||
}
|
||||
|
||||
// Configure Library Search Path
|
||||
std::vector<std::string> mcpi_ld_path;
|
||||
{
|
||||
// Log
|
||||
DEBUG("Setting Linker Search Paths...");
|
||||
|
||||
// Library Search Path For ARM Components
|
||||
{
|
||||
// Add ARM Library Directory
|
||||
mcpi_ld_path.push_back("lib/arm");
|
||||
|
||||
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
|
||||
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
||||
mcpi_ld_path.push_back("sysroot/lib");
|
||||
mcpi_ld_path.push_back("sysroot/lib/arm-linux-gnueabihf");
|
||||
mcpi_ld_path.push_back("sysroot/usr/lib");
|
||||
mcpi_ld_path.push_back("sysroot/usr/lib/arm-linux-gnueabihf");
|
||||
#endif
|
||||
|
||||
// Fix Paths
|
||||
for (std::string &path : mcpi_ld_path) {
|
||||
path = binary_directory + '/' + path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix MCPI Dependencies
|
||||
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
|
||||
{
|
||||
// Log
|
||||
DEBUG("Patching ELF...");
|
||||
|
||||
// Find Linker
|
||||
linker = "/lib/ld-linux-armhf.so.3";
|
||||
std::string linker = "/lib/ld-linux-armhf.so.3";
|
||||
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
||||
// Use ARM Sysroot Linker
|
||||
linker = binary_directory + "/sysroot" + linker;
|
||||
#endif
|
||||
|
||||
// Patch
|
||||
patch_mcpi_elf_dependencies(game_binary.c_str(), new_mcpi_exe_path);
|
||||
patch_mcpi_elf_dependencies(game_binary, new_mcpi_exe_path, linker, mcpi_ld_path, mcpi_ld_preload);
|
||||
|
||||
// Verify
|
||||
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
|
||||
@ -132,37 +167,6 @@ void bootstrap(const options_t &options) {
|
||||
set_and_print_env(_MCPI_VANILLA_ASSETS_PATH_ENV, assets_path.c_str());
|
||||
}
|
||||
|
||||
// Configure Library Search Path
|
||||
std::string mcpi_ld_path = "";
|
||||
{
|
||||
// Log
|
||||
DEBUG("Setting Linker Search Paths...");
|
||||
|
||||
// Library Search Path For ARM Components
|
||||
{
|
||||
// Add ARM Library Directory
|
||||
mcpi_ld_path += binary_directory + "/lib/arm:";
|
||||
|
||||
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
|
||||
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
||||
mcpi_ld_path += binary_directory + "/sysroot/lib:";
|
||||
mcpi_ld_path += binary_directory + "/sysroot/lib/arm-linux-gnueabihf:";
|
||||
mcpi_ld_path += binary_directory + "/sysroot/usr/lib:";
|
||||
mcpi_ld_path += binary_directory + "/sysroot/usr/lib/arm-linux-gnueabihf:";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Configure Preloaded Objects
|
||||
std::string mcpi_ld_preload;
|
||||
{
|
||||
// Log
|
||||
DEBUG("Locating Mods...");
|
||||
|
||||
// ARM Components
|
||||
mcpi_ld_preload = bootstrap_mods(binary_directory);
|
||||
}
|
||||
|
||||
// Start Game
|
||||
INFO("Starting Game...");
|
||||
|
||||
@ -178,13 +182,6 @@ void bootstrap(const options_t &options) {
|
||||
args.push_back("0x40000"); // Arbitrary Value (Aligns To 4k And 16k Page Sizes)
|
||||
#endif
|
||||
|
||||
// Setup Linker
|
||||
args.push_back(linker);
|
||||
args.push_back("--library-path");
|
||||
args.push_back(mcpi_ld_path);
|
||||
args.push_back("--preload");
|
||||
args.push_back(mcpi_ld_preload);
|
||||
|
||||
// Specify MCPI Binary
|
||||
args.push_back(new_mcpi_exe_path);
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "options/parser.h"
|
||||
|
||||
void bootstrap(const options_t &options);
|
||||
void copy_sdk(const std::string &binary_directory, bool log_with_debug);
|
||||
std::string bootstrap_mods(const std::string &binary_directory);
|
||||
std::vector<std::string> bootstrap_mods(const std::string &binary_directory);
|
||||
|
@ -113,3 +113,5 @@ TRUE 3D Dropped Items
|
||||
TRUE Render Entity Shadows
|
||||
TRUE Render Vignette
|
||||
TRUE Implement RaspberryJuice API
|
||||
TRUE Increase Render Chunk Size
|
||||
TRUE Proper Entity Shading
|
||||
|
@ -8,14 +8,14 @@
|
||||
#include "bootstrap.h"
|
||||
|
||||
// Get All Mods In Folder
|
||||
static void load(std::string &ld_preload, const std::string &folder, int recursion_limit = 128);
|
||||
static void handle_file(std::string &ld_preload, const std::string &file, const int recursion_limit) {
|
||||
static void load(std::vector<std::string> &ld_preload, const std::string &folder, int recursion_limit = 128);
|
||||
static void handle_file(std::vector<std::string> &ld_preload, const std::string &file, const int recursion_limit) {
|
||||
// Check Type
|
||||
struct stat file_stat = {};
|
||||
lstat(file.c_str(), &file_stat);
|
||||
if (S_ISDIR(file_stat.st_mode)) {
|
||||
// Recurse Into Directory
|
||||
load(ld_preload, std::string(file) + "/", recursion_limit - 1);
|
||||
load(ld_preload, std::string(file) + '/', recursion_limit - 1);
|
||||
} else if (S_ISLNK(file_stat.st_mode)) {
|
||||
// Resolve Symlink
|
||||
char *resolved_file = realpath(file.c_str(), nullptr);
|
||||
@ -27,7 +27,8 @@ static void handle_file(std::string &ld_preload, const std::string &file, const
|
||||
const int result = access(file.c_str(), R_OK);
|
||||
if (result == 0) {
|
||||
// Add To LD_PRELOAD
|
||||
ld_preload += file + ":";
|
||||
DEBUG("Found Mod: %s", file.c_str());
|
||||
ld_preload.push_back(file);
|
||||
} else if (result == -1 && errno != 0) {
|
||||
// Fail
|
||||
WARN("Unable To Access: %s: %s", file.c_str(), strerror(errno));
|
||||
@ -35,7 +36,7 @@ static void handle_file(std::string &ld_preload, const std::string &file, const
|
||||
}
|
||||
}
|
||||
}
|
||||
static void load(std::string &ld_preload, const std::string &folder, const int recursion_limit) {
|
||||
static void load(std::vector<std::string> &ld_preload, const std::string &folder, const int recursion_limit) {
|
||||
// Check Recursion
|
||||
if (recursion_limit <= 0) {
|
||||
ERR("Reached Recursion Limit While Loading Mods");
|
||||
@ -74,9 +75,9 @@ static void load(std::string &ld_preload, const std::string &folder, const int r
|
||||
|
||||
// Bootstrap Mods
|
||||
#define SUBDIRECTORY_FOR_MODS "/mods/"
|
||||
std::string bootstrap_mods(const std::string &binary_directory) {
|
||||
std::vector<std::string> bootstrap_mods(const std::string &binary_directory) {
|
||||
// Prepare
|
||||
std::string preload = "";
|
||||
std::vector<std::string> preload;
|
||||
|
||||
// ~/.minecraft-pi/mods
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <cstdlib>
|
||||
#include <sys/stat.h>
|
||||
#include <ranges>
|
||||
|
||||
#include <LIEF/ELF.hpp>
|
||||
|
||||
@ -16,7 +17,7 @@ static void duplicate_mcpi_executable(char *new_path) {
|
||||
ensure_directory(MCPI_PATCHED_DIR);
|
||||
|
||||
// Generate New File
|
||||
int new_file_fd = mkstemp(new_path);
|
||||
const int new_file_fd = mkstemp(new_path);
|
||||
if (new_file_fd == -1) {
|
||||
ERR("Unable To Create Temporary File: %s", strerror(errno));
|
||||
}
|
||||
@ -24,34 +25,72 @@ static void duplicate_mcpi_executable(char *new_path) {
|
||||
}
|
||||
|
||||
// Fix MCPI Dependencies
|
||||
static const char *libraries_to_remove[] = {
|
||||
"libbcm_host.so",
|
||||
"libX11.so.6",
|
||||
"libEGL.so",
|
||||
"libGLESv2.so",
|
||||
"libSDL-1.2.so.0"
|
||||
static std::vector<std::string> needed_libraries = {
|
||||
"libmedia-layer-core.so",
|
||||
"libpng12.so.0",
|
||||
"libstdc++.so.6",
|
||||
"libm.so.6",
|
||||
"libgcc_s.so.1",
|
||||
"libc.so.6",
|
||||
"libpthread.so.0"
|
||||
};
|
||||
static const char *libraries_to_add[] = {
|
||||
"libmedia-layer-core.so"
|
||||
static std::vector<std::string> function_prefixes_to_patch = {
|
||||
"SDL_",
|
||||
"gl"
|
||||
};
|
||||
void patch_mcpi_elf_dependencies(const char *original_path, char *new_path) {
|
||||
void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_path, const std::string &interpreter, const std::vector<std::string> &rpath, const std::vector<std::string> &mods) {
|
||||
// Duplicate MCPI executable into /tmp so it can be modified.
|
||||
duplicate_mcpi_executable(new_path);
|
||||
|
||||
// Patch File
|
||||
{
|
||||
std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path);
|
||||
for (size_t i = 0; i < (sizeof (libraries_to_remove) / sizeof (const char *)); i++) {
|
||||
binary->remove_library(libraries_to_remove[i]);
|
||||
}
|
||||
for (size_t i = 0; i < (sizeof (libraries_to_add) / sizeof (const char *)); i++) {
|
||||
binary->add_library(libraries_to_add[i]);
|
||||
}
|
||||
LIEF::ELF::Builder builder{*binary};
|
||||
builder.build();
|
||||
builder.write(new_path);
|
||||
// Load Binary
|
||||
std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path);
|
||||
|
||||
// Set Interpreter
|
||||
if (!interpreter.empty()) {
|
||||
binary->interpreter(interpreter);
|
||||
}
|
||||
|
||||
// Remove Existing Needed Libraries
|
||||
std::vector<std::string> to_remove;
|
||||
for (const LIEF::ELF::DynamicEntry &entry : binary->dynamic_entries()) {
|
||||
const LIEF::ELF::DynamicEntryLibrary *library = dynamic_cast<const LIEF::ELF::DynamicEntryLibrary *>(&entry);
|
||||
if (library) {
|
||||
to_remove.push_back(library->name());
|
||||
}
|
||||
}
|
||||
for (const std::string &library : to_remove) {
|
||||
binary->remove_library(library);
|
||||
}
|
||||
|
||||
// Setup RPath
|
||||
binary->add(LIEF::ELF::DynamicEntryRpath(rpath));
|
||||
|
||||
// Add Libraries
|
||||
std::vector<std::string> all_libraries;
|
||||
for (const std::vector<std::string> &list : {mods, needed_libraries}) {
|
||||
all_libraries.insert(all_libraries.end(), list.begin(), list.end());
|
||||
}
|
||||
for (const std::string &library : all_libraries | std::views::reverse) {
|
||||
binary->add_library(library);
|
||||
}
|
||||
|
||||
// Fix Symbol Names
|
||||
for (LIEF::ELF::Symbol &symbol : binary->dynamic_symbols()) {
|
||||
if (symbol.is_function()) {
|
||||
for (const std::string &prefix : function_prefixes_to_patch) {
|
||||
if (symbol.name().rfind(prefix, 0) == 0) {
|
||||
symbol.name("media_" + symbol.name());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write Binary
|
||||
LIEF::ELF::Builder builder{*binary};
|
||||
builder.build();
|
||||
builder.write(new_path);
|
||||
|
||||
// Fix Permissions
|
||||
if (chmod(new_path, S_IRUSR | S_IXUSR) != 0) {
|
||||
ERR("Unable To Set File Permissions: %s: %s", new_path, strerror(errno));
|
||||
|
@ -1,13 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define MCPI_PATCHED_DIR "/tmp/.minecraft-pi-patched"
|
||||
|
||||
void patch_mcpi_elf_dependencies(const char *original_path, char *new_path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_path, const std::string &interpreter, const std::vector<std::string> &rpath, const std::vector<std::string> &mods);
|
||||
|
@ -10,7 +10,7 @@
|
||||
void reborn_init_patch();
|
||||
|
||||
// Replace Call Located At start With A Call To target
|
||||
void overwrite_call(void *start, void *target);
|
||||
void overwrite_call(void *start, void *target, bool force_b_instruction = false);
|
||||
|
||||
// Replace All Calls To Method start With target
|
||||
void *overwrite_calls_manual(void *start, void *target, bool allow_no_callsites = false);
|
||||
|
@ -21,8 +21,8 @@ static void _overwrite_call_internal(void *start, void *target, const bool use_b
|
||||
// Increment Code Block Position
|
||||
increment_code_block();
|
||||
}
|
||||
void overwrite_call(void *start, void *target) {
|
||||
const bool use_b_instruction = ((unsigned char *) start)[3] == B_INSTRUCTION;
|
||||
void overwrite_call(void *start, void *target, const bool force_b_instruction) {
|
||||
const bool use_b_instruction = force_b_instruction || ((unsigned char *) start)[3] == B_INSTRUCTION;
|
||||
_overwrite_call_internal(start, target, use_b_instruction);
|
||||
}
|
||||
|
||||
|
@ -19,9 +19,6 @@ if(BUILD_ARM_COMPONENTS)
|
||||
)
|
||||
endif()
|
||||
|
||||
# Add Extras
|
||||
add_subdirectory(extras)
|
||||
|
||||
# Add Core
|
||||
if(BUILD_MEDIA_LAYER_CORE)
|
||||
add_subdirectory(gles)
|
||||
|
@ -4,10 +4,12 @@ project(media-layer-core)
|
||||
set(CORE_SRC
|
||||
src/base.cpp
|
||||
src/media.cpp
|
||||
src/cursor.cpp
|
||||
src/util.cpp
|
||||
src/events.cpp
|
||||
src/audio/api.cpp
|
||||
src/audio/engine.c
|
||||
src/audio/file.cpp
|
||||
$<TARGET_OBJECTS:media-layer-extras>
|
||||
)
|
||||
|
||||
# Build
|
||||
|
@ -2,13 +2,14 @@
|
||||
|
||||
#include <SDL/SDL.h>
|
||||
|
||||
#include <media-layer/internal.h>
|
||||
#include <media-layer/core.h>
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#include "media.h"
|
||||
|
||||
// SDL Is Replaced With GLFW
|
||||
|
||||
int SDL_Init(__attribute__((unused)) uint32_t flags) {
|
||||
int media_SDL_Init(__attribute__((unused)) uint32_t flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -16,9 +17,9 @@ int SDL_Init(__attribute__((unused)) uint32_t flags) {
|
||||
|
||||
static std::vector<SDL_Event> queue;
|
||||
|
||||
int SDL_PollEvent(SDL_Event *event) {
|
||||
int media_SDL_PollEvent(SDL_Event *event) {
|
||||
// Handle External Events
|
||||
_media_handle_SDL_PollEvent();
|
||||
_media_handle_media_SDL_PollEvent();
|
||||
|
||||
// Poll Event
|
||||
int ret;
|
||||
@ -32,7 +33,7 @@ int SDL_PollEvent(SDL_Event *event) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int SDL_PushEvent(SDL_Event *event) {
|
||||
int media_SDL_PushEvent(SDL_Event *event) {
|
||||
queue.push_back(*event);
|
||||
return 1;
|
||||
}
|
||||
|
108
media-layer/core/src/cursor.cpp
Normal file
108
media-layer/core/src/cursor.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "media.h"
|
||||
|
||||
// Enable/Disable Raw Mouse Motion
|
||||
bool raw_mouse_motion_enabled = true;
|
||||
void media_set_raw_mouse_motion_enabled(const int enabled) {
|
||||
raw_mouse_motion_enabled = enabled;
|
||||
if (glfw_window) {
|
||||
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
|
||||
}
|
||||
if (!raw_mouse_motion_enabled) {
|
||||
WARN("Raw mouse motion has been DISABLED, this IS NOT recommended, and should only ever be used on systems that don't support or have broken raw mouse motion.");
|
||||
}
|
||||
}
|
||||
|
||||
// Store Cursor State
|
||||
static bool cursor_grabbed = false;
|
||||
static bool cursor_visible = true;
|
||||
|
||||
// Ignore Relative Cursor Motion
|
||||
bool ignore_relative_motion = false;
|
||||
|
||||
// Update GLFW Cursor State (Client Only)
|
||||
void _media_update_cursor() {
|
||||
if (glfw_window) {
|
||||
// Get New State
|
||||
const bool new_cursor_visible = is_interactable ? cursor_visible : true;
|
||||
const bool new_cursor_grabbed = is_interactable ? cursor_grabbed : false;
|
||||
|
||||
// Store Old Mode
|
||||
const int old_mode = glfwGetInputMode(glfw_window, GLFW_CURSOR);
|
||||
|
||||
// Handle Cursor Visibility
|
||||
int new_mode;
|
||||
if (!new_cursor_visible) {
|
||||
if (new_cursor_grabbed) {
|
||||
new_mode = GLFW_CURSOR_DISABLED;
|
||||
} else {
|
||||
new_mode = GLFW_CURSOR_HIDDEN;
|
||||
}
|
||||
} else {
|
||||
new_mode = GLFW_CURSOR_NORMAL;
|
||||
}
|
||||
if (new_mode != old_mode) {
|
||||
// Ignore Relative Cursor Motion When Locking
|
||||
if (new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) {
|
||||
ignore_relative_motion = true;
|
||||
}
|
||||
|
||||
// Set New Mode
|
||||
glfwSetInputMode(glfw_window, GLFW_CURSOR, new_mode);
|
||||
|
||||
// Handle Cursor Lock/Unlock
|
||||
if ((new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) || (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED)) {
|
||||
// Use Raw Mouse Motion
|
||||
if (raw_mouse_motion_enabled) {
|
||||
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, new_mode == GLFW_CURSOR_DISABLED ? GLFW_TRUE : GLFW_FALSE);
|
||||
}
|
||||
|
||||
// Request Focus
|
||||
glfwRequestWindowAttention(glfw_window);
|
||||
}
|
||||
|
||||
// Reset Mouse Position When Unlocking
|
||||
if (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED) {
|
||||
double cursor_x;
|
||||
double cursor_y;
|
||||
glfwGetCursorPos(glfw_window, &cursor_x, &cursor_y);
|
||||
_media_glfw_motion(glfw_window, cursor_x, cursor_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix SDL Cursor Visibility/Grabbing
|
||||
SDL_GrabMode media_SDL_WM_GrabInput(const SDL_GrabMode mode) {
|
||||
if (mode == SDL_GRAB_QUERY) {
|
||||
// Query
|
||||
return cursor_grabbed ? SDL_GRAB_ON : SDL_GRAB_OFF;
|
||||
} else if (mode == SDL_GRAB_ON) {
|
||||
// Store State
|
||||
cursor_grabbed = true;
|
||||
} else if (mode == SDL_GRAB_OFF) {
|
||||
// Store State
|
||||
cursor_grabbed = false;
|
||||
}
|
||||
// Update Cursor GLFW State (Client Only)
|
||||
_media_update_cursor();
|
||||
// Return
|
||||
return mode;
|
||||
}
|
||||
|
||||
// Stub SDL Cursor Visibility
|
||||
int media_SDL_ShowCursor(const int toggle) {
|
||||
if (toggle == SDL_QUERY) {
|
||||
// Query
|
||||
return cursor_visible ? SDL_ENABLE : SDL_DISABLE;
|
||||
} else if (toggle == SDL_ENABLE) {
|
||||
// Store State
|
||||
cursor_visible = true;
|
||||
} else if (toggle == SDL_DISABLE) {
|
||||
// Store State
|
||||
cursor_visible = false;
|
||||
}
|
||||
// Update Cursor GLFW State (Client Only)
|
||||
_media_update_cursor();
|
||||
// Return
|
||||
return toggle;
|
||||
}
|
275
media-layer/core/src/events.cpp
Normal file
275
media-layer/core/src/events.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
#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);
|
||||
}
|
@ -1,309 +1,21 @@
|
||||
#include <ctime>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <SDL/SDL.h>
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <media-layer/core.h>
|
||||
#include <media-layer/internal.h>
|
||||
|
||||
#include "media.h"
|
||||
#include "audio/engine.h"
|
||||
|
||||
// Allow Disabling Interaction
|
||||
static void update_cursor();
|
||||
static int is_interactable = 1;
|
||||
void media_set_interactable(const int toggle) {
|
||||
if (bool(toggle) != is_interactable) {
|
||||
is_interactable = toggle;
|
||||
update_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
// Store Cursor State
|
||||
static bool cursor_grabbed = false;
|
||||
static bool cursor_visible = true;
|
||||
|
||||
// Track If Raw Mouse Motion Is Enabled
|
||||
static bool raw_mouse_motion_enabled = true;
|
||||
|
||||
// Window
|
||||
static GLFWwindow *glfw_window = nullptr;
|
||||
GLFWwindow *glfw_window = nullptr;
|
||||
|
||||
// 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(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);
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
// Last Mouse Location
|
||||
static double last_mouse_x = 0;
|
||||
static double last_mouse_y = 0;
|
||||
// Ignore Relative Cursor Motion
|
||||
static bool ignore_relative_motion = false;
|
||||
|
||||
// Convert Screen Coordinates To Pixels
|
||||
static void convert_to_pixels(GLFWwindow *window, double *xpos, double *ypos) {
|
||||
// Skip If Cursor Is Grabbed
|
||||
if (cursor_grabbed && 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;
|
||||
}
|
||||
|
||||
// Pass Mouse Movement To SDL
|
||||
static void 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 = xpos;
|
||||
event.motion.y = ypos;
|
||||
event.motion.xrel = !ignore_relative_motion ? (xpos - last_mouse_x) : 0;
|
||||
event.motion.yrel = !ignore_relative_motion ? (ypos - last_mouse_y) : 0;
|
||||
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 = 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_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);
|
||||
}
|
||||
}
|
||||
|
||||
// Enable/Disable Raw Mouse Motion
|
||||
void media_set_raw_mouse_motion_enabled(const int enabled) {
|
||||
raw_mouse_motion_enabled = enabled;
|
||||
if (glfw_window) {
|
||||
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
|
||||
}
|
||||
if (!raw_mouse_motion_enabled) {
|
||||
WARN("Raw mouse motion has been DISABLED, this IS NOT recommended, and should only ever be used on systems that don't support or have broken raw mouse motion.");
|
||||
}
|
||||
}
|
||||
|
||||
// Disable V-Sync
|
||||
static int disable_vsync = 0;
|
||||
static bool disable_vsync = false;
|
||||
void media_disable_vsync() {
|
||||
disable_vsync = 1;
|
||||
disable_vsync = true;
|
||||
if (glfw_window) {
|
||||
glfwSwapInterval(0);
|
||||
}
|
||||
@ -321,7 +33,7 @@ void media_force_egl() {
|
||||
// Init Media Layer
|
||||
#define GL_VERSION 0x1f02
|
||||
typedef const char *(*glGetString_t)(unsigned int name);
|
||||
void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) {
|
||||
void media_SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) {
|
||||
// Disable In Headless Mode
|
||||
if (reborn_is_headless()) {
|
||||
return;
|
||||
@ -356,11 +68,7 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic
|
||||
}
|
||||
|
||||
// Event Handlers
|
||||
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);
|
||||
_media_register_event_listeners();
|
||||
|
||||
// Make Window Context Current
|
||||
glfwMakeContextCurrent(glfw_window);
|
||||
@ -373,7 +81,7 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic
|
||||
_media_audio_init();
|
||||
|
||||
// Update State
|
||||
update_cursor();
|
||||
_media_update_cursor();
|
||||
if (disable_vsync) {
|
||||
media_disable_vsync();
|
||||
}
|
||||
@ -382,58 +90,6 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic
|
||||
atexit(media_cleanup);
|
||||
}
|
||||
|
||||
void media_swap_buffers() {
|
||||
if (glfw_window) {
|
||||
glfwSwapBuffers(glfw_window);
|
||||
}
|
||||
}
|
||||
|
||||
// Track Fullscreen
|
||||
static bool is_fullscreen = false;
|
||||
// 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 (glfw_window) {
|
||||
if (is_fullscreen) {
|
||||
glfwSetWindowMonitor(glfw_window, nullptr, 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Intercept SDL Events
|
||||
void _media_handle_SDL_PollEvent() {
|
||||
if (glfw_window) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup Media Layer
|
||||
void media_cleanup() {
|
||||
if (glfw_window) {
|
||||
@ -450,113 +106,4 @@ void media_cleanup() {
|
||||
// Mark As Stopped
|
||||
glfw_window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Update GLFW Cursor State (Client Only)
|
||||
static void update_cursor() {
|
||||
if (glfw_window) {
|
||||
// Get New State
|
||||
const bool new_cursor_visible = is_interactable ? cursor_visible : true;
|
||||
const bool new_cursor_grabbed = is_interactable ? cursor_grabbed : false;
|
||||
|
||||
// Store Old Mode
|
||||
const int old_mode = glfwGetInputMode(glfw_window, GLFW_CURSOR);
|
||||
|
||||
// Handle Cursor Visibility
|
||||
int new_mode;
|
||||
if (!new_cursor_visible) {
|
||||
if (new_cursor_grabbed) {
|
||||
new_mode = GLFW_CURSOR_DISABLED;
|
||||
} else {
|
||||
new_mode = GLFW_CURSOR_HIDDEN;
|
||||
}
|
||||
} else {
|
||||
new_mode = GLFW_CURSOR_NORMAL;
|
||||
}
|
||||
if (new_mode != old_mode) {
|
||||
// Ignore Relative Cursor Motion When Locking
|
||||
if (new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) {
|
||||
ignore_relative_motion = true;
|
||||
}
|
||||
|
||||
// Set New Mode
|
||||
glfwSetInputMode(glfw_window, GLFW_CURSOR, new_mode);
|
||||
|
||||
// Handle Cursor Lock/Unlock
|
||||
if ((new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) || (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED)) {
|
||||
// Use Raw Mouse Motion
|
||||
if (raw_mouse_motion_enabled) {
|
||||
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, new_mode == GLFW_CURSOR_DISABLED ? GLFW_TRUE : GLFW_FALSE);
|
||||
}
|
||||
|
||||
// Request Focus
|
||||
if (!glfwGetWindowAttrib(glfw_window, GLFW_FOCUSED)) {
|
||||
glfwRequestWindowAttention(glfw_window);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset Mouse Position When Unlocking
|
||||
if (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED) {
|
||||
double cursor_x;
|
||||
double cursor_y;
|
||||
glfwGetCursorPos(glfw_window, &cursor_x, &cursor_y);
|
||||
glfw_motion(glfw_window, cursor_x, cursor_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix SDL Cursor Visibility/Grabbing
|
||||
SDL_GrabMode SDL_WM_GrabInput(const SDL_GrabMode mode) {
|
||||
if (mode == SDL_GRAB_QUERY) {
|
||||
// Query
|
||||
return cursor_grabbed ? SDL_GRAB_ON : SDL_GRAB_OFF;
|
||||
} else if (mode == SDL_GRAB_ON) {
|
||||
// Store State
|
||||
cursor_grabbed = true;
|
||||
} else if (mode == SDL_GRAB_OFF) {
|
||||
// Store State
|
||||
cursor_grabbed = false;
|
||||
}
|
||||
// Update Cursor GLFW State (Client Only)
|
||||
update_cursor();
|
||||
// Return
|
||||
return mode;
|
||||
}
|
||||
|
||||
// Stub SDL Cursor Visibility
|
||||
int SDL_ShowCursor(const int toggle) {
|
||||
if (toggle == SDL_QUERY) {
|
||||
// Query
|
||||
return cursor_visible ? SDL_ENABLE : SDL_DISABLE;
|
||||
} else if (toggle == SDL_ENABLE) {
|
||||
// Store State
|
||||
cursor_visible = true;
|
||||
} else if (toggle == SDL_DISABLE) {
|
||||
// Store State
|
||||
cursor_visible = false;
|
||||
}
|
||||
// Update Cursor GLFW State (Client Only)
|
||||
update_cursor();
|
||||
// Return
|
||||
return toggle;
|
||||
}
|
||||
|
||||
// Get Framebuffer Size
|
||||
void media_get_framebuffer_size(int *width, int *height) {
|
||||
if (glfw_window) {
|
||||
glfwGetFramebufferSize(glfw_window, width, height);
|
||||
} else {
|
||||
*width = DEFAULT_WIDTH;
|
||||
*height = DEFAULT_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
// Check OpenGL Extension
|
||||
int media_has_extension(const char *name) {
|
||||
if (glfw_window) {
|
||||
return glfwExtensionSupported(name);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
25
media-layer/core/src/media.h
Normal file
25
media-layer/core/src/media.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL/SDL.h>
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <media-layer/core.h>
|
||||
|
||||
// Interactivity
|
||||
__attribute__((visibility("internal"))) extern bool is_interactable;
|
||||
|
||||
// Window
|
||||
__attribute__((visibility("internal"))) extern GLFWwindow *glfw_window;
|
||||
|
||||
// Cursor
|
||||
__attribute__((visibility("internal"))) void _media_update_cursor();
|
||||
__attribute__((visibility("internal"))) extern bool ignore_relative_motion;
|
||||
__attribute__((visibility("internal"))) extern bool raw_mouse_motion_enabled;
|
||||
|
||||
// Events
|
||||
__attribute__((visibility("internal"))) void _media_register_event_listeners();
|
||||
__attribute__((visibility("internal"))) void _media_handle_media_SDL_PollEvent();
|
||||
__attribute__((visibility("internal"))) void _media_glfw_motion(GLFWwindow *window, double xpos, double ypos);
|
67
media-layer/core/src/util.cpp
Normal file
67
media-layer/core/src/util.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include "media.h"
|
||||
|
||||
// Allow Disabling Interaction
|
||||
bool is_interactable = true;
|
||||
void media_set_interactable(const int toggle) {
|
||||
if (bool(toggle) != is_interactable) {
|
||||
is_interactable = toggle;
|
||||
_media_update_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
// Get Framebuffer Size
|
||||
void media_get_framebuffer_size(int *width, int *height) {
|
||||
if (glfw_window) {
|
||||
glfwGetFramebufferSize(glfw_window, width, height);
|
||||
} else {
|
||||