More cache work, should be fully done for apps now

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-10-16 20:25:06 +03:00
parent d368760f78
commit f61710010d
5 changed files with 177 additions and 76 deletions

View File

@@ -91,6 +91,19 @@ FetchContent_Declare(
) )
FetchContent_MakeAvailable(cpptrace) 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) add_subdirectory(vendor)
find_program(WAYLAND_SCANNER wayland-scanner REQUIRED) find_program(WAYLAND_SCANNER wayland-scanner REQUIRED)
@@ -222,6 +235,7 @@ target_link_libraries(waylight PRIVATE
msdfgen::msdfgen-ext msdfgen::msdfgen-ext
lunasvg::lunasvg lunasvg::lunasvg
SQLiteCpp SQLiteCpp
inotify-cpp-static
m m
dl dl

View File

@@ -30,6 +30,7 @@
harfbuzz harfbuzz
sqlite sqlite
zenity zenity
boost
]; ];
buildInputs = with pkgs; [ buildInputs = with pkgs; [
cmake cmake

View File

@@ -170,7 +170,11 @@ App::App()
path TEXT, path TEXT,
comment TEXT, comment TEXT,
dbus_activatable BOOL NOT NULL dbus_activatable BOOL NOT NULL
); )
)")
.exec();
SQLite::Statement(*m_db, R"(
CREATE TABLE IF NOT EXISTS ApplicationActionCache ( CREATE TABLE IF NOT EXISTS ApplicationActionCache (
id INTEGER PRIMARY KEY NOT NULL, id INTEGER PRIMARY KEY NOT NULL,
id_app INTEGER NOT NULL, id_app INTEGER NOT NULL,
@@ -178,11 +182,11 @@ App::App()
exec TEXT, exec TEXT,
icon TEXT, icon TEXT,
FOREIGN KEY (id_app) REFERENCES ApplicationCache(id) FOREIGN KEY (id_app) REFERENCES ApplicationCache(id)
); )
)") )")
.exec(); .exec();
m_cache = Waylight::Cache(m_db); m_cache.emplace(m_db);
} }
App::~App() App::~App()

View File

