More cache work, should be fully done for apps now
Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
@@ -91,6 +91,19 @@ FetchContent_Declare(
|
||||
)
|
||||
FetchContent_MakeAvailable(cpptrace)
|
||||
|
||||
FetchContent_Declare(
|
||||
inotify-cpp
|
||||
GIT_REPOSITORY https://github.com/erikzenker/inotify-cpp.git
|
||||
GIT_TAG "v1.0.0"
|
||||
GIT_SHALLOW 1
|
||||
)
|
||||
set(BUILD_EXAMPLE OFF)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
set(BUILD_STATIC_LIBS ON)
|
||||
set(BUILD_TEST OFF)
|
||||
set(USE_BOOST_FILESYSTEM OFF)
|
||||
FetchContent_MakeAvailable(inotify-cpp)
|
||||
|
||||
add_subdirectory(vendor)
|
||||
|
||||
find_program(WAYLAND_SCANNER wayland-scanner REQUIRED)
|
||||
@@ -222,6 +235,7 @@ target_link_libraries(waylight PRIVATE
|
||||
msdfgen::msdfgen-ext
|
||||
lunasvg::lunasvg
|
||||
SQLiteCpp
|
||||
inotify-cpp-static
|
||||
|
||||
m
|
||||
dl
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
harfbuzz
|
||||
sqlite
|
||||
zenity
|
||||
boost
|
||||
];
|
||||
buildInputs = with pkgs; [
|
||||
cmake
|
||||
|
||||
10
src/App.cpp
10
src/App.cpp
@@ -170,7 +170,11 @@ App::App()
|
||||
path TEXT,
|
||||
comment TEXT,
|
||||
dbus_activatable BOOL NOT NULL
|
||||
);
|
||||
)
|
||||
)")
|
||||
.exec();
|
||||
|
||||
SQLite::Statement(*m_db, R"(
|
||||
CREATE TABLE IF NOT EXISTS ApplicationActionCache (
|
||||
id INTEGER PRIMARY KEY NOT NULL,
|
||||
id_app INTEGER NOT NULL,
|
||||
@@ -178,11 +182,11 @@ App::App()
|
||||
exec TEXT,
|
||||
icon TEXT,
|
||||
FOREIGN KEY (id_app) REFERENCES ApplicationCache(id)
|
||||
);
|
||||
)
|
||||
)")
|
||||
.exec();
|
||||
|
||||
m_cache = Waylight::Cache(m_db);
|
||||
m_cache.emplace(m_db);
|
||||
}
|
||||
|
||||
App::~App()
|
||||
|
||||
222
src/Cache.cpp
222
src/Cache.cpp
@@ -12,6 +12,18 @@
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
void replace_all(
|
||||
std::string &str, std::string const &from, std::string const &to)
|
||||
{
|
||||
if (from.empty())
|
||||
return;
|
||||
size_t pos = 0;
|
||||
while ((pos = str.find(from, pos)) != std::string::npos) {
|
||||
str.replace(pos, from.size(), to);
|
||||
pos += to.size();
|
||||
}
|
||||
}
|
||||
|
||||
namespace Waylight {
|
||||
|
||||
Cache::Cache(std::shared_ptr<SQLite::Database> db)
|
||||
@@ -74,11 +86,32 @@ Cache::Cache(std::shared_ptr<SQLite::Database> db)
|
||||
std::format(" Exec: {}", *action.exec).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const &dir : m_app_dirs) {
|
||||
m_inot.watchPathRecursively(dir);
|
||||
}
|
||||
|
||||
auto events = {
|
||||
inotify::Event::open | inotify::Event::is_dir,
|
||||
inotify::Event::create,
|
||||
inotify::Event::modify,
|
||||
inotify::Event::remove,
|
||||
inotify::Event::move,
|
||||
};
|
||||
m_inot.onEvents(events, [this](auto notification) { rescan(); });
|
||||
|
||||
m_inot_th = std::thread([this]() { m_inot.run(); });
|
||||
}
|
||||
|
||||
Cache::~Cache()
|
||||
{
|
||||
m_inot.stop();
|
||||
m_inot_th.join();
|
||||
}
|
||||
|
||||
void Cache::rescan()
|
||||
{
|
||||
m_app_dirs.clear();
|
||||
m_apps.clear();
|
||||
|
||||
int id = 0;
|
||||
for (auto const &dir : m_app_dirs) {
|
||||
@@ -93,40 +126,76 @@ void Cache::rescan()
|
||||
mINI::INIStructure ini;
|
||||
ini_file.read(ini);
|
||||
|
||||
constexpr auto read_action =
|
||||
[&](mINI::INIMap<std::string> const §ion) {
|
||||
return ApplicationCache::Action {
|
||||
.name = section.get("Name"),
|
||||
.exec = [&]() -> std::optional<std::string> {
|
||||
if (section.has("Exec")) {
|
||||
auto const s = section.get("Exec");
|
||||
if (!s.empty())
|
||||
return s;
|
||||
}
|
||||
return std::nullopt;
|
||||
}(),
|
||||
.icon = [&]() -> std::optional<std::variant<
|
||||
std::filesystem::path, std::string>> {
|
||||
if (section.has("Icon")) {
|
||||
auto const icon_name = section.get("Icon");
|
||||
if (!icon_name.empty()) {
|
||||
if (icon_name[0] == '/') {
|
||||
return std::filesystem::path(icon_name);
|
||||
} else {
|
||||
return icon_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}(),
|
||||
};
|
||||
};
|
||||
constexpr auto read_action = [&](std::string desktop_file_uri,
|
||||
mINI::INIMap<std::string> const
|
||||
§ion) {
|
||||
auto const name = section.get("Name");
|
||||
auto const icon = [&]()
|
||||
-> std::optional<
|
||||
std::variant<std::filesystem::path, std::string>> {
|
||||
if (section.has("Icon")) {
|
||||
auto const icon_name = section.get("Icon");
|
||||
if (!icon_name.empty()) {
|
||||
if (icon_name[0] == '/') {
|
||||
return std::filesystem::path(icon_name);
|
||||
} else {
|
||||
return icon_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}();
|
||||
|
||||
ApplicationCache app {
|
||||
.id = id++,
|
||||
.desktop_entry_path = file.path(),
|
||||
.type =
|
||||
[&]() {
|
||||
return ApplicationCache::Action {
|
||||
.name = name,
|
||||
.exec = [&]() -> std::optional<std::string> {
|
||||
if (section.has("Exec")) {
|
||||
auto s = section.get("Exec");
|
||||
if (!s.empty()) {
|
||||
// Either deprecated or not used...
|
||||
replace_all(s, "%f", "");
|
||||
replace_all(s, "%F", "");
|
||||
replace_all(s, "%u", "");
|
||||
replace_all(s, "%U", "");
|
||||
replace_all(s, "%d", "");
|
||||
replace_all(s, "%D", "");
|
||||
replace_all(s, "%n", "");
|
||||
replace_all(s, "%N", "");
|
||||
replace_all(s, "%v", "");
|
||||
replace_all(s, "%M", "");
|
||||
|
||||
replace_all(s, "%c", name);
|
||||
if (icon) {
|
||||
if (auto const p
|
||||
= std::get_if<std::filesystem::path>(
|
||||
&*icon)) {
|
||||
replace_all(s, "%i",
|
||||
"--icon '" + p->string() + "'");
|
||||
} else if (auto const n
|
||||
= std::get_if<std::string>(&*icon)) {
|
||||
replace_all(
|
||||
s, "%i", "--icon '" + *n + "'");
|
||||
} else {
|
||||
replace_all(s, "%i", "");
|
||||
}
|
||||
} else {
|
||||
replace_all(s, "%i", "");
|
||||
}
|
||||
replace_all(s, "%k", "'" + desktop_file_uri);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}(),
|
||||
.icon = icon,
|
||||
};
|
||||
};
|
||||
|
||||
m_apps.push_back({
|
||||
.id = id++,
|
||||
.desktop_entry_path = file.path(),
|
||||
.type =
|
||||
[&]() {
|
||||
auto const type_str { ini["Desktop Entry"].get(
|
||||
"Type") };
|
||||
auto type { ApplicationCache::Type::Application };
|
||||
@@ -137,51 +206,54 @@ void Cache::rescan()
|
||||
else if (type_str == "Directory")
|
||||
type = ApplicationCache::Type::Directory;
|
||||
return type;
|
||||
}(),
|
||||
.terminal =
|
||||
[&]() {
|
||||
}(),
|
||||
.terminal =
|
||||
[&]() {
|
||||
if (ini["Desktop Entry"].has("Terminal")) {
|
||||
return ini["Desktop Entry"]["Terminal"] == "true"
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
return false;
|
||||
}(),
|
||||
.no_display =
|
||||
[&]() {
|
||||
}(),
|
||||
.no_display =
|
||||
[&]() {
|
||||
if (ini["Desktop Entry"].has("NoDisplay")) {
|
||||
return ini["Desktop Entry"]["NoDisplay"] == "true"
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
return false;
|
||||
}(),
|
||||
.path = [&]() -> std::optional<std::string> {
|
||||
}(),
|
||||
.path = [&]() -> std::optional<std::string> {
|
||||
if (ini["Desktop Entry"].has("Path")) {
|
||||
return ini["Desktop Entry"]["Path"];
|
||||
}
|
||||
return std::nullopt;
|
||||
}(),
|
||||
.comment = [&]() -> std::optional<std::string> {
|
||||
}(),
|
||||
.comment = [&]() -> std::optional<std::string> {
|
||||
if (ini["Desktop Entry"].has("Comment")) {
|
||||
return ini["Desktop Entry"]["Comment"];
|
||||
}
|
||||
return std::nullopt;
|
||||
}(),
|
||||
.actions =
|
||||
[&]() {
|
||||
}(),
|
||||
.actions =
|
||||
[&]() {
|
||||
std::vector<ApplicationCache::Action> actions;
|
||||
for (auto const &[_, v] : ini) {
|
||||
try {
|
||||
auto const action = read_action(v);
|
||||
auto const action = read_action(
|
||||
std::filesystem::canonical(file.path())
|
||||
.string(),
|
||||
v);
|
||||
actions.push_back(action);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
}(),
|
||||
.dbus_activatable =
|
||||
[&]() {
|
||||
}(),
|
||||
.dbus_activatable =
|
||||
[&]() {
|
||||
if (ini["Desktop Entry"].has("DBusActivatable")) {
|
||||
return ini["Desktop Entry"]["DBusActivatable"]
|
||||
== "true"
|
||||
@@ -189,10 +261,12 @@ void Cache::rescan()
|
||||
: false;
|
||||
}
|
||||
return false;
|
||||
}(),
|
||||
};
|
||||
}(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
dump();
|
||||
}
|
||||
|
||||
void Cache::dump()
|
||||
@@ -221,6 +295,7 @@ void Cache::dump()
|
||||
"(?,?,?,?)");
|
||||
|
||||
for (auto &app : m_apps) {
|
||||
ins_app.reset();
|
||||
ins_app.clearBindings();
|
||||
ins_app.bind(1, static_cast<int>(app.type));
|
||||
ins_app.bind(2, app.desktop_entry_path.string());
|
||||
@@ -240,6 +315,7 @@ void Cache::dump()
|
||||
app.id = m_db->getLastInsertRowid();
|
||||
|
||||
for (auto const &action : app.actions) {
|
||||
ins_act.reset();
|
||||
ins_act.clearBindings();
|
||||
ins_act.bind(1, app.id);
|
||||
ins_act.bind(2, action.name);
|
||||
@@ -271,34 +347,34 @@ void Cache::load()
|
||||
{
|
||||
m_apps.clear();
|
||||
|
||||
SQLite::Statement getApps(*m_db,
|
||||
SQLite::Statement get_apps(*m_db,
|
||||
"SELECT id, type, desktop_entry_path, terminal, no_display, path, "
|
||||
"comment, dbus_activatable "
|
||||
"FROM ApplicationCache");
|
||||
|
||||
std::unordered_map<std::int64_t, std::size_t> id_to_index;
|
||||
|
||||
while (getApps.executeStep()) {
|
||||
while (get_apps.executeStep()) {
|
||||
ApplicationCache app {};
|
||||
app.id = getApps.getColumn(0).getInt64();
|
||||
app.id = get_apps.getColumn(0).getInt64();
|
||||
app.type = static_cast<ApplicationCache::Type>(
|
||||
getApps.getColumn(1).getInt());
|
||||
get_apps.getColumn(1).getInt());
|
||||
app.desktop_entry_path
|
||||
= std::filesystem::path(getApps.getColumn(2).getString());
|
||||
app.terminal = getApps.getColumn(3).getInt() != 0;
|
||||
app.no_display = getApps.getColumn(4).getInt() != 0;
|
||||
= std::filesystem::path(get_apps.getColumn(2).getString());
|
||||
app.terminal = get_apps.getColumn(3).getInt() != 0;
|
||||
app.no_display = get_apps.getColumn(4).getInt() != 0;
|
||||
|
||||
if (!getApps.getColumn(5).isNull())
|
||||
app.path = std::string(getApps.getColumn(5).getString());
|
||||
if (!get_apps.getColumn(5).isNull())
|
||||
app.path = std::string(get_apps.getColumn(5).getString());
|
||||
else
|
||||
app.path.reset();
|
||||
|
||||
if (!getApps.getColumn(6).isNull())
|
||||
app.comment = std::string(getApps.getColumn(6).getString());
|
||||
if (!get_apps.getColumn(6).isNull())
|
||||
app.comment = std::string(get_apps.getColumn(6).getString());
|
||||
else
|
||||
app.comment.reset();
|
||||
|
||||
app.dbus_activatable = getApps.getColumn(7).getInt() != 0;
|
||||
app.dbus_activatable = get_apps.getColumn(7).getInt() != 0;
|
||||
|
||||
id_to_index.emplace(app.id, m_apps.size());
|
||||
m_apps.push_back(std::move(app));
|
||||
@@ -307,27 +383,27 @@ void Cache::load()
|
||||
if (m_apps.empty())
|
||||
return;
|
||||
|
||||
SQLite::Statement getActions(*m_db,
|
||||
SQLite::Statement get_actions(*m_db,
|
||||
"SELECT id_app, name, exec, icon "
|
||||
"FROM ApplicationActionCache "
|
||||
"ORDER BY id_app");
|
||||
|
||||
while (getActions.executeStep()) {
|
||||
auto id_app = getActions.getColumn(0).getInt64();
|
||||
while (get_actions.executeStep()) {
|
||||
auto id_app = get_actions.getColumn(0).getInt64();
|
||||
auto it = id_to_index.find(id_app);
|
||||
if (it == id_to_index.end())
|
||||
continue;
|
||||
|
||||
ApplicationCache::Action action {};
|
||||
action.name = std::string(getActions.getColumn(1).getString());
|
||||
action.name = std::string(get_actions.getColumn(1).getString());
|
||||
|
||||
if (!getActions.getColumn(2).isNull())
|
||||
action.exec = std::string(getActions.getColumn(2).getString());
|
||||
if (!get_actions.getColumn(2).isNull())
|
||||
action.exec = std::string(get_actions.getColumn(2).getString());
|
||||
else
|
||||
action.exec.reset();
|
||||
|
||||
if (!getActions.getColumn(3).isNull()) {
|
||||
auto const str = getActions.getColumn(3).getString();
|
||||
if (!get_actions.getColumn(3).isNull()) {
|
||||
auto const str = get_actions.getColumn(3).getString();
|
||||
if (str.at(0) == '/') {
|
||||
action.icon
|
||||
= std::filesystem::canonical(std::filesystem::path(str));
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <SQLiteCpp/Database.h>
|
||||
#include <inotify-cpp/NotifierBuilder.h>
|
||||
|
||||
namespace Waylight {
|
||||
|
||||
@@ -46,6 +48,7 @@ struct ApplicationCache {
|
||||
|
||||
struct Cache {
|
||||
explicit Cache(std::shared_ptr<SQLite::Database> db);
|
||||
~Cache();
|
||||
|
||||
void rescan();
|
||||
void dump();
|
||||
@@ -55,6 +58,9 @@ private:
|
||||
std::vector<ApplicationCache> m_apps;
|
||||
std::vector<std::filesystem::path> m_app_dirs;
|
||||
std::shared_ptr<SQLite::Database> m_db;
|
||||
|
||||
inotify::NotifierBuilder m_inot;
|
||||
std::thread m_inot_th;
|
||||
};
|
||||
|
||||
} // namespace Cache
|
||||
|
||||
Reference in New Issue
Block a user