diff --git a/CMakeLists.txt b/CMakeLists.txt index 2976a90..45f420f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,17 +135,28 @@ if(WIN32 OR MCRF_CROSS_WINDOWS) target_compile_definitions(mcrogueface PRIVATE Py_ENABLE_SHARED Py_PYCONFIG_H) endif() -# On Windows, set subsystem to WINDOWS to hide console +# On Windows, set subsystem to WINDOWS to hide console (release builds only) +# Use -DMCRF_WINDOWS_CONSOLE=ON for debug builds with console output +option(MCRF_WINDOWS_CONSOLE "Keep console window visible for debugging" OFF) + if(WIN32 AND NOT MCRF_CROSS_WINDOWS) # MSVC-specific flags - set_target_properties(mcrogueface PROPERTIES - WIN32_EXECUTABLE TRUE - LINK_FLAGS "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") + if(NOT MCRF_WINDOWS_CONSOLE) + set_target_properties(mcrogueface PROPERTIES + WIN32_EXECUTABLE TRUE + LINK_FLAGS "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") + endif() elseif(MCRF_CROSS_WINDOWS) - # MinGW cross-compilation: use -mwindows to hide console - set_target_properties(mcrogueface PROPERTIES - WIN32_EXECUTABLE TRUE - LINK_FLAGS "-mwindows") + # MinGW cross-compilation + if(NOT MCRF_WINDOWS_CONSOLE) + # Release: use -mwindows to hide console + set_target_properties(mcrogueface PROPERTIES + WIN32_EXECUTABLE TRUE + LINK_FLAGS "-mwindows") + else() + # Debug: keep console for stdout/stderr output + message(STATUS "Windows console enabled for debugging") + endif() endif() # Now the linker will find the libraries in the specified directory diff --git a/cmake/toolchains/mingw-w64-x86_64.cmake b/cmake/toolchains/mingw-w64-x86_64.cmake new file mode 100644 index 0000000..fb8ebf5 --- /dev/null +++ b/cmake/toolchains/mingw-w64-x86_64.cmake @@ -0,0 +1,34 @@ +# CMake toolchain file for cross-compiling to Windows using MinGW-w64 +# Usage: cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64-x86_64.cmake .. + +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +# Specify the cross-compiler (use posix variant for std::mutex support) +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc-posix) +set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++-posix) +set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) + +# Target environment location +set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) + +# Add MinGW system include directories for Windows headers +include_directories(SYSTEM /usr/x86_64-w64-mingw32/include) + +# Adjust search behavior +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Static linking of libgcc and libstdc++ to avoid runtime dependency issues +# Enable auto-import for Python DLL data symbols +set(CMAKE_EXE_LINKER_FLAGS_INIT "-static-libgcc -static-libstdc++ -Wl,--enable-auto-import") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "-static-libgcc -static-libstdc++ -Wl,--enable-auto-import") + +# Windows-specific defines +add_definitions(-DWIN32 -D_WIN32 -D_WINDOWS) +add_definitions(-DMINGW_HAS_SECURE_API) + +# Disable console window for GUI applications (optional, can be overridden) +# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -mwindows") diff --git a/deps/platform/windows/platform.h b/deps/platform/windows/platform.h index d7d0fc7..ac9b60e 100644 --- a/deps/platform/windows/platform.h +++ b/deps/platform/windows/platform.h @@ -1,6 +1,6 @@ #ifndef __PLATFORM #define __PLATFORM -#define __PLATFORM_SET_PYTHON_SEARCH_PATHS 0 +#define __PLATFORM_SET_PYTHON_SEARCH_PATHS 1 #include std::wstring executable_path() diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index ab86bab..de149be 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -65,11 +65,21 @@ GameEngine::GameEngine(const McRogueFaceConfig& cfg) !config.python_mode; if (should_load_game) { + std::cerr << "[DEBUG] GameEngine: loading default game.py" << std::endl; + std::cerr.flush(); if (!Py_IsInitialized()) { + std::cerr << "[DEBUG] GameEngine: initializing Python API" << std::endl; + std::cerr.flush(); McRFPy_API::api_init(); } + std::cerr << "[DEBUG] GameEngine: importing mcrfpy" << std::endl; + std::cerr.flush(); McRFPy_API::executePyString("import mcrfpy"); + std::cerr << "[DEBUG] GameEngine: executing scripts/game.py" << std::endl; + std::cerr.flush(); McRFPy_API::executeScript("scripts/game.py"); + std::cerr << "[DEBUG] GameEngine: game.py execution complete" << std::endl; + std::cerr.flush(); } // Note: --exec scripts are NOT executed here. diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index 0f46054..37a2654 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -28,6 +28,7 @@ #include "PyScene.h" #include "PythonObjectCache.h" #include +#include #include #include @@ -806,13 +807,26 @@ void McRFPy_API::executeScript(std::string filename) } } - FILE* PScriptFile = fopen(script_path.string().c_str(), "r"); - if(PScriptFile) { - PyRun_SimpleFile(PScriptFile, script_path.string().c_str()); - fclose(PScriptFile); - } else { + // Use std::ifstream + PyRun_SimpleString instead of PyRun_SimpleFile + // PyRun_SimpleFile has compatibility issues with MinGW-compiled code + std::ifstream file(script_path); + if (!file.is_open()) { std::cout << "Failed to open script: " << script_path.string() << std::endl; + return; } + + std::string script_content((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + file.close(); + + // Set __file__ before execution + PyObject* main_module = PyImport_AddModule("__main__"); + PyObject* main_dict = PyModule_GetDict(main_module); + PyObject* py_filename = PyUnicode_FromString(script_path.string().c_str()); + PyDict_SetItemString(main_dict, "__file__", py_filename); + Py_DECREF(py_filename); + + PyRun_SimpleString(script_content.c_str()); } void McRFPy_API::api_shutdown() diff --git a/src/main.cpp b/src/main.cpp index b0e9599..b9f95f2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include "PyTexture.h" #include #include +#include #include // Forward declarations @@ -18,7 +19,6 @@ int main(int argc, char* argv[]) McRogueFaceConfig config; CommandLineParser parser(argc, argv); - // Parse arguments auto parse_result = parser.parse(config); if (parse_result.should_exit) { return parse_result.exit_code; @@ -59,7 +59,7 @@ int run_python_interpreter(const McRogueFaceConfig& config) // Initialize Python with configuration (argv is constructed from config) McRFPy_API::init_python_with_config(config); - + // Import mcrfpy module and store reference McRFPy_API::mcrf_module = PyImport_ImportModule("mcrfpy"); if (!McRFPy_API::mcrf_module) { @@ -74,7 +74,7 @@ int run_python_interpreter(const McRogueFaceConfig& config) PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_font", McRFPy_API::default_font->pyObject()); PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_texture", McRFPy_API::default_texture->pyObject()); } - + // Handle different Python modes if (!config.python_command.empty()) { // Execute command from -c @@ -82,15 +82,15 @@ int run_python_interpreter(const McRogueFaceConfig& config) // Use PyRun_String to catch SystemExit PyObject* main_module = PyImport_AddModule("__main__"); PyObject* main_dict = PyModule_GetDict(main_module); - PyObject* result_obj = PyRun_String(config.python_command.c_str(), + PyObject* result_obj = PyRun_String(config.python_command.c_str(), Py_file_input, main_dict, main_dict); - + if (result_obj == NULL) { // Check if it's SystemExit if (PyErr_Occurred()) { PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); - + // If it's SystemExit and we're in interactive mode, clear it if (PyErr_GivenExceptionMatches(type, PyExc_SystemExit)) { PyErr_Clear(); @@ -99,7 +99,7 @@ int run_python_interpreter(const McRogueFaceConfig& config) PyErr_Restore(type, value, traceback); PyErr_Print(); } - + Py_XDECREF(type); Py_XDECREF(value); Py_XDECREF(traceback); @@ -128,16 +128,46 @@ int run_python_interpreter(const McRogueFaceConfig& config) } else if (!config.script_path.empty()) { // Execute script file (sys.argv already set at init time) - FILE* fp = fopen(config.script_path.string().c_str(), "r"); - if (!fp) { + // Note: Using PyRun_SimpleString instead of PyRun_SimpleFile for better + // cross-platform compatibility (PyRun_SimpleFile has issues with MinGW/Wine) + + // Read file contents + std::ifstream file(config.script_path); + if (!file.is_open()) { std::cerr << "mcrogueface: can't open file '" << config.script_path << "': "; std::cerr << "[Errno " << errno << "] " << strerror(errno) << std::endl; + delete engine; return 1; } + std::string script_content((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + file.close(); + + // Set __file__ before execution + PyObject* main_module = PyImport_AddModule("__main__"); + PyObject* main_dict = PyModule_GetDict(main_module); + PyObject* py_filename = PyUnicode_FromString(config.script_path.string().c_str()); + PyDict_SetItemString(main_dict, "__file__", py_filename); + Py_DECREF(py_filename); + + int result = PyRun_SimpleString(script_content.c_str()); + + // Flush Python stdout/stderr to ensure output appears before engine->run() blocks + PyObject* sys_module = PyImport_ImportModule("sys"); + if (sys_module) { + PyObject* stdout_obj = PyObject_GetAttrString(sys_module, "stdout"); + if (stdout_obj) { + PyObject_CallMethod(stdout_obj, "flush", NULL); + Py_DECREF(stdout_obj); + } + PyObject* stderr_obj = PyObject_GetAttrString(sys_module, "stderr"); + if (stderr_obj) { + PyObject_CallMethod(stderr_obj, "flush", NULL); + Py_DECREF(stderr_obj); + } + Py_DECREF(sys_module); + } - int result = PyRun_SimpleFile(fp, config.script_path.string().c_str()); - fclose(fp); - if (config.interactive_mode) { // Even if script had SystemExit, continue to interactive mode if (result != 0) { @@ -145,7 +175,7 @@ int run_python_interpreter(const McRogueFaceConfig& config) if (PyErr_Occurred()) { PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); - + if (PyErr_GivenExceptionMatches(type, PyExc_SystemExit)) { PyErr_Clear(); result = 0; // Don't exit with error @@ -153,7 +183,7 @@ int run_python_interpreter(const McRogueFaceConfig& config) PyErr_Restore(type, value, traceback); PyErr_Print(); } - + Py_XDECREF(type); Py_XDECREF(value); Py_XDECREF(traceback); @@ -162,7 +192,7 @@ int run_python_interpreter(const McRogueFaceConfig& config) // Run interactive mode after script PyRun_InteractiveLoop(stdin, ""); } - + // Run the game engine after script execution engine->run(); @@ -197,7 +227,7 @@ int run_python_interpreter(const McRogueFaceConfig& config) } return 0; } - + delete engine; return 0; }