From 17aadb565ef14c698b6cbb90b8f6942e88d00774 Mon Sep 17 00:00:00 2001 From: WrinklyNinja Date: Sun, 15 Jun 2014 15:41:55 +0100 Subject: [PATCH] Added command-line parameters. However, there's an issue with initialisation and the whole multi-process thing making that difficult. --- CMakeLists.txt | 3 +- docs/BUILD.MSVC.md | 2 +- docs/BUILD.MinGW.md | 2 +- src/gui/app.cpp | 114 ++++++++++++++++++++++++++++++++++++++- src/gui/app.h | 12 ++++- src/gui/main_win.cpp | 123 ++++++++++++++++++++++++++++++------------- 6 files changed, 212 insertions(+), 44 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 45f6ee2d..40eb45dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,7 @@ set (LOOT_GUI_SRC ${LOOT_SRC} set (LOOT_API_SRC ${LOOT_SRC} "${CMAKE_SOURCE_DIR}/src/api/api.cpp") -find_package(Boost REQUIRED COMPONENTS log log_setup locale thread chrono date_time filesystem system regex iostreams) +find_package(Boost REQUIRED COMPONENTS log log_setup locale thread chrono date_time filesystem program_options system regex iostreams) # Include source and library directories. include_directories ("${CMAKE_SOURCE_DIR}/src" @@ -113,6 +113,7 @@ IF (MINGW) boost_chrono boost_date_time boost_filesystem + boost_program_options boost_system boost_regex version diff --git a/docs/BUILD.MSVC.md b/docs/BUILD.MSVC.md index 9108ec1c..a6a71162 100644 --- a/docs/BUILD.MSVC.md +++ b/docs/BUILD.MSVC.md @@ -6,7 +6,7 @@ These instructions were used to build LOOT using Microsoft Visual Studio 2012 an ``` bootstrap.bat -b2 toolset=msvc threadapi=win32 link=static runtime-link=static variant=release address-model=32 --with-log --with-date_time --with-thread --with-filesystem --with-locale --with-regex --with-system --with-iostreams +b2 toolset=msvc threadapi=win32 link=static runtime-link=static variant=release address-model=32 --with-log --with-date_time --with-thread --with-filesystem --with-program_options --with-locale --with-regex --with-system --with-iostreams ``` `link`, `runtime-link` and `address-model` can all be modified if shared linking or 64 bit builds are desired. LOOT uses statically-linked Boost libraries by default: to change this, edit [CMakeLists.txt](../CMakeLists.txt). diff --git a/docs/BUILD.MinGW.md b/docs/BUILD.MinGW.md index d3c36aba..bffb5073 100644 --- a/docs/BUILD.MinGW.md +++ b/docs/BUILD.MinGW.md @@ -7,7 +7,7 @@ These instructions were used to build LOOT using mingw-w64 on Ubuntu and Debian ``` ./bootstrap.sh echo "using gcc : 4.6.3 : i686-w64-mingw32-g++ : i686-w64-mingw32-windres i686-w64-mingw32-ar i686-w64-mingw32-ranlib ;" > tools/build/v2/user-config.jam -./b2 toolset=gcc-4.6.3 target-os=windows threadapi=win32 link=static runtime-link=static variant=release address-model=32 cxxflags=-fPIC --with-log --with-date_time --with-thread --with-filesystem --with-locale --with-regex --with-system --with-iostreams --stagedir=stage-32 +./b2 toolset=gcc-4.6.3 target-os=windows threadapi=win32 link=static runtime-link=static variant=release address-model=32 cxxflags=-fPIC --with-log --with-date_time --with-thread --with-filesystem --with-program_options --with-locale --with-regex --with-system --with-iostreams ``` #### Chromium Embedded Framework diff --git a/src/gui/app.cpp b/src/gui/app.cpp index 8736792a..d9e2cd96 100644 --- a/src/gui/app.cpp +++ b/src/gui/app.cpp @@ -27,13 +27,104 @@ #include #include +#include + #include +#include +#include using namespace std; +using boost::locale::translate; +using boost::format; namespace loot { - LootApp::LootApp(YAML::Node& settings) : _settings(settings) {} + LootApp::LootApp() {} + + void LootApp::Init(YAML::Node& settings, std::string& cmdLineGame) { + _settings = settings; + + string initError; + + // Detect Games + //------------- + + int gameIndex = -1; + + //Detect installed games. + BOOST_LOG_TRIVIAL(debug) << "Detecting installed games."; + try { + _games = GetGames(settings); + } + catch (YAML::Exception& e) { + BOOST_LOG_TRIVIAL(error) << "Games' settings parsing failed. " << e.what(); + initError = (format(translate("Error: Games' settings parsing failed. %1%")) % e.what()).str(); + return; + } + catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "Game-specific settings could not be initialised. " << e.what(); + initError = (format(translate("Error: Game-specific settings could not be initialised. %1%")) % e.what()).str(); + return; + } + + BOOST_LOG_TRIVIAL(debug) << "Selecting game."; + + if (cmdLineGame.empty()) { + if (_settings["Game"] && _settings["Game"].as() != "auto") + cmdLineGame = _settings["Game"].as(); + else if (_settings["Last Game"] && _settings["Last Game"].as() != "auto") + cmdLineGame = _settings["Last Game"].as(); + } + + if (!cmdLineGame.empty()) { + for (size_t i = 0, max = _games.size(); i < max; ++i) { + if (cmdLineGame == _games[i].FolderName() && _games[i].IsInstalled()) + gameIndex = i; + } + } + if (gameIndex < 0) { + //Set gameIndex to the first installed game. + for (size_t i = 0, max = _games.size(); i < max; ++i) { + if (_games[i].IsInstalled()) { + gameIndex = i; + break; + } + } + if (gameIndex < 0) { + BOOST_LOG_TRIVIAL(error) << "None of the supported games were detected."; + initError = translate("Error: None of the supported games were detected.").str(); + return; + } + } + loot::Game& game(_games[gameIndex]); + BOOST_LOG_TRIVIAL(debug) << "Game selected is " << game.Name(); + + //Now that game is selected, initialise it. + BOOST_LOG_TRIVIAL(debug) << "Initialising game-specific settings."; + try { + game.Init(); + *find(_games.begin(), _games.end(), game) = game; //Sync changes. + } + catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "Game-specific settings could not be initialised. " << e.what(); + initError = (format(translate("Error: Game-specific settings could not be initialised. %1%")) % e.what()).str(); + return; + } + + // Create the message object. + CefRefPtr msg = CefProcessMessage::Create("initJSVars"); + + // Retrieve the argument list object. + CefRefPtr args = msg>GetArgumentList(); + + // Populate the argument values. + args->SetString(0, “my string”); + args->SetInt(0, 10); + + // Send the process message to the render process. + // Use PID_BROWSER instead when sending a message to the browser process. + browser->SendProcessMessage(PID_RENDERER, msg); + } CefRefPtr LootApp::GetBrowserProcessHandler() { return this; @@ -90,8 +181,24 @@ namespace loot { // Register javascript functions. message_router_->OnContextCreated(browser, frame, context); + _context = context; + } + + void LootApp::OnContextReleased(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) { + + _context = NULL; + } + + void LootApp::InitJSVars() { + BOOST_LOG_TRIVIAL(debug) << "Populating JS object variable initial values."; + + if (_context == NULL) + return; + // Retrieve the context's window object. - CefRefPtr object = context->GetGlobal(); + CefRefPtr object = _context->GetGlobal(); // Here the initialisation values should be set. @@ -167,6 +274,9 @@ namespace loot { lootObj->SetValue("languages", langsArr, V8_PROPERTY_ATTRIBUTE_NONE); + // Load Order + //----------- + object->SetValue("loot", lootObj, V8_PROPERTY_ATTRIBUTE_NONE); } } \ No newline at end of file diff --git a/src/gui/app.h b/src/gui/app.h index 51aba29f..40978bcf 100644 --- a/src/gui/app.h +++ b/src/gui/app.h @@ -38,7 +38,9 @@ namespace loot { public CefBrowserProcessHandler, public CefRenderProcessHandler { public: - LootApp(YAML::Node& settings); + LootApp(); + + void Init(YAML::Node& settings, std::string& cmdLineGame); // Override CefApp methods. virtual CefRefPtr GetBrowserProcessHandler() OVERRIDE; @@ -59,6 +61,14 @@ namespace loot { CefRefPtr frame, CefRefPtr context) OVERRIDE; + virtual void OnContextReleased(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr context) OVERRIDE; + + void InitJSVars(); + + CefRefPtr _context; + YAML::Node _settings; std::vector _games; diff --git a/src/gui/main_win.cpp b/src/gui/main_win.cpp index 32adab05..fb480fb1 100644 --- a/src/gui/main_win.cpp +++ b/src/gui/main_win.cpp @@ -40,8 +40,10 @@ #include #include #include +#include namespace fs = boost::filesystem; +namespace po = boost::program_options; using namespace std; using namespace loot; @@ -50,11 +52,91 @@ using boost::format; int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { - // Do application init - //-------------------- + // Do all the standard CEF setup stuff. + //------------------------------------- + + // Set up CEF sandbox. + CefScopedSandboxInfo scoped_sandbox; + void * sandbox_info = scoped_sandbox.sandbox_info(); + + // Read command line arguments. + CefMainArgs main_args(hInstance); + + // Create the process reference. + CefRefPtr app(new loot::LootApp); + + // Run the process. + int exit_code = CefExecuteProcess(main_args, app.get(), sandbox_info); + if (exit_code >= 0) { + // The sub-process has completed so return here. + return exit_code; + } string initError; + string gameStr; YAML::Node settings; + unsigned int verbosity = 0; + + // Check if LOOT is already running + //--------------------------------- + + HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, L"LOOT.Shell.Instance"); + if (hMutex != NULL) { + // An instance of LOOT is already running, so quit. + return 0; + } + else { + //Create the mutex so that future instances will not run. + hMutex = ::CreateMutex(NULL, FALSE, L"LOOT.Shell.Instance"); + } + + // Handle command line args (not CEF args) + //---------------------------------------- + + // declare the supported options + po::options_description opts("Options"); + opts.add_options() + ("help,h", translate("produces this help message").str().c_str()) + ("version,V", translate("prints the version banner").str().c_str()) + ("game,g", po::value(&gameStr), + translate("Override game autodetection. Valid values are the folder " + "names defined in the settings file.").str().c_str()); + + // parse command line arguments + po::variables_map vm; + try{ + vector args = po::split_winmain(lpCmdLine); + po::store(po::wcommand_line_parser(args).options(opts).run(), vm); + po::notify(vm); + } + catch (po::multiple_occurrences &){ + BOOST_LOG_TRIVIAL(error) << "Cannot specify options multiple times; please use the '--help' option to see usage instructions"; + std::cout << "Cannot specify options multiple times; please use the '--help' option to see usage instructions"; + return 1; + } + catch (exception & e){ + BOOST_LOG_TRIVIAL(error) << e.what() << "; please use the '--help' option to see usage instructions"; + std::cout << e.what() << "; please use the '--help' option to see usage instructions"; + return 1; + } + + if (vm.count("help")) { + BOOST_LOG_TRIVIAL(info) << "Displaying command line help..."; + std::cout << opts << std::endl; + if (hMutex != NULL) + ReleaseMutex(hMutex); + return 0; + } + if (vm.count("version")) { + BOOST_LOG_TRIVIAL(info) << "Displaying command line version..."; + std::cout << "LOOT v" << g_version_major << "." << g_version_minor << "." << g_version_patch << std::endl; + if (hMutex != NULL) + ReleaseMutex(hMutex); + return 0; + } + + // Do application init + //-------------------- //Load settings. if (!fs::exists(g_path_settings)) { @@ -64,7 +146,6 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd } catch (fs::filesystem_error& /*e*/) { initError = "Error: Could not create local app data LOOT folder."; - return 1; } GenerateDefaultSettingsFile(g_path_settings.string()); } @@ -75,7 +156,6 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd } catch (YAML::ParserException& e) { initError = (format(translate("Error: Settings parsing failed. %1%")) % e.what()).str(); - return 1; } //Set up logging. @@ -90,7 +170,6 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd ) ); boost::log::add_common_attributes(); - unsigned int verbosity = 0; if (settings["Debug Verbosity"]) { verbosity = settings["Debug Verbosity"].as(); } @@ -128,39 +207,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd cout.imbue(locale()); boost::filesystem::path::imbue(locale()); - // Now do all the standard CEF setup stuff. - //---------------------------- - - // Set up CEF sandbox. - CefScopedSandboxInfo scoped_sandbox; - void * sandbox_info = scoped_sandbox.sandbox_info(); - - // Read command line arguments. - CefMainArgs main_args(hInstance); - - // Create the process reference. - CefRefPtr app(new loot::LootApp(settings)); - - // Run the process. - int exit_code = CefExecuteProcess(main_args, app.get(), sandbox_info); - if (exit_code >= 0) { - // The sub-process has completed so return here. - return exit_code; - } - - - // Check if LOOT is already running - //--------------------------------- - - HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, L"LOOT.Shell.Instance"); - if (hMutex != NULL) { - // An instance of LOOT is already running, so quit. - return 0; - } - else { - //Create the mutex so that future instances will not run. - hMutex = ::CreateMutex(NULL, FALSE, L"LOOT.Shell.Instance"); - } + app.get()->Init(settings, gameStr); // Back to CEF //------------