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:
Ignacio R. Morelle 2016-10-10 22:37:30 -03:00
parent db0b20dae7
commit 4915f0349c
5 changed files with 324 additions and 0 deletions

View file

@ -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" />

View file

@ -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

View file

@ -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
View 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
View 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