Fix building LOOT as a native Linux application.

Not all Windows-specific code has equivalent Linux code, file path to
URL conversion is hacky, and only that LOOT launches correctly with no
game detected has been tested.
This commit is contained in:
Oliver Hamlet
2015-10-29 18:20:14 +00:00
parent f837e3a235
commit d874fbb52c
14 changed files with 377 additions and 148 deletions
+37 -18
View File
@@ -143,7 +143,7 @@ set (LOOT_HEADERS "${CMAKE_SOURCE_DIR}/src/backend/metadata/condition_grammar.h"
"${CMAKE_SOURCE_DIR}/src/backend/error.h")
set (LOOT_GUI_SRC ${LOOT_SRC}
"${CMAKE_SOURCE_DIR}/src/gui/main_win.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/main.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/handler.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/loot_handler.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/loot_app.cpp"
@@ -213,7 +213,7 @@ include_directories ("${CMAKE_SOURCE_DIR}/src"
${CEF_ROOT}
"${LIBESPM_ROOT}/include"
${Boost_INCLUDE_DIRS}
${YAML_CPP_INCLUDE_DIR}
${YAML_CPP_INCLUDE_DIR}
${GTEST_INCLUDE_DIRS}
"${PSEUDOSEM_ROOT}/include")
@@ -255,6 +255,17 @@ IF (CMAKE_COMPILER_IS_GNUCXX)
ssl
curl
loadorder${PROJECT_ARCH})
set (LOOT_GUI_LIBS ${LOOT_LIBS}
cef
cef_dll_wrapper
z
crypto
X11)
IF (NOT MINGW)
link_directories("${CEF_ROOT}/Release")
add_definitions(-DNDEBUG)
ENDIF ()
ENDIF ()
# MinGW settings.
@@ -267,10 +278,8 @@ IF (MINGW)
version
ws2_32
shlwapi)
set (LOOT_GUI_LIBS ${LOOT_LIBS}
set (LOOT_GUI_LIBS ${LOOT_GUI_LIBS}
cef_sandbox
libcef
libcef_dll_wrapper
rpcrt4
comctl32)
ELSEIF (MSVC)
@@ -355,20 +364,30 @@ ENDIF ()
# Post-Build Steps
##############################
add_custom_command(
TARGET LOOT
POST_BUILD
COMMAND "mt.exe" -manifest \"${CMAKE_SOURCE_DIR}/src/gui/LOOT.exe.manifest\" -inputresource:"$(TargetDir)$(TargetFileName)"\;\#1 -outputresource:"$(TargetDir)$(TargetFileName)"\;\#1
COMMENT "Adding manifest..."
)
IF (CMAKE_SYSTEM_NAME MATCHES "Windows")
add_custom_command(
TARGET LOOT
POST_BUILD
COMMAND "mt.exe" -manifest \"${CMAKE_SOURCE_DIR}/src/gui/LOOT.exe.manifest\" -inputresource:"$(TargetDir)$(TargetFileName)"\;\#1 -outputresource:"$(TargetDir)$(TargetFileName)"\;\#1
COMMENT "Adding manifest..."
)
# Copy CEF binaries.
FOREACH(cef_dll libcef.dll natives_blob.bin snapshot_blob.bin d3dcompiler_47.dll libEGL.dll libGLESv2.dll wow_helper.exe)
add_custom_command(TARGET LOOT POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/${CEF_ROOT}/$<CONFIGURATION>/${cef_dll}"
$<TARGET_FILE_DIR:LOOT>/${cef_dll})
ENDFOREACH()
# Copy CEF binaries.
FOREACH(cef_dll libcef.dll natives_blob.bin snapshot_blob.bin d3dcompiler_47.dll libEGL.dll libGLESv2.dll wow_helper.exe)
add_custom_command(TARGET LOOT POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/${CEF_ROOT}/$<CONFIGURATION>/${cef_dll}"
$<TARGET_FILE_DIR:LOOT>/${cef_dll})
ENDFOREACH()
ELSE ()
# Copy CEF binaries.
FOREACH(cef_dll libcef.so natives_blob.bin snapshot_blob.bin chrome-sandbox)
add_custom_command(TARGET LOOT POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/${CEF_ROOT}/Release/${cef_dll}"
$<TARGET_FILE_DIR:LOOT>/${cef_dll})
ENDFOREACH()
ENDIF ()
# Copy CEF resources.
FOREACH(cef_resource icudtl.dat cef.pak cef_100_percent.pak cef_200_percent.pak devtools_resources.pak)
+16 -3
View File
@@ -46,13 +46,26 @@ In addition, LOOT's UI relies on the web libraries below, which can be fetched b
### Building
Aside from Libespm and Pseudosem, the C/C++ libraries must be built separately. Instructions for building them and LOOT itself using Microsoft Visual Studio are given in [docs/BUILD.MSVC.md](docs/BUILD.MSVC.md).
Instructions for building Windows binaries using Microsoft Visual Studio are given in [docs/BUILD.MSVC.md](docs/BUILD.MSVC.md), and instructions for building Linux binaries using GCC are given in [docs/BUILD.LINUX.md](docs/BUILD.LINUX.md).
The HTML UI is automatically built when building the LOOT executable, but it can also be built by running `node scripts/vulcanize.js` from the repository root.
### Cross-Platform Support
#### CMake Variables
Although LOOT uses a cross-platform build system and cross-platform libraries, it does rely on some Windows API functionality. Anyone wishing to port LOOT to other platforms will need to ensure equivalent functionality is implemented for their target platform. The Windows API code is wrapped in `#ifdef _WIN32` blocks so that it can be easily identified.
LOOT uses the following CMake variables to set build parameters:
Parameter | Values | Default |Description
----------|--------|---------|-----------
`BUILD_SHARED_LIBS` | `ON`, `OFF` | `OFF` | Whether or not to build a shared libloot.
`PROJECT_STATIC_RUNTIME` | `ON`, `OFF` | `ON` | Whether to link the C++ runtime statically or not. This also affects the whether static or shared Boost libraries are used.
`PROJECT_ARCH` | `32`, `64` | `32` | Whether to build 32 or 64 bit LOOT binaries.
`CEF_ROOT` | path | `../../cef` | Path to the root of the Chromium Embedded Framework folder.
`LIBESPM_ROOT` | path | `../../libespm` | Path to the root of the libespm repository folder.
`LIBGIT2_ROOT` | path | `../../libgit2` | Path to the root of the libgit2 repository folder.
`LIBLOADORDER_ROOT` | path | `../../libloadorder` | Path to the root of the libloadorder repository folder.
`PSEUDOSEM_ROOT` | Path | `../../pseudosem` | Path to the root of the Pseudosem repository folder.
The default paths given in the table above are relative to LOOT's `CMakeLists.txt`.
## Packaging Releases
+50
View File
@@ -0,0 +1,50 @@
# Build Instructions using GCC
Linux binaries can be built for LOOT, and these instructions are for doing so on
Ubuntu Desktop 14.04, though they may also apply to other versions and
distributions.
Most of the procedure for building the API, tests and metadata validator can be
found in the `.travis.yml` file, which is the configuration file for LOOT's
Travis CI instance. However, Travis instances have a few more libraries and
utilities by default than Ubuntu Desktop 14.04 does, and the procedure does not
cover building the GUI application, so these differences will be covered here.
Linux builds of the GUI application should be considered officially
**unsupported and unmaintained**, though contributions are welcome.
## Installing Missing Dependencies
To install Node.js (for building the HTML UI) and the libcurl and X11
development libraries (for libgit2 and the UI respectively), run the following:
```
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 68576280
sudo apt-add-repository 'deb https://deb.nodesource.com/node_4.x precise main'
sudo apt-get update
sudo apt-get install nodejs npm libcurl4-openssl-dev libx11-dev
sudo ln -s /usr/bin/nodejs /usr/bin/node
sudo npm install -g bower vulcanize@0.7.11
```
If you wish to build the GUI application, you must also manually download the
Chromium Embedded Framework binary archive, then extract it and run
```
mkdir build && cd build
cmake ..
make
```
from the extracted folder.
## Runtime Differences
Not all LOOT's features have been implemented for Linux builds. Issues labelled
`linux` on LOOT's issue tracker cover such missing features where they can be
implemented. Unavoidable platform differences are documented here:
* On Windows, LOOT can detect game installs using their Registry entries. On
Linux this is obviously not possible, so either game paths will have to be
entered manually in LOOT's settings dialog when it is run, or LOOT will need
to be installed beside a game's Data folder for that game to be detected.
-15
View File
@@ -69,21 +69,6 @@ Example CMake keys: `-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=build -DCMAKE_ARCHIVE_OUTP
LOOT uses a third-party CMake module to generate its build revision data, so first download the `GetGitRevisionDescription.cmake` and `GetGitRevisionDescription.cmake.in` files found in [this repository](https://github.com/rpavlik/cmake-modules) to the `build` subdirectory of the LOOT folder.
LOOT uses the following CMake variables to set build parameters:
Parameter | Values | Default |Description
----------|--------|---------|-----------
`BUILD_SHARED_LIBS` | `ON`, `OFF` | `OFF` | Whether or not to build a shared libloot.
`PROJECT_STATIC_RUNTIME` | `ON`, `OFF` | `ON` | Whether to link the C++ runtime statically or not. This also affects the Boost libraries used.
`PROJECT_ARCH` | `32`, `64` | `32` | Whether to build 32 or 64 bit LOOT binaries.
`CEF_ROOT` | path | `../../cef` | Path to the root of the Chromium Embedded Framework folder.
`LIBESPM_ROOT` | path | `../../libespm` | Path to the root of the libespm repository folder.
`LIBGIT2_ROOT` | path | `../../libgit2` | Path to the root of the libgit2 repository folder.
`LIBLOADORDER_ROOT` | path | `../../libloadorder` | Path to the root of the libloadorder repository folder.
`PSEUDOSEM_ROOT` | Path | `../../pseudosem` | Path to the root of the Pseudosem repository folder.
The default paths given in the table above are relative to LOOT's `CMakeLists.txt`.
1. Set CMake up so that it builds the binaries in the `build` subdirectory of the LOOT folder.
2. Define any necessary parameters.
3. Configure CMake, then generate a build system for Visual Studio 12.
+59 -50
View File
@@ -6,12 +6,9 @@
var child_process = require('child_process');
var path = require('path');
var fs = require('fs-extra');
var os = require('os');
var helpers = require('./helpers');
function isApi64Bit(root_path) {
return helpers.fileExists(path.join(root_path, 'build', 'Release', 'loot64.dll'));
}
function vulcanize() {
return child_process.execFileSync('node', [
'scripts/vulcanize.js',
@@ -28,7 +25,12 @@ function getGitDescription() {
}
function compress(source_path, dest_path) {
var sevenzip_path = path.join('C:\\', 'Program Files', '7-Zip', '7z.exe');
var sevenzip_path = '';
if (os.platform() == 'win32') {
sevenzip_path = path.join('C:\\', 'Program Files', '7-Zip', '7z.exe');
} else {
sevenzip_path = '/usr/bin/7z';
}
// First remove any existing archive.
fs.removeSync(dest_path);
@@ -43,32 +45,43 @@ function compress(source_path, dest_path) {
]);
}
function createAppArchive(dest_path) {
function createAppArchive(release_path, dest_path) {
// Ensure that the output directory is empty.
fs.emptyDirSync(temp_path);
if (helpers.isMultiArch(root_path)) {
var release_path = path.join(root_path, 'build', '32', 'Release');
} else {
var release_path = path.join(root_path, 'build', 'Release');
}
// Copy LOOT exectuable and CEF files.
[
'LOOT.exe',
'd3dcompiler_47.dll',
'libEGL.dll',
'libGLESv2.dll',
'libcef.dll',
'wow_helper.exe',
'natives_blob.bin',
'snapshot_blob.bin',
'cef.pak',
'cef_100_percent.pak',
'cef_200_percent.pak',
'devtools_resources.pak',
'icudtl.dat'
].forEach(function(file){
var binaries = [];
if (os.platform() == 'win32') {
binaries = [
'LOOT.exe',
'd3dcompiler_47.dll',
'libEGL.dll',
'libGLESv2.dll',
'libcef.dll',
'wow_helper.exe',
'natives_blob.bin',
'snapshot_blob.bin',
'cef.pak',
'cef_100_percent.pak',
'cef_200_percent.pak',
'devtools_resources.pak',
'icudtl.dat'
];
} else {
binaries = [
'LOOT',
'chrome-sandbox',
'libcef.so',
'natives_blob.bin',
'snapshot_blob.bin',
'cef.pak',
'cef_100_percent.pak',
'cef_200_percent.pak',
'devtools_resources.pak',
'icudtl.dat'
];
}
binaries.forEach(function(file){
fs.copySync(
path.join(release_path, file),
path.join(temp_path, file)
@@ -130,31 +143,15 @@ function createAppArchive(dest_path) {
fs.removeSync(temp_path);
}
function createApiArchive(dest_path) {
function createApiArchive(binary_path, dest_path) {
// Ensure that the output directory is empty.
fs.emptyDirSync(temp_path);
// API binary/binaries.
if (helpers.isMultiArch(root_path)) {
fs.copySync(
path.join(root_path, 'build', '32', 'Release', 'loot32.dll'),
path.join(temp_path, 'loot32.dll')
);
fs.copySync(
path.join(root_path, 'build', '64', 'Release', 'loot64.dll'),
path.join(temp_path, 'loot64.dll')
);
} else if (isApi64Bit(root_path)) {
fs.copySync(
path.join(root_path, 'build', 'Release', 'loot64.dll'),
path.join(temp_path, 'loot64.dll')
);
} else {
fs.copySync(
path.join(root_path, 'build', 'Release', 'loot32.dll'),
path.join(temp_path, 'loot32.dll')
);
}
fs.copySync(
binary_path,
path.join(temp_path, path.basename(binary_path))
);
// API header file.
fs.mkdirsSync(path.join(temp_path, 'include', 'loot'));
@@ -191,5 +188,17 @@ var temp_path = path.join(root_path, 'build', 'archive.tmp');
var git_desc = getGitDescription();
vulcanize();
createAppArchive(path.join(root_path, 'build', 'LOOT ' + git_desc + '.7z'));
createApiArchive(path.join(root_path, 'build', 'LOOT API ' + git_desc + '.7z'));
var release_paths = helpers.getAppReleasePaths(root_path);
for (var i = 0; i < release_paths.length; ++i) {
if (release_paths[i].label) {
createAppArchive(release_paths[i].path, path.join(root_path, 'build', 'LOOT ' + git_desc + ' (' + release_paths[i].label + ').7z'));
} else {
createAppArchive(release_paths[i].path, path.join(root_path, 'build', 'LOOT ' + git_desc + '.7z'));
}
}
var binary_paths = helpers.getApiBinaryPaths(root_path);
for (var i = 0; i < binary_paths.length; ++i) {
createApiArchive(binary_paths[i].path, path.join(root_path, 'build', 'LOOT API ' + git_desc + ' (' + binary_paths[i].label + ').7z'));
}
+94 -3
View File
@@ -1,6 +1,7 @@
// Helper functions shared across scripts.
var path = require('path');
var fs = require('fs');
var os = require('os');
function fileExists(file_path) {
try {
@@ -16,9 +17,99 @@ function fileExists(file_path) {
return false;
}
function isMultiArch(root_path) {
return fileExists(path.join(root_path, 'build', '64', 'Release', 'loot64.dll'));
function getAppReleasePaths(root_path) {
var paths = [];
var file = 'LOOT';
var paths_to_try = [
{
path: path.join(root_path, 'build'),
label: null
},
{
path: path.join(root_path, 'build', '32'),
label: '32 bit'
},
{
path: path.join(root_path, 'build', '64'),
label: '64 bit'
}
];
if (os.platform() == 'win32') {
file += '.exe';
}
for (var i = 0; i < paths_to_try.length; ++i) {
if (os.platform() == 'win32') {
paths_to_try[i].path = path.join(paths_to_try[i].path, 'Release');
}
if (fileExists(path.join(paths_to_try[i].path, file))) {
paths.push(paths_to_try[i]);
}
}
return paths;
}
function getApiBinaryPaths(root_path) {
var paths = [];
var files = [];
var paths_to_try = [
{
path: path.join(root_path, 'build'),
label: null
},
{
path: path.join(root_path, 'build', '32'),
label: '32 bit'
},
{
path: path.join(root_path, 'build', '64'),
label: '64 bit'
}
];
for (var i = 0; i < paths_to_try.length; ++i) {
if (os.platform() == 'win32') {
paths_to_try[i].path = path.join(paths_to_try[i].path, 'Release');
files = [
{
name: 'loot32.dll',
label: '32 bit'
},
{
name: 'loot64.dll',
label: '64 bit'
}
];
} else {
files = [
{
name: 'libloot32.so',
label: '32 bit'
},
{
name: 'libloot64.so',
label: '64 bit'
}
];
}
for (var j = 0; j < files.length; ++j) {
if (fileExists(path.join(paths_to_try[i].path, files[j].name))) {
paths_to_try[i].path = path.join(paths_to_try[i].path, files[j].name);
paths_to_try[i].label = files[j].label;
paths.push(paths_to_try[i]);
break;
}
}
}
return paths;
}
module.exports.fileExists = fileExists;
module.exports.isMultiArch = isMultiArch;
module.exports.getAppReleasePaths = getAppReleasePaths;
module.exports.getApiBinaryPaths = getApiBinaryPaths;
+27 -21
View File
@@ -4,6 +4,7 @@
var child_process = require('child_process');
var path = require('path');
var fs = require('fs');
var os = require('os');
var helpers = require('./helpers');
if (process.argv.length < 3) {
@@ -12,27 +13,32 @@ if (process.argv.length < 3) {
var root_path = process.argv[2];
}
if (helpers.isMultiArch(root_path)) {
var output_path = path.join(root_path, 'build', '32', 'Release', 'resources', 'ui');
} else {
var output_path = path.join(root_path, 'build', 'Release', 'resources', 'ui');
}
var release_paths = helpers.getAppReleasePaths(root_path);
// Makes sure output directory exists first.
try {
fs.mkdirSync(output_path);
} catch (e) {
if (e.code != 'EEXIST') {
console.log(e);
for (var i = 0; i < release_paths.length; ++i) {
var output_path = path.join(release_paths[i].path, 'resources', 'ui');
// Makes sure output directory exists first.
try {
fs.mkdirSync(output_path);
} catch (e) {
if (e.code != 'EEXIST') {
console.log(e);
}
}
}
child_process.execFileSync('vulcanize.cmd', [
'--inline',
'--strip',
'--config',
path.join(root_path, 'scripts', 'vulcanize.config.json'),
'-o',
path.join(output_path, 'index.html'),
path.join(root_path, 'src', 'gui', 'html', 'index.html')
]);
var vulcanize = 'vulcanize';
if (os.platform() == 'win32') {
vulcanize += '.cmd';
}
child_process.execFileSync('vulcanize.cmd', [
'--inline',
'--strip',
'--config',
path.join(root_path, 'scripts', 'vulcanize.config.json'),
'-o',
path.join(output_path, 'index.html'),
path.join(root_path, 'src', 'gui', 'html', 'index.html')
]);
}
+31 -13
View File
@@ -99,6 +99,37 @@ namespace loot {
return out;
}
//Turns an absolute filesystem path into a valid file:// URL.
std::string ToFileURL(const fs::path& file) {
BOOST_LOG_TRIVIAL(trace) << "Converting file path " << file << " to a URL.";
string url;
#ifdef _WIN32
wstring wstr(MAX_PATH, 0);
DWORD len = MAX_PATH;
UrlCreateFromPath(ToWinWide(file.string()).c_str(), &wstr[0], &len, NULL);
url = FromWinWide(wstr.c_str()); // Passing c_str() cuts off any unused buffer.
BOOST_LOG_TRIVIAL(trace) << "Converted to: " << url;
#else
// Let's be naive about this.
url = "file://" + file.string();
#endif
return url;
}
//Opens the file in its registered default application.
void OpenInDefaultApplication(const boost::filesystem::path& file) {
#ifdef _WIN32
HINSTANCE ret = ShellExecute(0, NULL, ToWinWide(file.string()).c_str(), NULL, NULL, SW_SHOWNORMAL);
if ((int)ret <= 32)
throw error(error::windows_error, lc::translate("Failed to open file in its default application."));
#else
if (system(("/usr/bin/xdg-open" + file.string()).c_str()) != 0)
throw error(error::windows_error, lc::translate("Failed to open file in its default application."));
#endif
}
#ifdef _WIN32
//Get registry subkey value string.
string RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value) {
@@ -150,19 +181,6 @@ namespace loot {
return fs::path("");
}
//Turns an absolute filesystem path into a valid file:// URL.
std::string ToFileURL(const fs::path& file) {
BOOST_LOG_TRIVIAL(trace) << "Converting file path " << file << " to a URL.";
wstring wstr(MAX_PATH, 0);
DWORD len = MAX_PATH;
UrlCreateFromPath(ToWinWide(file.string()).c_str(), &wstr[0], &len, NULL);
string str = FromWinWide(wstr.c_str()); // Passing c_str() cuts off any unused buffer.
BOOST_LOG_TRIVIAL(trace) << "Converted to: " << str;
return str;
}
//Helper to turn UTF8 strings into strings that can be used by WinAPI.
std::wstring ToWinWide(const std::string& str) {
size_t len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), 0, 0);
+6 -3
View File
@@ -36,6 +36,12 @@ namespace loot {
//Converts an unsigned 32-bit integer to a hex string using BOOST's Spirit.Karma. Faster than a stringstream conversion.
std::string IntToHexString(const uint32_t n);
//Turns an absolute filesystem path into a valid file:// URL.
std::string ToFileURL(const boost::filesystem::path& file);
//Opens the file in its registered default application.
void OpenInDefaultApplication(const boost::filesystem::path& file);
#ifdef _WIN32
//Get registry subkey value string.
std::string RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value);
@@ -43,9 +49,6 @@ namespace loot {
//Get the local application data path, within which LOOT's data folder should be stored.
boost::filesystem::path GetLocalAppDataPath();
//Turns an absolute filesystem path into a valid file:// URL.
std::string ToFileURL(const boost::filesystem::path& file);
//Helper to turn UTF8 strings into strings that can be used by WinAPI.
std::wstring ToWinWide(const std::string& str);
+6 -7
View File
@@ -180,7 +180,10 @@ namespace loot {
// May be a request with arguments.
YAML::Node req;
try {
req = JSON::parse(request.ToString());
// Can't pass this as a reference directly as GCC
// complains about it.
std::string requestString = request.ToString();
req = JSON::parse(requestString);
}
catch (exception &e) {
BOOST_LOG_TRIVIAL(error) << "Failed to parse CEF query request \"" << request.ToString() << "\": " << e.what();
@@ -568,17 +571,13 @@ namespace loot {
void Handler::OpenReadme() {
BOOST_LOG_TRIVIAL(info) << "Opening LOOT readme.";
// Open readme in default application.
HINSTANCE ret = ShellExecute(0, NULL, ToWinWide(ToFileURL(g_path_readme)).c_str(), NULL, NULL, SW_SHOWNORMAL);
if ((int)ret <= 32)
throw error(error::windows_error, loc::translate("Shell execute failed."));
OpenInDefaultApplication(g_path_readme);
}
void Handler::OpenLogLocation() {
BOOST_LOG_TRIVIAL(info) << "Opening LOOT local appdata folder.";
//Open debug log folder.
HINSTANCE ret = ShellExecute(NULL, L"open", ToWinWide(g_path_log.parent_path().string()).c_str(), NULL, NULL, SW_SHOWNORMAL);
if ((int)ret <= 32)
throw error(error::windows_error, loc::translate("Shell execute failed."));
OpenInDefaultApplication(g_path_log.parent_path());
}
std::string Handler::GetVersion() {
+1 -4
View File
@@ -248,10 +248,7 @@ namespace loot {
}
BOOST_LOG_TRIVIAL(info) << "Opening link in Windows' default handler.";
// Open readme in default application.
HINSTANCE ret = ShellExecute(0, NULL, request->GetURL().ToWString().c_str(), NULL, NULL, SW_SHOWNORMAL);
if ((int)ret <= 32)
throw error(error::windows_error, "Shell execute failed.");
OpenInDefaultApplication(fs::path(request->GetURL().ToString()));
return true;
}
+4 -3
View File
@@ -144,7 +144,8 @@ namespace loot {
_initErrors.push_back((format(translate("Error: Games' settings parsing failed. %1%")) % e.what()).str());
// Now redo, but with no games settings, so only the hardcoded defaults get loaded. It means the user can
// at least still then edit them.
_games = ToGames(GetGameSettings(YAML::Node()));
YAML::Node node;
_games = ToGames(GetGameSettings(node));
}
try {
@@ -369,7 +370,7 @@ namespace loot {
// Conversion from 0.6 key.
_settings["games"] = _settings["Games"];
for (auto &node : _settings["games"]) {
for (auto node : _settings["games"]) {
if (node["url"]) {
node["repo"] = node["url"];
node["branch"] = "v0.8";
@@ -385,7 +386,7 @@ namespace loot {
else {
// Update existing default branches to new version default, if the
// default repositories are used.
for (auto &node : _settings["games"]) {
for (auto node : _settings["games"]) {
GameSettings settings(node.as<GameSettings>());
set<string> oldDefaultBranches({
+44 -8
View File
@@ -26,8 +26,13 @@
#include "loot_state.h"
#include "../backend/globals.h"
#ifdef _WIN32
#include <windows.h>
#include <include/cef_sandbox_win.h>
#else
#include <X11/Xlib.h>
#include "include/base/cef_logging.h"
#endif
#include <boost/format.hpp>
#include <boost/locale.hpp>
@@ -60,23 +65,49 @@ CefSettings GetCefSettings() {
return cef_settings;
}
#ifndef _WIN32
namespace {
int XErrorHandlerImpl(Display *display, XErrorEvent *event) {
LOG(WARNING)
<< "X error received: "
<< "type " << event->type << ", "
<< "serial " << event->serial << ", "
<< "error_code " << static_cast<int>(event->error_code) << ", "
<< "request_code " << static_cast<int>(event->request_code) << ", "
<< "minor_code " << static_cast<int>(event->minor_code);
return 0;
}
int XIOErrorHandlerImpl(Display *display) {
return 0;
}
}
#endif
#ifdef _WIN32
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
#else
int main(int argc, char* argv[]) {
#endif
// Do all the standard CEF setup stuff.
//-------------------------------------
void * sandbox_info = nullptr;
#ifdef _WIN32
// Enable High-DPI support on Windows 7 or newer.
CefEnableHighDPISupport();
// Set up CEF sandbox.
CefScopedSandboxInfo scoped_sandbox;
void * sandbox_info = scoped_sandbox.sandbox_info();
sandbox_info = scoped_sandbox.sandbox_info();
// Read command line arguments.
#ifdef _WIN32
CefMainArgs main_args(hInstance);
#else
// Read command line arguments.
CefMainArgs main_args(argc, argv);
#endif
// Create the process reference.
@@ -89,6 +120,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd
return exit_code;
}
#ifdef _WIN32
// Check if LOOT is already running
//---------------------------------
@@ -103,6 +135,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd
//Create the mutex so that future instances will not run.
hMutex = ::CreateMutex(NULL, FALSE, L"LOOT.Shell.Instance");
}
#endif
// Handle command line args (not CEF args)
//----------------------------------------
@@ -125,25 +158,28 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd
// Initialise CEF settings.
CefSettings cef_settings = GetCefSettings();
#ifndef _WIN32
// Install xlib error handlers so that the application won't be terminated
// on non-fatal errors.
XSetErrorHandler(XErrorHandlerImpl);
XSetIOErrorHandler(XIOErrorHandlerImpl);
#endif
// Initialize CEF.
CefInitialize(main_args, cef_settings, app.get(), sandbox_info);
// Do application init
//--------------------
// Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
#ifdef _WIN32
// Release the program instance mutex.
if (hMutex != NULL)
ReleaseMutex(hMutex);
#endif
return 0;
#ifdef _WIN32
}
#endif
+2
View File
@@ -25,8 +25,10 @@ along with LOOT. If not, see
#ifndef __LOOT_GUI_RESOURCE__
#define __LOOT_GUI_RESOURCE__
#ifdef _WIN32
#include <windows.h>
#define MAINICON 101
#endif
#endif