diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d8dc2e..b301179 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.13.0) # Specify Options option(MCPI_USE_MEDIA_LAYER_PROXY "Whether To Enable The Media Layer Proxy" FALSE) option(MCPI_SERVER_MODE "Server Mode" FALSE) +option(MCPI_HEADLESS_MODE "Headless Mode" ${MCPI_SERVER_MODE}) set(MCPI_BUILD_MODE "both" CACHE STRING "\"arm\" = Build Only Code That Must Be ARM; \"native\" = Build Architecture-Independent Code; \"both\" = Build All Code As ARM") set_property(CACHE MCPI_BUILD_MODE PROPERTY STRINGS "both" "arm" "native") @@ -95,6 +96,9 @@ set(CMAKE_CXX_STANDARD 11) if(MCPI_SERVER_MODE) add_definitions(-DMCPI_SERVER_MODE) endif() +if(MCPI_HEADLESS_MODE) + add_definitions(-DMCPI_HEADLESS_MODE) +endif() # Version file(STRINGS VERSION VERSION) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 92c3bf8..640f566 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -8,6 +8,9 @@ * ``MCPI_SERVER_MODE`` * ``ON``: Enable Server Mode * ``OFF`` (Default): Disable Server Mode +* ``MCPI_HEADLESS_MODE`` + * ``ON`` (Default In Server Mode): Enable Headless Mode + * ``OFF`` (Default In Client Mode): Disable Headless Mode * ``MCPI_USE_MEDIA_LAYER_PROXY`` * ``ON``: Enable The Media Layer Proxy * ``OFF`` (Default): Disable The Media Layer Proxy diff --git a/libreborn/include/libreborn/log.h b/libreborn/include/libreborn/log.h index fad7ca0..7350d03 100644 --- a/libreborn/include/libreborn/log.h +++ b/libreborn/include/libreborn/log.h @@ -7,3 +7,4 @@ #define INFO(format, ...) { fprintf(stderr, "[INFO]: " format "\n", __VA_ARGS__); } #define WARN(format, ...) { fprintf(stderr, "[WARN]: " format "\n", __VA_ARGS__); } #define ERR(format, ...) { fprintf(stderr, "[ERR]: " format "\n", __VA_ARGS__); exit(EXIT_FAILURE); } +#define IMPOSSIBLE() ERR("(%s:%i) This Should Never Be Called", __FILE__, __LINE__) diff --git a/media-layer/CMakeLists.txt b/media-layer/CMakeLists.txt index a0812e2..6d7ac18 100644 --- a/media-layer/CMakeLists.txt +++ b/media-layer/CMakeLists.txt @@ -2,8 +2,8 @@ project(media-layer) # Check Options if(MCPI_USE_MEDIA_LAYER_PROXY) - if(MCPI_SERVER_MODE) - message(FATAL_ERROR "Server Mode With Media Layer Proxy Configuration Is Redundant") + if(MCPI_HEADLESS_MODE) + message(FATAL_ERROR "Headless Mode With Media Layer Proxy Configuration Is Redundant") endif() if(MCPI_BUILD_MODE STREQUAL "both") message(FATAL_ERROR "Media Layer Proxy Is Redundant When Building ARM And Native Components In The Same Build") diff --git a/media-layer/core/CMakeLists.txt b/media-layer/core/CMakeLists.txt index 26a8af7..bd70237 100644 --- a/media-layer/core/CMakeLists.txt +++ b/media-layer/core/CMakeLists.txt @@ -18,7 +18,7 @@ endif() if(TARGET media-layer-core) # Link target_link_libraries(media-layer-core media-layer-headers reborn-headers pthread dl) - if(NOT MCPI_SERVER_MODE) + if(NOT MCPI_HEADLESS_MODE) # Find GLFW find_package(glfw3 3.3 REQUIRED) # Find FreeImage diff --git a/media-layer/core/src/media.c b/media-layer/core/src/media.c index 0ebc296..f23c821 100644 --- a/media-layer/core/src/media.c +++ b/media-layer/core/src/media.c @@ -3,17 +3,17 @@ #include #include -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE #define GLFW_INCLUDE_NONE #include -#endif // #ifndef MCPI_SERVER_MODE +#endif // #ifndef MCPI_HEADLESS_MODE #include #include #include // GLFW Code Not Needed In Server Mode -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE static GLFWwindow *glfw_window; @@ -194,12 +194,12 @@ static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute_ } } -#endif // #ifndef MCPI_SERVER_MODE +#endif // #ifndef MCPI_HEADLESS_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 +#ifndef MCPI_HEADLESS_MODE glfwSetErrorCallback(glfw_error); if (!glfwInit()) { @@ -229,20 +229,20 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic glfwSetScrollCallback(glfw_window, glfw_scroll); glfwMakeContextCurrent(glfw_window); -#else // #ifndef MCPI_SERVER_MODE +#else // #ifndef MCPI_HEADLESS_MODE (void) title; // Mark As Used -#endif // #ifndef MCPI_SERVER_MODE +#endif // #ifndef MCPI_HEADLESS_MODE } void media_swap_buffers() { -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE // Don't Swap Buffers In A Context-Less Window glfwSwapBuffers(glfw_window); -#endif // #ifndef MCPI_SERVER_MODE +#endif // #ifndef MCPI_HEADLESS_MODE } // Fullscreen Not Needed In Server Mode -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE static int is_fullscreen = 0; // Old Size And Position To Use When Exiting Fullscreen @@ -271,15 +271,15 @@ void media_toggle_fullscreen() { } is_fullscreen = !is_fullscreen; } -#else // #ifndef MCPI_SERVER_MODE +#else // #ifndef MCPI_HEADLESS_MODE void media_toggle_fullscreen() { } -#endif // #ifndef MCPI_SERVER_MODE +#endif // #ifndef MCPI_HEADLESS_MODE // Intercept SDL Events void _media_handle_SDL_PollEvent() { // GLFW Is Disabled In Server Mode -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE // Process GLFW Events glfwPollEvents(); @@ -290,16 +290,16 @@ void _media_handle_SDL_PollEvent() { SDL_PushEvent(&event); glfwSetWindowShouldClose(glfw_window, GLFW_FALSE); } -#endif // #ifndef MCPI_SERVER_MODE +#endif // #ifndef MCPI_HEADLESS_MODE } // Terminate GLFW void media_cleanup() { // GLFW Is Disabled In Server Mode -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE glfwDestroyWindow(glfw_window); glfwTerminate(); -#endif // #ifndef MCPI_SERVER_MODE +#endif // #ifndef MCPI_HEADLESS_MODE } // Store Cursor State @@ -307,7 +307,7 @@ static int cursor_grabbed = 0; static int cursor_visible = 1; // Update GLFW Cursor State (Client Only) -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE static void update_glfw_cursor() { // Store Old Mode int old_mode = glfwGetInputMode(glfw_window, GLFW_CURSOR); @@ -352,7 +352,7 @@ SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode) { cursor_grabbed = 0; } // Update Cursor GLFW State (Client Only) -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE update_glfw_cursor(); #endif // Return @@ -372,7 +372,7 @@ int SDL_ShowCursor(int toggle) { cursor_visible = 0; } // Update Cursor GLFW State (Client Only) -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE update_glfw_cursor(); #endif // Return @@ -381,12 +381,12 @@ int SDL_ShowCursor(int toggle) { // Get Framebuffer Size void media_get_framebuffer_size(int *width, int *height) { -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE if (glfw_window != NULL) { glfwGetFramebufferSize(glfw_window, width, height); return; } -#endif // #ifndef MCPI_SERVER_MODE +#endif // #ifndef MCPI_HEADLESS_MODE *width = DEFAULT_WIDTH; *height = DEFAULT_HEIGHT; } diff --git a/media-layer/core/src/screenshot.c b/media-layer/core/src/screenshot.c index 992b199..d388980 100644 --- a/media-layer/core/src/screenshot.c +++ b/media-layer/core/src/screenshot.c @@ -1,5 +1,5 @@ // Screenshot Code Is Useless In Server Mode -#ifndef MCPI_SERVER_MODE +#ifndef MCPI_HEADLESS_MODE #include #include @@ -16,22 +16,41 @@ #include #include +// Ensure Screenshots Folder Exists +static void ensure_screenshots_folder(char *screenshots) { + // Check Screenshots Folder + struct stat obj; + if (stat(screenshots, &obj) != 0 || !S_ISDIR(obj.st_mode)) { + // Create Screenshots Folder + int ret = mkdir(screenshots, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (ret != 0) { + // Unable To Create Folder + ERR("Error Creating Directory: %s: %s", screenshots, strerror(errno)); + } + } +} + // 4 (Year) + 1 (Hyphen) + 2 (Month) + 1 (Hyphen) + 2 (Day) + 1 (Underscore) + 2 (Hour) + 1 (Period) + 2 (Minute) + 1 (Period) + 2 (Second) + 1 (Null Terminator) #define TIME_SIZE 20 // Take Screenshot -void media_take_screenshot() { +void media_take_screenshot(char *home) { + // Get Directory + char *screenshots = NULL; + safe_asprintf(&screenshots, "%s/screenshots", home); + + // Get Timestamp 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; - safe_asprintf(&screenshots, "%s/.minecraft-pi/screenshots", getenv("HOME")); + // Ensure Screenshots Folder Exists + ensure_screenshots_folder(screenshots); + // Prevent Overwriting Screenshots int num = 1; char *file = NULL; safe_asprintf(&file, "%s/%s.png", screenshots, time); @@ -42,6 +61,7 @@ void media_take_screenshot() { num++; } + // Get Image Size GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); int x = viewport[0]; @@ -49,12 +69,15 @@ void media_take_screenshot() { int width = viewport[2]; int height = viewport[3]; + // Get Line Size int line_size = width * 3; int size = height * line_size; + // Read Pixels unsigned char pixels[size]; glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels); + // Handle Little Endian Systems #if __BYTE_ORDER == __LITTLE_ENDIAN // Swap Red And Blue for (int i = 0; i < (size / 3); i++) { @@ -66,6 +89,7 @@ void media_take_screenshot() { } #endif + // Save Image FIBITMAP *image = FreeImage_ConvertFromRawBits(pixels, width, height, line_size, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, 0); if (!FreeImage_Save(FIF_PNG, image, file, 0)) { INFO("Screenshot Failed: %s", file); @@ -74,6 +98,7 @@ void media_take_screenshot() { } FreeImage_Unload(image); + // Free free(file); free(screenshots); } @@ -82,27 +107,10 @@ void media_take_screenshot() { __attribute__((constructor)) static void init() { // Init FreeImage FreeImage_Initialise(0); - - // Screenshots Folder - char *screenshots_folder = NULL; - safe_asprintf(&screenshots_folder, "%s/.minecraft-pi/screenshots", getenv("HOME")); - { - // Check Screenshots Folder - struct stat obj; - if (stat(screenshots_folder, &obj) != 0 || !S_ISDIR(obj.st_mode)) { - // Create Screenshots Folder - int ret = mkdir(screenshots_folder, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - if (ret != 0) { - // Unable To Create Folder - ERR("Error Creating Directory: %s: %s", screenshots_folder, strerror(errno)); - } - } - } - free(screenshots_folder); } -#else // #ifndef MCPI_SERVER_MODE +#else // #ifndef MCPI_HEADLESS_MODE void media_take_screenshot() { // NOP } -#endif // #ifndef MCPI_SERVER_MODE +#endif // #ifndef MCPI_HEADLESS_MODE diff --git a/media-layer/include/libreborn/media-layer/core.h b/media-layer/include/libreborn/media-layer/core.h index 73efa9e..724cecd 100644 --- a/media-layer/include/libreborn/media-layer/core.h +++ b/media-layer/include/libreborn/media-layer/core.h @@ -8,7 +8,7 @@ extern "C" { #define DEFAULT_WIDTH 840 #define DEFAULT_HEIGHT 480 -void media_take_screenshot(); +void media_take_screenshot(char *home); void media_toggle_fullscreen(); void media_swap_buffers(); void media_cleanup(); diff --git a/media-layer/proxy/CMakeLists.txt b/media-layer/proxy/CMakeLists.txt index 4049af8..b2ae49a 100644 --- a/media-layer/proxy/CMakeLists.txt +++ b/media-layer/proxy/CMakeLists.txt @@ -1,9 +1,3 @@ -# Component Details: -# Media Layer Proxy -# -# This components proxies multi-media calls from the ARM -# MCPI to the native architecture by using a UNIX socket. - project(media-layer-proxy) # Configuration diff --git a/media-layer/proxy/src/media-layer-core.c b/media-layer/proxy/src/media-layer-core.c index 005ae54..73953a9 100644 --- a/media-layer/proxy/src/media-layer-core.c +++ b/media-layer/proxy/src/media-layer-core.c @@ -284,15 +284,22 @@ CALL(6, SDL_ShowCursor, int, (int32_t toggle)) { #endif } -CALL(7, media_take_screenshot, void, ()) { +CALL(7, media_take_screenshot, void, (char *home)) { #if defined(MEDIA_LAYER_PROXY_SERVER) // Lock Proxy start_proxy_call(); + + // Arguments + write_string(home); + // Release Proxy end_proxy_call(); #else + char *home = read_string(); // Run - media_take_screenshot(); + media_take_screenshot(home); + // Free + free(home); #endif } diff --git a/media-layer/stubs/CMakeLists.txt b/media-layer/stubs/CMakeLists.txt index f14602e..656dff8 100644 --- a/media-layer/stubs/CMakeLists.txt +++ b/media-layer/stubs/CMakeLists.txt @@ -21,21 +21,21 @@ if(BUILD_ARM_COMPONENTS) target_link_libraries(X11 reborn-headers media-layer-headers) set_target_properties(X11 PROPERTIES SOVERSION "6") # Install - if(MCPI_SERVER_MODE OR MCPI_USE_MEDIA_LAYER_PROXY) + if(MCPI_HEADLESS_MODE OR MCPI_USE_MEDIA_LAYER_PROXY) install(TARGETS EGL X11 DESTINATION "${MCPI_LIB_DIR}") else() install(TARGETS EGL X11 DESTINATION "${MCPI_FALLBACK_LIB_DIR}") # Place At The End Of LD_LIBRARY_PATH endif() # Install GLESv1_CM Stubs In Server Mode - if(MCPI_SERVER_MODE) + if(MCPI_HEADLESS_MODE) install(TARGETS GLESv1_CM DESTINATION "${MCPI_LIB_DIR}") endif() # MCPI Depends On GLESv2, But Uses GLESv1_CM install_symlink("libGLESv1_CM.so.1" "${MCPI_LIB_DIR}/libGLESv2.so") # Prevent MCPI From Linking To The Legacy GL Driver When Directly Linking To GL - if(NOT MCPI_SERVER_MODE AND NOT MCPI_USE_MEDIA_LAYER_PROXY) + if(NOT MCPI_HEADLESS_MODE AND NOT MCPI_USE_MEDIA_LAYER_PROXY) # Symlinks install_symlink("/usr/lib/arm-linux-gnueabihf/libEGL.so.1" "${MCPI_LIB_DIR}/libEGL.so") install_symlink("/usr/lib/arm-linux-gnueabihf/libGLESv1_CM.so.1" "${MCPI_LIB_DIR}/libGLESv1_CM.so.1") diff --git a/media-layer/stubs/src/EGL.c b/media-layer/stubs/src/EGL.c index 93a442f..41b7821 100644 --- a/media-layer/stubs/src/EGL.c +++ b/media-layer/stubs/src/EGL.c @@ -2,8 +2,6 @@ #include -#define IMPOSSIBLE() ERR("(%s:%i) This Should Never Be Called", __FILE__, __LINE__) - // EGL Is Replaced With GLFW EGLDisplay eglGetDisplay(__attribute__((unused)) NativeDisplayType native_display) { diff --git a/media-layer/stubs/src/X11.c b/media-layer/stubs/src/X11.c index 56ab67c..e478897 100644 --- a/media-layer/stubs/src/X11.c +++ b/media-layer/stubs/src/X11.c @@ -2,8 +2,6 @@ #include -#define IMPOSSIBLE() ERR("(%s:%i) This Should Never Be Called", __FILE__, __LINE__) - // Raw X11 Is Replaced With GLFW int XTranslateCoordinates(__attribute__((unused)) void *display, __attribute__((unused)) XID src_w, __attribute__((unused)) XID dest_w, __attribute__((unused)) int src_x, __attribute__((unused)) int src_y, __attribute__((unused)) int *dest_x_return, __attribute__((unused)) int *dest_y_return, __attribute__((unused)) XID *child_return) { diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index 7a040f0..d193e4c 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -8,7 +8,7 @@ add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) ## Mods add_library(compat SHARED src/compat/compat.c src/compat/egl.c src/compat/x11.c) -target_link_libraries(compat feature input chat sign media-layer-core dl) +target_link_libraries(compat feature input chat sign media-layer-core home dl) add_library(readdir SHARED src/readdir/readdir.c) @@ -27,7 +27,7 @@ else() endif() add_library(camera SHARED src/camera/camera.cpp) -target_link_libraries(camera reborn media-layer-core feature) +target_link_libraries(camera reborn media-layer-core feature home) add_library(game-mode SHARED src/game-mode/game-mode.c src/game-mode/game-mode.cpp) target_link_libraries(game-mode reborn feature) diff --git a/mods/src/camera/camera.cpp b/mods/src/camera/camera.cpp index a6585a6..f4adb91 100644 --- a/mods/src/camera/camera.cpp +++ b/mods/src/camera/camera.cpp @@ -2,12 +2,13 @@ #include #include -#include "../init/init.h" #include "../feature/feature.h" +#include "../home/home.h" +#include "../init/init.h" // Take Screenshot Using TripodCamera static void AppPlatform_linux_saveScreenshot_injection(__attribute__((unused)) unsigned char *app_platform, __attribute__((unused)) std::string const& path, __attribute__((unused)) int32_t width, __attribute__((unused)) int32_t height) { - media_take_screenshot(); + media_take_screenshot(home_get()); } // Enable TripodCameraRenderer diff --git a/mods/src/compat/compat.c b/mods/src/compat/compat.c index bcc555a..7d2a842 100644 --- a/mods/src/compat/compat.c +++ b/mods/src/compat/compat.c @@ -9,6 +9,7 @@ #include "../input/input.h" #include "../sign/sign.h" #include "../chat/chat.h" +#include "../home/home.h" #include "../init/init.h" #include "compat.h" @@ -53,7 +54,7 @@ HOOK(SDL_PollEvent, int, (SDL_Event *event)) { media_toggle_fullscreen(); handled = 1; } else if (event->key.keysym.sym == SDLK_F2) { - media_take_screenshot(); + media_take_screenshot(home_get()); handled = 1; } else if (event->key.keysym.sym == SDLK_F1) { input_hide_gui(); diff --git a/mods/src/home/home.c b/mods/src/home/home.c index 950bf92..7105295 100644 --- a/mods/src/home/home.c +++ b/mods/src/home/home.c @@ -13,15 +13,24 @@ #define NEW_PATH "" // Store Launch Directory -static char *launch_directory = NULL; +static char *get_launch_directory() { + static char *launch_directory = NULL; + if (launch_directory == NULL) { + launch_directory = getcwd(NULL, 0); + } + return launch_directory; +} __attribute__((constructor)) static void init_launch_directory() { - launch_directory = getcwd(NULL, 0); + get_launch_directory(); +} +__attribute__((destructor)) static void free_launch_directory() { + free(get_launch_directory()); } // Pretend $HOME Is Launch Directory HOOK(getenv, char *, (const char *name)) { if (strcmp(name, "HOME") == 0) { - return launch_directory; + return get_launch_directory(); } else { ensure_getenv(); return (*real_getenv)(name); @@ -34,7 +43,7 @@ char *home_get() { static char *dir = NULL; // Load if (dir == NULL) { - safe_asprintf(&dir, "%s/" NEW_PATH, getenv("HOME")); + safe_asprintf(&dir, "%s" NEW_PATH, getenv("HOME")); } // Return return dir;