desktop: Add functionality to enumerate paths of interest
This allows querying things such as the user's home dir, drive letters (Windows-only), and game paths such as the data dir, preferences, user data, and binaries. The results are presented in a format that's suitable for UI use, with translatable labels used wherever applicable. At some point there'll be support for listing user-defined bookmarks as well.
This commit is contained in:
parent
db0b20dae7
commit
4915f0349c
5 changed files with 324 additions and 0 deletions
|
@ -243,6 +243,8 @@
|
|||
<Unit filename="../../src/desktop/notifications.hpp" />
|
||||
<Unit filename="../../src/desktop/open.cpp" />
|
||||
<Unit filename="../../src/desktop/open.hpp" />
|
||||
<Unit filename="../../src/desktop/paths.cpp" />
|
||||
<Unit filename="../../src/desktop/paths.hpp" />
|
||||
<Unit filename="../../src/desktop/version.cpp" />
|
||||
<Unit filename="../../src/desktop/version.hpp" />
|
||||
<Unit filename="../../src/desktop/windows_tray_notification.cpp" />
|
||||
|
|
|
@ -693,6 +693,7 @@ set(wesnoth-main_SRC
|
|||
game_initialization/depcheck.cpp
|
||||
desktop/notifications.cpp
|
||||
desktop/open.cpp
|
||||
desktop/paths.cpp
|
||||
desktop/version.cpp
|
||||
display_chat_manager.cpp
|
||||
editor/action/action.cpp
|
||||
|
|
|
@ -250,6 +250,7 @@ wesnoth_sources = Split("""
|
|||
countdown_clock.cpp
|
||||
desktop/notifications.cpp
|
||||
desktop/open.cpp
|
||||
desktop/paths.cpp
|
||||
desktop/version.cpp
|
||||
display_chat_manager.cpp
|
||||
editor/action/action_item.cpp
|
||||
|
|
226
src/desktop/paths.cpp
Normal file
226
src/desktop/paths.cpp
Normal file
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
Copyright (C) 2016 by Ignacio R. Morelle <shadowm2006@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-lib"
|
||||
|
||||
#include "desktop/paths.hpp"
|
||||
|
||||
#include "game_config.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "log.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "serialization/unicode.hpp"
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
#include <boost/filesystem.hpp>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
// For username stuff on Unix:
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#else // _WIN32
|
||||
|
||||
#ifndef UNICODE
|
||||
#define UNICODE
|
||||
#endif
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
#endif
|
||||
|
||||
static lg::log_domain log_desktop("desktop");
|
||||
#define ERR_DU LOG_STREAM(err, log_desktop)
|
||||
#define LOG_DU LOG_STREAM(info, log_desktop)
|
||||
#define DBG_DU LOG_STREAM(debug, log_desktop)
|
||||
|
||||
namespace desktop
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void enumerate_storage_devices(std::vector<path_info>& res)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
const DWORD drive_table = GetLogicalDrives();
|
||||
|
||||
for(unsigned n = 0; n < 26; ++n) {
|
||||
if((drive_table >> n) & 1) {
|
||||
std::string u8drive = "A:";
|
||||
u8drive[0] += n;
|
||||
|
||||
LOG_DU << "enumerate_win32_drives(): " << u8drive << " is reported to be present\n";
|
||||
|
||||
wchar_t drive[] = L"A:\\";
|
||||
drive[0] += n;
|
||||
|
||||
constexpr DWORD label_bufsize = MAX_PATH + 1;
|
||||
wchar_t label[label_bufsize] = { 0 };
|
||||
|
||||
if(GetVolumeInformation(drive, label, label_bufsize, NULL, NULL, NULL, NULL, 0) == 0) {
|
||||
const DWORD err = GetLastError();
|
||||
ERR_DU << "enumerate_win32_drives(): GetVolumeInformation() failed (" << err << ")\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Trailing slash so that we don't get compatibility per-drive working dirs
|
||||
// involved in path resolution.
|
||||
res.push_back({u8drive, unicode_cast<std::string>(std::wstring{label}), u8drive + '\\'});
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
// Probably as unreliable as /media|/mnt on other platforms, not worth
|
||||
// examining in detail.
|
||||
res.push_back({{ N_("filesystem_path_system^Volumes"), GETTEXT_DOMAIN }, "", "/Volumes"});
|
||||
|
||||
#else
|
||||
|
||||
namespace bsys = boost::system;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
// These are either used as mount points themselves, or host mount points. The
|
||||
// reasoning here is that if any or all of them are non-empty, they are
|
||||
// probably used for _something_ that might be of interest to the user (if not
|
||||
// directly and actively controlled by the user themselves).
|
||||
static const std::vector<std::string> candidates = { "/media", "/mnt" };
|
||||
|
||||
for(const auto& mnt : candidates) {
|
||||
bsys::error_code e;
|
||||
if(bfs::is_directory(mnt, e) && !bfs::is_empty(mnt, e) && !e) {
|
||||
DBG_DU << "enumerate_mount_parents(): " << mnt << " appears to be a non-empty dir\n";
|
||||
res.push_back({mnt, "", mnt});
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
bool have_path(const std::vector<path_info>& pathset, const std::string& path)
|
||||
{
|
||||
for(const auto& pinfo : pathset) {
|
||||
if(pinfo.path == path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline std::string pretty_path(const std::string& path)
|
||||
{
|
||||
return filesystem::normalize_path(path, true, true);
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
std::string user_profile_dir()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
|
||||
// TODO: The filesystem API uses $HOME for this purpose, which may be
|
||||
// overridden or missing. Not sure which one really makes more sense
|
||||
// for us here.
|
||||
const passwd* const pwd = getpwuid(geteuid());
|
||||
|
||||
if(!pwd || !pwd->pw_dir || !*pwd->pw_dir) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return pwd->pw_dir;
|
||||
|
||||
#else // _WIN32
|
||||
|
||||
wchar_t profile_path[MAX_PATH];
|
||||
HRESULT res = SHGetFolderPath(nullptr, CSIDL_PROFILE, nullptr, SHGFP_TYPE_CURRENT, profile_path);
|
||||
return res != S_OK ? "" : unicode_cast<std::string>(std::wstring{profile_path});
|
||||
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
std::string path_info::display_name() const
|
||||
{
|
||||
return label.empty() ? name : label + " (" + name + ")";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const path_info& pinf)
|
||||
{
|
||||
return os << pinf.name << " [" << pinf.label << "] - " << pinf.path;
|
||||
}
|
||||
|
||||
std::vector<path_info> game_paths(unsigned path_types)
|
||||
{
|
||||
static const std::string& game_bin_dir = pretty_path(filesystem::get_exe_dir());
|
||||
static const std::string& game_data_dir = pretty_path(game_config::path);
|
||||
static const std::string& game_user_data_dir = pretty_path(filesystem::get_user_data_dir());
|
||||
static const std::string& game_user_pref_dir = pretty_path(filesystem::get_user_config_dir());
|
||||
|
||||
std::vector<path_info> res;
|
||||
|
||||
if(path_types & GAME_BIN_DIR && !have_path(res, game_bin_dir)) {
|
||||
res.push_back({{ N_("filesystem_path_game^Game executables"), GETTEXT_DOMAIN }, "", game_bin_dir});
|
||||
}
|
||||
|
||||
if(path_types & GAME_CORE_DATA_DIR && !have_path(res, game_data_dir)) {
|
||||
res.push_back({{ N_("filesystem_path_game^Game data"), GETTEXT_DOMAIN }, "", game_data_dir});
|
||||
}
|
||||
|
||||
if(path_types & GAME_USER_DATA_DIR && !have_path(res, game_user_data_dir)) {
|
||||
res.push_back({{ N_("filesystem_path_game^User data"), GETTEXT_DOMAIN }, "", game_user_data_dir});
|
||||
}
|
||||
|
||||
if(path_types & GAME_USER_PREFS_DIR && !have_path(res, game_user_pref_dir)) {
|
||||
res.push_back({{ N_("filesystem_path_game^User preferences"), GETTEXT_DOMAIN }, "", game_user_pref_dir});
|
||||
}
|
||||
|
||||
if(path_types & GAME_USER_BOOKMARKS) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<path_info> system_paths(unsigned path_types)
|
||||
{
|
||||
static const std::string& home_dir = user_profile_dir();
|
||||
|
||||
std::vector<path_info> res;
|
||||
|
||||
if(path_types & SYSTEM_USER_PROFILE && !home_dir.empty()) {
|
||||
res.push_back({{ N_("filesystem_path_system^Home"), GETTEXT_DOMAIN }, "", home_dir});
|
||||
}
|
||||
|
||||
if(path_types & SYSTEM_ALL_DRIVES) {
|
||||
enumerate_storage_devices(res);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
if(path_types & SYSTEM_ROOTFS) {
|
||||
res.push_back({{ N_("filesystem_path_system^Root"), GETTEXT_DOMAIN }, "", "/"});
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace desktop
|
94
src/desktop/paths.hpp
Normal file
94
src/desktop/paths.hpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
Copyright (C) 2016 by Ignacio R. Morelle <shadowm2006@gmail.com>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Desktop paths, storage media and bookmark functions.
|
||||
*/
|
||||
|
||||
#ifndef DESKTOP_PATHS_HPP_INCLUDED
|
||||
#define DESKTOP_PATHS_HPP_INCLUDED
|
||||
|
||||
#include "tstring.hpp"
|
||||
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
||||
namespace desktop
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns the path to the user profile dir (e.g. /home/username).
|
||||
*
|
||||
* An empty string is returned if the path cannot be determined somehow.
|
||||
*/
|
||||
std::string user_profile_dir();
|
||||
|
||||
struct path_info
|
||||
{
|
||||
/** Path name or drive letter/mount point path; may be a translatable string if it's a game resources path. */
|
||||
t_string name;
|
||||
/** System-defined label, if the path is a drive or mount point. */
|
||||
std::string label;
|
||||
/** Real path. */
|
||||
std::string path;
|
||||
|
||||
/**
|
||||
* Formats this path for UI display.
|
||||
*/
|
||||
std::string display_name() const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const path_info& pinf);
|
||||
|
||||
enum GAME_PATH_TYPES
|
||||
{
|
||||
GAME_BIN_DIR = 0x1, /**< Game executable dir. */
|
||||
GAME_CORE_DATA_DIR = 0x2, /**< Game data dir. */
|
||||
GAME_USER_PREFS_DIR = 0x4, /**< User preferences dir. */
|
||||
GAME_USER_DATA_DIR = 0x8, /**< User data dir. */
|
||||
GAME_USER_BOOKMARKS = 0x10 /**< User-defined bookmarked paths. */
|
||||
};
|
||||
|
||||
enum SYSTEM_PATH_TYPES
|
||||
{
|
||||
SYSTEM_ALL_DRIVES = 0x1, /**< Paths for each storage media found (Windows), /media and/or /mnt (X11, if non-empty). */
|
||||
SYSTEM_USER_PROFILE = 0x2, /**< Path to the user's profile dir (e.g. /home/username or X:\\Users\\Username). */
|
||||
SYSTEM_ROOTFS = 0x4 /**< Path to the root of the filesystem hierarchy (ignored on Windows). */
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of game-related paths.
|
||||
*
|
||||
* These paths are guaranteed to be their canonical forms (with links and dot
|
||||
* entries resolved) and using the platform's preferred path delimiter.
|
||||
*/
|
||||
std::vector<path_info> game_paths(unsigned path_types = GAME_CORE_DATA_DIR | GAME_USER_DATA_DIR | GAME_USER_BOOKMARKS);
|
||||
|
||||
/**
|
||||
* Returns a list of system-defined paths.
|
||||
*
|
||||
* This includes removable media on platforms where we can reasonably
|
||||
* accurately enumerate those (FIXME: only Windows right now), the path to the
|
||||
* user's profile directory (/home/username, X:\\Users\\Username, etc.), and
|
||||
* the system drive root.
|
||||
*
|
||||
* These paths are guaranteed to be their canonical forms (with links and dot
|
||||
* entries resolved) and using the platform's preferred path delimiter.
|
||||
*/
|
||||
std::vector<path_info> system_paths(unsigned path_types = SYSTEM_ALL_DRIVES | SYSTEM_USER_PROFILE | SYSTEM_ROOTFS);
|
||||
|
||||
} // namespace desktop
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue