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_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
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
harfbuzz
|
harfbuzz
|
||||||
sqlite
|
sqlite
|
||||||
zenity
|
zenity
|
||||||
|
boost
|
||||||
];
|
];
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
cmake
|
cmake
|
||||||
|
|||||||
10
src/App.cpp
10
src/App.cpp
@@ -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()
|
||||||
|
|||||||
222
src/Cache.cpp
222
src/Cache.cpp
@@ -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 §ion) {
|
mINI::INIMap<std::string> const
|
||||||
return ApplicationCache::Action {
|
§ion) {
|
||||||
.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));
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user