mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 09:27:34 +00:00
Launcher: Provide launcher details to LibDesktop
This allows applications to get additional information, such as name or icons, for each launch handler.
This commit is contained in:
parent
7498024805
commit
535113bac4
7 changed files with 208 additions and 26 deletions
|
@ -24,6 +24,7 @@
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/JsonObject.h>
|
||||||
#include <AK/URL.h>
|
#include <AK/URL.h>
|
||||||
#include <LaunchServer/LaunchClientEndpoint.h>
|
#include <LaunchServer/LaunchClientEndpoint.h>
|
||||||
#include <LaunchServer/LaunchServerEndpoint.h>
|
#include <LaunchServer/LaunchServerEndpoint.h>
|
||||||
|
@ -33,6 +34,29 @@
|
||||||
|
|
||||||
namespace Desktop {
|
namespace Desktop {
|
||||||
|
|
||||||
|
auto Launcher::Details::from_details_str(const String& details_str) -> NonnullRefPtr<Details>
|
||||||
|
{
|
||||||
|
auto details = adopt(*new Details);
|
||||||
|
auto json = JsonValue::from_string(details_str);
|
||||||
|
ASSERT(json.has_value());
|
||||||
|
auto obj = json.value().as_object();
|
||||||
|
details->executable = obj.get("executable").to_string();
|
||||||
|
details->name = obj.get("name").to_string();
|
||||||
|
if (auto type_value = obj.get_ptr("type")) {
|
||||||
|
auto type_str = type_value->to_string();
|
||||||
|
if (type_str == "userpreferred")
|
||||||
|
details->launcher_type = LauncherType::UserPreferred;
|
||||||
|
else if (type_str == "userdefault")
|
||||||
|
details->launcher_type = LauncherType::UserDefault;
|
||||||
|
}
|
||||||
|
if (auto icons_value = obj.get_ptr("icons")) {
|
||||||
|
icons_value->as_object().for_each_member([&](auto& name, auto& value) {
|
||||||
|
details->icons.set(name, value.to_string());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
class LaunchServerConnection : public IPC::ServerConnection<LaunchClientEndpoint, LaunchServerEndpoint>
|
class LaunchServerConnection : public IPC::ServerConnection<LaunchClientEndpoint, LaunchServerEndpoint>
|
||||||
, public LaunchClientEndpoint {
|
, public LaunchClientEndpoint {
|
||||||
C_OBJECT(LaunchServerConnection)
|
C_OBJECT(LaunchServerConnection)
|
||||||
|
@ -57,10 +81,26 @@ bool Launcher::open(const URL& url, const String& handler_name)
|
||||||
return connection->send_sync<Messages::LaunchServer::OpenURL>(url, handler_name)->response();
|
return connection->send_sync<Messages::LaunchServer::OpenURL>(url, handler_name)->response();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Launcher::open(const URL& url, const Details& details)
|
||||||
|
{
|
||||||
|
return open(url, details.executable);
|
||||||
|
}
|
||||||
|
|
||||||
Vector<String> Launcher::get_handlers_for_url(const URL& url)
|
Vector<String> Launcher::get_handlers_for_url(const URL& url)
|
||||||
{
|
{
|
||||||
auto connection = LaunchServerConnection::construct();
|
auto connection = LaunchServerConnection::construct();
|
||||||
return connection->send_sync<Messages::LaunchServer::GetHandlersForURL>(url.to_string())->handlers();
|
return connection->send_sync<Messages::LaunchServer::GetHandlersForURL>(url.to_string())->handlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Launcher::get_handlers_with_details_for_url(const URL& url) -> NonnullRefPtrVector<Details>
|
||||||
|
{
|
||||||
|
auto connection = LaunchServerConnection::construct();
|
||||||
|
auto details = connection->send_sync<Messages::LaunchServer::GetHandlersWithDetailsForURL>(url.to_string())->handlers_details();
|
||||||
|
NonnullRefPtrVector<Details> handlers_with_details;
|
||||||
|
for (auto& value : details) {
|
||||||
|
handlers_with_details.append(Details::from_details_str(value));
|
||||||
|
}
|
||||||
|
return handlers_with_details;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,13 +27,35 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/Forward.h>
|
#include <AK/Forward.h>
|
||||||
|
#include <AK/HashMap.h>
|
||||||
|
#include <AK/NonnullRefPtrVector.h>
|
||||||
|
#include <AK/RefCounted.h>
|
||||||
|
#include <AK/String.h>
|
||||||
|
|
||||||
namespace Desktop {
|
namespace Desktop {
|
||||||
|
|
||||||
class Launcher {
|
class Launcher {
|
||||||
public:
|
public:
|
||||||
|
enum class LauncherType {
|
||||||
|
Default = 0,
|
||||||
|
UserPreferred,
|
||||||
|
UserDefault
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Details: public RefCounted<Details> {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
String executable;
|
||||||
|
HashMap<String, String> icons;
|
||||||
|
LauncherType launcher_type { LauncherType::Default };
|
||||||
|
|
||||||
|
static NonnullRefPtr<Details> from_details_str(const String&);
|
||||||
|
};
|
||||||
|
|
||||||
static bool open(const URL&, const String& handler_name = {});
|
static bool open(const URL&, const String& handler_name = {});
|
||||||
|
static bool open(const URL&, const Details& details);
|
||||||
static Vector<String> get_handlers_for_url(const URL&);
|
static Vector<String> get_handlers_for_url(const URL&);
|
||||||
|
static NonnullRefPtrVector<Details> get_handlers_with_details_for_url(const URL&);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,4 +67,11 @@ OwnPtr<Messages::LaunchServer::GetHandlersForURLResponse> ClientConnection::hand
|
||||||
return make<Messages::LaunchServer::GetHandlersForURLResponse>(result);
|
return make<Messages::LaunchServer::GetHandlersForURLResponse>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OwnPtr<Messages::LaunchServer::GetHandlersWithDetailsForURLResponse> ClientConnection::handle(const Messages::LaunchServer::GetHandlersWithDetailsForURL& request)
|
||||||
|
{
|
||||||
|
URL url(request.url());
|
||||||
|
auto result = Launcher::the().handlers_with_details_for_url(url);
|
||||||
|
return make<Messages::LaunchServer::GetHandlersWithDetailsForURLResponse>(result);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,5 +45,6 @@ private:
|
||||||
virtual OwnPtr<Messages::LaunchServer::GreetResponse> handle(const Messages::LaunchServer::Greet&) override;
|
virtual OwnPtr<Messages::LaunchServer::GreetResponse> handle(const Messages::LaunchServer::Greet&) override;
|
||||||
virtual OwnPtr<Messages::LaunchServer::OpenURLResponse> handle(const Messages::LaunchServer::OpenURL&) override;
|
virtual OwnPtr<Messages::LaunchServer::OpenURLResponse> handle(const Messages::LaunchServer::OpenURL&) override;
|
||||||
virtual OwnPtr<Messages::LaunchServer::GetHandlersForURLResponse> handle(const Messages::LaunchServer::GetHandlersForURL&) override;
|
virtual OwnPtr<Messages::LaunchServer::GetHandlersForURLResponse> handle(const Messages::LaunchServer::GetHandlersForURL&) override;
|
||||||
|
virtual OwnPtr<Messages::LaunchServer::GetHandlersWithDetailsForURLResponse> handle(const Messages::LaunchServer::GetHandlersWithDetailsForURL&) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,5 @@ endpoint LaunchServer = 101
|
||||||
Greet() => (i32 client_id)
|
Greet() => (i32 client_id)
|
||||||
OpenURL(URL url, String handler_name) => (bool response)
|
OpenURL(URL url, String handler_name) => (bool response)
|
||||||
GetHandlersForURL(URL url) => (Vector<String> handlers)
|
GetHandlersForURL(URL url) => (Vector<String> handlers)
|
||||||
|
GetHandlersWithDetailsForURL(URL url) => (Vector<String> handlers_details)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,11 @@
|
||||||
|
|
||||||
#include "Launcher.h"
|
#include "Launcher.h"
|
||||||
#include <AK/Function.h>
|
#include <AK/Function.h>
|
||||||
|
#include <AK/JsonObject.h>
|
||||||
|
#include <AK/JsonObjectSerializer.h>
|
||||||
|
#include <AK/JsonValue.h>
|
||||||
#include <AK/LexicalPath.h>
|
#include <AK/LexicalPath.h>
|
||||||
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibCore/ConfigFile.h>
|
#include <LibCore/ConfigFile.h>
|
||||||
#include <LibCore/DirIterator.h>
|
#include <LibCore/DirIterator.h>
|
||||||
#include <spawn.h>
|
#include <spawn.h>
|
||||||
|
@ -38,6 +42,47 @@ namespace LaunchServer {
|
||||||
static Launcher* s_the;
|
static Launcher* s_the;
|
||||||
static bool spawn(String executable, String argument);
|
static bool spawn(String executable, String argument);
|
||||||
|
|
||||||
|
String Handler::name_from_executable(const StringView& executable)
|
||||||
|
{
|
||||||
|
auto separator = executable.find_last_of('/');
|
||||||
|
if (separator.has_value()) {
|
||||||
|
auto start = separator.value() + 1;
|
||||||
|
return executable.substring_view(start, executable.length() - start);
|
||||||
|
}
|
||||||
|
return executable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Handler::from_executable(Type handler_type, const String& executable)
|
||||||
|
{
|
||||||
|
this->handler_type = handler_type;
|
||||||
|
this->name = name_from_executable(executable);
|
||||||
|
this->executable = executable;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Handler::to_details_str() const
|
||||||
|
{
|
||||||
|
StringBuilder builder;
|
||||||
|
JsonObjectSerializer obj { builder };
|
||||||
|
obj.add("executable", executable);
|
||||||
|
obj.add("name", name);
|
||||||
|
switch (handler_type) {
|
||||||
|
case Type::UserDefault:
|
||||||
|
obj.add("type", "userdefault");
|
||||||
|
break;
|
||||||
|
case Type::UserPreferred:
|
||||||
|
obj.add("type", "userpreferred");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
JsonObject icons_obj;
|
||||||
|
for (auto& icon : icons)
|
||||||
|
icons_obj.set(icon.key, icon.value);
|
||||||
|
obj.add("icons", move(icons_obj));
|
||||||
|
obj.finish();
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
Launcher::Launcher()
|
Launcher::Launcher()
|
||||||
{
|
{
|
||||||
ASSERT(s_the == nullptr);
|
ASSERT(s_the == nullptr);
|
||||||
|
@ -61,6 +106,14 @@ void Launcher::load_handlers(const String& af_dir)
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
};
|
};
|
||||||
|
auto load_hashmap = [](auto& af, auto& group) {
|
||||||
|
HashMap<String, String> map;
|
||||||
|
auto keys = af->keys(group);
|
||||||
|
for (auto& key : keys)
|
||||||
|
map.set(key, af->read_entry(group, key));
|
||||||
|
|
||||||
|
return map;
|
||||||
|
};
|
||||||
|
|
||||||
Core::DirIterator dt(af_dir, Core::DirIterator::SkipDots);
|
Core::DirIterator dt(af_dir, Core::DirIterator::SkipDots);
|
||||||
while (dt.has_next()) {
|
while (dt.has_next()) {
|
||||||
|
@ -73,7 +126,8 @@ void Launcher::load_handlers(const String& af_dir)
|
||||||
auto app_executable = af->read_entry("App", "Executable");
|
auto app_executable = af->read_entry("App", "Executable");
|
||||||
auto file_types = load_hashtable(af, "FileTypes");
|
auto file_types = load_hashtable(af, "FileTypes");
|
||||||
auto protocols = load_hashtable(af, "Protocols");
|
auto protocols = load_hashtable(af, "Protocols");
|
||||||
m_handlers.set(app_executable, { app_name, app_executable, file_types, protocols });
|
auto icons = load_hashmap(af, "Icons");
|
||||||
|
m_handlers.set(app_executable, { Handler::Type::Default, move(app_name), app_executable, move(file_types), move(protocols), move(icons) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,12 +144,42 @@ void Launcher::load_config(const Core::ConfigFile& cfg)
|
||||||
|
|
||||||
Vector<String> Launcher::handlers_for_url(const URL& url)
|
Vector<String> Launcher::handlers_for_url(const URL& url)
|
||||||
{
|
{
|
||||||
if (url.protocol() == "file")
|
Vector<String> handlers;
|
||||||
return handlers_for_path(url.path());
|
if (url.protocol() == "file") {
|
||||||
|
for_each_handler_for_path(url.path(), [&](auto& handler) -> bool {
|
||||||
|
handlers.append(handler.executable);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for_each_handler(url.protocol(), m_protocol_handlers, [&](const auto& handler) -> bool {
|
||||||
|
if (handler.handler_type != Handler::Type::Default || handler.protocols.contains(url.protocol())) {
|
||||||
|
handlers.append(handler.executable);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
return handlers_for(url.protocol(), m_protocol_handlers, [](auto& handler, auto& key) {
|
Vector<String> Launcher::handlers_with_details_for_url(const URL& url)
|
||||||
return handler.protocols.contains(key);
|
{
|
||||||
});
|
Vector<String> handlers;
|
||||||
|
if (url.protocol() == "file") {
|
||||||
|
for_each_handler_for_path(url.path(), [&](auto& handler) -> bool {
|
||||||
|
handlers.append(handler.to_details_str());
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for_each_handler(url.protocol(), m_protocol_handlers, [&](const auto& handler) -> bool {
|
||||||
|
if (handler.handler_type != Handler::Type::Default || handler.protocols.contains(url.protocol())) {
|
||||||
|
handlers.append(handler.to_details_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Launcher::open_url(const URL& url, const String& handler_name)
|
bool Launcher::open_url(const URL& url, const String& handler_name)
|
||||||
|
@ -135,6 +219,19 @@ bool spawn(String executable, String argument)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handler Launcher::get_handler_for_executable(Handler::Type handler_type, const String& executable) const
|
||||||
|
{
|
||||||
|
Handler handler;
|
||||||
|
auto existing_handler = m_handlers.get(executable);
|
||||||
|
if (existing_handler.has_value()) {
|
||||||
|
handler = existing_handler.value();
|
||||||
|
handler.handler_type = handler_type;
|
||||||
|
} else {
|
||||||
|
handler.from_executable(handler_type, executable);
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
bool Launcher::open_with_user_preferences(const HashMap<String, String>& user_preferences, const String key, const String argument, const String default_program)
|
bool Launcher::open_with_user_preferences(const HashMap<String, String>& user_preferences, const String key, const String argument, const String default_program)
|
||||||
{
|
{
|
||||||
auto program_path = user_preferences.get(key);
|
auto program_path = user_preferences.get(key);
|
||||||
|
@ -151,45 +248,46 @@ bool Launcher::open_with_user_preferences(const HashMap<String, String>& user_pr
|
||||||
return spawn(default_program, argument);
|
return spawn(default_program, argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<String> Launcher::handlers_for(const String& key, HashMap<String, String>& user_preference, Function<bool(Handler&, const String&)> handler_matches)
|
void Launcher::for_each_handler(const String& key, HashMap<String, String>& user_preference, Function<bool(const Handler&)> f)
|
||||||
{
|
{
|
||||||
Vector<String> handlers;
|
|
||||||
|
|
||||||
auto user_preferred = user_preference.get(key);
|
auto user_preferred = user_preference.get(key);
|
||||||
if (user_preferred.has_value())
|
if (user_preferred.has_value())
|
||||||
handlers.append(user_preferred.value());
|
f(get_handler_for_executable(Handler::Type::UserPreferred, user_preferred.value()));
|
||||||
|
|
||||||
|
size_t counted = 0;
|
||||||
for (auto& handler : m_handlers) {
|
for (auto& handler : m_handlers) {
|
||||||
// Skip over the existing item in the list
|
// Skip over the existing item in the list
|
||||||
if (user_preferred.has_value() && user_preferred.value() == handler.value.executable)
|
if (user_preferred.has_value() && user_preferred.value() == handler.value.executable)
|
||||||
continue;
|
continue;
|
||||||
if (handler_matches(handler.value, key))
|
if (f(handler.value))
|
||||||
handlers.append(handler.value.executable);
|
counted++;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto user_default = user_preference.get("*");
|
auto user_default = user_preference.get("*");
|
||||||
if (handlers.size() == 0 && user_default.has_value())
|
if (counted == 0 && user_default.has_value())
|
||||||
handlers.append(user_default.value());
|
f(get_handler_for_executable(Handler::Type::UserDefault, user_default.value()));
|
||||||
|
|
||||||
return handlers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<String> Launcher::handlers_for_path(const String& path)
|
void Launcher::for_each_handler_for_path(const String& path, Function<bool(const Handler&)> f)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path.characters(), &st) < 0) {
|
if (stat(path.characters(), &st) < 0) {
|
||||||
perror("stat");
|
perror("stat");
|
||||||
return {};
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make directory opening configurable
|
// TODO: Make directory opening configurable
|
||||||
if (S_ISDIR(st.st_mode))
|
if (S_ISDIR(st.st_mode)) {
|
||||||
return { "/bin/FileManager" };
|
f(get_handler_for_executable(Handler::Type::Default, "/bin/FileManager"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto extension = LexicalPath(path).extension().to_lowercase();
|
auto extension = LexicalPath(path).extension().to_lowercase();
|
||||||
|
|
||||||
return handlers_for(extension, m_file_handlers, [](auto& handler, auto& key) {
|
for_each_handler(extension, m_file_handlers, [&](const auto& handler) -> bool {
|
||||||
return handler.file_types.contains(key);
|
if (handler.handler_type != Handler::Type::Default || handler.file_types.contains(extension))
|
||||||
|
return f(handler);
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,10 +34,21 @@
|
||||||
namespace LaunchServer {
|
namespace LaunchServer {
|
||||||
|
|
||||||
struct Handler {
|
struct Handler {
|
||||||
|
enum class Type {
|
||||||
|
Default = 0,
|
||||||
|
UserPreferred,
|
||||||
|
UserDefault
|
||||||
|
};
|
||||||
|
Type handler_type;
|
||||||
String name;
|
String name;
|
||||||
String executable;
|
String executable;
|
||||||
HashTable<String> file_types;
|
HashTable<String> file_types {};
|
||||||
HashTable<String> protocols;
|
HashTable<String> protocols {};
|
||||||
|
HashMap<String, String> icons {};
|
||||||
|
|
||||||
|
static String name_from_executable(const StringView&);
|
||||||
|
void from_executable(Type, const String&);
|
||||||
|
String to_details_str() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Launcher {
|
class Launcher {
|
||||||
|
@ -49,14 +60,16 @@ public:
|
||||||
void load_config(const Core::ConfigFile&);
|
void load_config(const Core::ConfigFile&);
|
||||||
bool open_url(const URL&, const String& handler_name);
|
bool open_url(const URL&, const String& handler_name);
|
||||||
Vector<String> handlers_for_url(const URL&);
|
Vector<String> handlers_for_url(const URL&);
|
||||||
|
Vector<String> handlers_with_details_for_url(const URL&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HashMap<String, Handler> m_handlers;
|
HashMap<String, Handler> m_handlers;
|
||||||
HashMap<String, String> m_protocol_handlers;
|
HashMap<String, String> m_protocol_handlers;
|
||||||
HashMap<String, String> m_file_handlers;
|
HashMap<String, String> m_file_handlers;
|
||||||
|
|
||||||
Vector<String> handlers_for(const String& key, HashMap<String, String>& user_preferences, Function<bool(Handler&, const String&)> handler_matches);
|
Handler get_handler_for_executable(Handler::Type, const String&) const;
|
||||||
Vector<String> handlers_for_path(const String&);
|
void for_each_handler(const String& key, HashMap<String, String>& user_preferences, Function<bool(const Handler&)> f);
|
||||||
|
void for_each_handler_for_path(const String&, Function<bool(const Handler&)> f);
|
||||||
bool open_file_url(const URL&);
|
bool open_file_url(const URL&);
|
||||||
bool open_with_user_preferences(const HashMap<String, String>& user_preferences, const String key, const String argument, const String default_program);
|
bool open_with_user_preferences(const HashMap<String, String>& user_preferences, const String key, const String argument, const String default_program);
|
||||||
bool open_with_handler_name(const URL&, const String& handler_name);
|
bool open_with_handler_name(const URL&, const String& handler_name);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue