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

View File

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

View File

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

View File

@@ -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 &section) {
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
&section) {
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));

View File

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