@@ -12,6 +12,18 @@
#include "common.hpp" #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 { namespace Waylight {
Cache::Cache(std::shared_ptr<SQLite::Database> db) 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()); 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() void Cache::rescan()
{ {
m_app_dirs.clear(); m_apps.clear();
int id = 0; int id = 0;
for (auto const &dir : m_app_dirs) { for (auto const &dir : m_app_dirs) {
@@ -93,40 +126,76 @@ void Cache::rescan()
mINI::INIStructure ini; mINI::INIStructure ini;
ini_file.read(ini); ini_file.read(ini);
constexpr auto read_action = constexpr auto read_action = [&](std::string desktop_file_uri,
[&](mINI::INIMap<std::string> const &section) { mINI::INIMap<std::string> const
return ApplicationCache::Action { &section) {
.name = section.get("Name"), auto const name = section.get("Name");
.exec = [&]() -> std::optional<std::string> { auto const icon = [&]()
if (section.has("Exec")) { -> std::optional<
auto const s = section.get("Exec"); std::variant<std::filesystem::path, std::string>> {
if (!s.empty()) if (section.has("Icon")) {
return s; auto const icon_name = section.get("Icon");
} if (!icon_name.empty()) {
return std::nullopt; if (icon_name[0] == '/') {
}(), return std::filesystem::path(icon_name);
.icon = [&]() -> std::optional<std::variant< } else {
std::filesystem::path, std::string>> { return icon_name;
if (section.has("Icon")) { }
auto const icon_name = section.get("Icon"); }
if (!icon_name.empty()) { }
if (icon_name[0] == '/') { return std::nullopt;
return std::filesystem::path(icon_name); }();
} else {
return icon_name;
}
}
}
return std::nullopt;
}(),
};
};
ApplicationCache app { return ApplicationCache::Action {
.id = id++, .name = name,
.desktop_entry_path = file.path(), .exec = [&]() -> std::optional<std::string> {
.type = 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( auto const type_str { ini["Desktop Entry"].get(
"Type") }; "Type") };
auto type { ApplicationCache::Type::Application }; auto type { ApplicationCache::Type::Application };
@@ -137,51 +206,54 @@ void Cache::rescan()
else if (type_str == "Directory") else if (type_str == "Directory")
type = ApplicationCache::Type::Directory; type = ApplicationCache::Type::Directory;
return type; return type;
}(), }(),
.terminal = .terminal =
[&]() { [&]() {
if (ini["Desktop Entry"].has("Terminal")) { if (ini["Desktop Entry"].has("Terminal")) {
return ini["Desktop Entry"]["Terminal"] == "true" return ini["Desktop Entry"]["Terminal"] == "true"
? true ? true
: false; : false;
} }
return false; return false;
}(), }(),
.no_display = .no_display =
[&]() { [&]() {
if (ini["Desktop Entry"].has("NoDisplay")) { if (ini["Desktop Entry"].has("NoDisplay")) {
return ini["Desktop Entry"]["NoDisplay"] == "true" return ini["Desktop Entry"]["NoDisplay"] == "true"
? true ? true
: false; : false;
} }
return false; return false;
}(), }(),
.path = [&]() -> std::optional<std::string> { .path = [&]() -> std::optional<std::string> {
if (ini["Desktop Entry"].has("Path")) { if (ini["Desktop Entry"].has("Path")) {
return ini["Desktop Entry"]["Path"]; return ini["Desktop Entry"]["Path"];
} }
return std::nullopt; return std::nullopt;
}(), }(),
.comment = [&]() -> std::optional<std::string> { .comment = [&]() -> std::optional<std::string> {
if (ini["Desktop Entry"].has("Comment")) { if (ini["Desktop Entry"].has("Comment")) {
return ini["Desktop Entry"]["Comment"]; return ini["Desktop Entry"]["Comment"];
} }
return std::nullopt; return std::nullopt;
}(), }(),
.actions = .actions =
[&]() { [&]() {
std::vector<ApplicationCache::Action> actions; std::vector<ApplicationCache::Action> actions;
for (auto const &[_, v] : ini) { for (auto const &[_, v] : ini) {
try { try {
auto const action = read_action(v); auto const action = read_action(
std::filesystem::canonical(file.path())
.string(),
v);
actions.push_back(action); actions.push_back(action);
} catch (...) { } catch (...) {
} }
} }
return actions; return actions;
}(), }(),
.dbus_activatable = .dbus_activatable =
[&]() { [&]() {
if (ini["Desktop Entry"].has("DBusActivatable")) { if (ini["Desktop Entry"].has("DBusActivatable")) {
return ini["Desktop Entry"]["DBusActivatable"] return ini["Desktop Entry"]["DBusActivatable"]
== "true" == "true"
@@ -189,10 +261,12 @@ void Cache::rescan()
: false; : false;
} }
return false; return false;
}(), }(),
}; });
} }
} }
dump();
} }
void Cache::dump() void Cache::dump()
@@ -221,6 +295,7 @@ void Cache::dump()
"(?,?,?,?)"); "(?,?,?,?)");
for (auto &app : m_apps) { for (auto &app : m_apps) {
ins_app.reset();
ins_app.clearBindings(); ins_app.clearBindings();
ins_app.bind(1, static_cast<int>(app.type)); ins_app.bind(1, static_cast<int>(app.type));
ins_app.bind(2, app.desktop_entry_path.string()); ins_app.bind(2, app.desktop_entry_path.string());
@@ -240,6 +315,7 @@ void Cache::dump()
app.id = m_db->getLastInsertRowid(); app.id = m_db->getLastInsertRowid();
for (auto const &action : app.actions) { for (auto const &action : app.actions) {
ins_act.reset();
ins_act.clearBindings(); ins_act.clearBindings();
ins_act.bind(1, app.id); ins_act.bind(1, app.id);
ins_act.bind(2, action.name); ins_act.bind(2, action.name);
@@ -271,34 +347,34 @@ void Cache::load()
{ {
m_apps.clear(); m_apps.clear();
SQLite::Statement getApps(*m_db, SQLite::Statement get_apps(*m_db,
"SELECT id, type, desktop_entry_path, terminal, no_display, path, " "SELECT id, type, desktop_entry_path, terminal, no_display, path, "
"comment, dbus_activatable " "comment, dbus_activatable "
"FROM ApplicationCache"); "FROM ApplicationCache");
std::unordered_map<std::int64_t, std::size_t> id_to_index; std::unordered_map<std::int64_t, std::size_t> id_to_index;
while (getApps.executeStep()) { while (get_apps.executeStep()) {
ApplicationCache app {}; ApplicationCache app {};
app.id = getApps.getColumn(0).getInt64(); app.id = get_apps.getColumn(0).getInt64();
app.type = static_cast<ApplicationCache::Type>( app.type = static_cast<ApplicationCache::Type>(
getApps.getColumn(1).getInt()); get_apps.getColumn(1).getInt());
app.desktop_entry_path app.desktop_entry_path
= std::filesystem::path(getApps.getColumn(2).getString()); = std::filesystem::path(get_apps.getColumn(2).getString());
app.terminal = getApps.getColumn(3).getInt() != 0; app.terminal = get_apps.getColumn(3).getInt() != 0;
app.no_display = getApps.getColumn(4).getInt() != 0; app.no_display = get_apps.getColumn(4).getInt() != 0;
if (!getApps.getColumn(5).isNull()) if (!get_apps.getColumn(5).isNull())
app.path = std::string(getApps.getColumn(5).getString()); app.path = std::string(get_apps.getColumn(5).getString());
else else
app.path.reset(); app.path.reset();
if (!getApps.getColumn(6).isNull()) if (!get_apps.getColumn(6).isNull())
app.comment = std::string(getApps.getColumn(6).getString()); app.comment = std::string(get_apps.getColumn(6).getString());
else else
app.comment.reset(); 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()); id_to_index.emplace(app.id, m_apps.size());
m_apps.push_back(std::move(app)); m_apps.push_back(std::move(app));
@@ -307,27 +383,27 @@ void Cache::load()
if (m_apps.empty()) if (m_apps.empty())
return; return;
SQLite::Statement getActions(*m_db, SQLite::Statement get_actions(*m_db,
"SELECT id_app, name, exec, icon " "SELECT id_app, name, exec, icon "
"FROM ApplicationActionCache " "FROM ApplicationActionCache "
"ORDER BY id_app"); "ORDER BY id_app");
while (getActions.executeStep()) { while (get_actions.executeStep()) {
auto id_app = getActions.getColumn(0).getInt64(); auto id_app = get_actions.getColumn(0).getInt64();
auto it = id_to_index.find(id_app); auto it = id_to_index.find(id_app);
if (it == id_to_index.end()) if (it == id_to_index.end())
continue; continue;
ApplicationCache::Action action {}; 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()) if (!get_actions.getColumn(2).isNull())
action.exec = std::string(getActions.getColumn(2).getString()); action.exec = std::string(get_actions.getColumn(2).getString());
else else
action.exec.reset(); action.exec.reset();
if (!getActions.getColumn(3).isNull()) { if (!get_actions.getColumn(3).isNull()) {
auto const str = getActions.getColumn(3).getString(); auto const str = get_actions.getColumn(3).getString();
if (str.at(0) == '/') { if (str.at(0) == '/') {
action.icon action.icon
= std::filesystem::canonical(std::filesystem::path(str)); = std::filesystem::canonical(std::filesystem::path(str));

View File

@@ -3,10 +3,12 @@
#include <filesystem> #include <filesystem>
#include <optional> #include <optional>
#include <string> #include <string>
#include <thread>
#include <variant> #include <variant>
#include <vector> #include <vector>
#include <SQLiteCpp/Database.h> #include <SQLiteCpp/Database.h>
#include <inotify-cpp/NotifierBuilder.h>
namespace Waylight { namespace Waylight {
@@ -46,6 +48,7 @@ struct ApplicationCache {
struct Cache { struct Cache {
explicit Cache(std::shared_ptr<SQLite::Database> db); explicit Cache(std::shared_ptr<SQLite::Database> db);
~Cache();
void rescan(); void rescan();
void dump(); void dump();
@@ -55,6 +58,9 @@ private:
std::vector<ApplicationCache> m_apps; std::vector<ApplicationCache> m_apps;
std::vector<std::filesystem::path> m_app_dirs; std::vector<std::filesystem::path> m_app_dirs;
std::shared_ptr<SQLite::Database> m_db; std::shared_ptr<SQLite::Database> m_db;
inotify::NotifierBuilder m_inot;
std::thread m_inot_th;
}; };
} // namespace Cache } // namespace Cache