diff options
-rw-r--r-- | Base/res/apps/Browser.af | 4 | ||||
-rw-r--r-- | Base/res/apps/TextEditor.af | 3 | ||||
-rw-r--r-- | Libraries/LibDesktop/Launcher.cpp | 6 | ||||
-rw-r--r-- | Libraries/LibDesktop/Launcher.h | 2 | ||||
-rw-r--r-- | Services/LaunchServer/ClientConnection.cpp | 2 | ||||
-rw-r--r-- | Services/LaunchServer/LaunchServer.ipc | 2 | ||||
-rw-r--r-- | Services/LaunchServer/Launcher.cpp | 92 | ||||
-rw-r--r-- | Services/LaunchServer/Launcher.h | 16 | ||||
-rw-r--r-- | Services/LaunchServer/main.cpp | 1 |
9 files changed, 111 insertions, 17 deletions
diff --git a/Base/res/apps/Browser.af b/Base/res/apps/Browser.af index acd6b59817..895d570f00 100644 --- a/Base/res/apps/Browser.af +++ b/Base/res/apps/Browser.af @@ -6,3 +6,7 @@ Category=Internet [Icons] 16x16=/res/icons/16x16/app-browser.png 32x32=/res/icons/32x32/app-browser.png + +[Launcher] +FileTypes=html,md +Protocols=http,https diff --git a/Base/res/apps/TextEditor.af b/Base/res/apps/TextEditor.af index e66c69cc5c..4916d03e26 100644 --- a/Base/res/apps/TextEditor.af +++ b/Base/res/apps/TextEditor.af @@ -6,3 +6,6 @@ Category=Utilities [Icons] 16x16=/res/icons/TextEditor16.png 32x32=/res/icons/32x32/app-texteditor.png + +[Launcher] +FileTypes=txt,md,html diff --git a/Libraries/LibDesktop/Launcher.cpp b/Libraries/LibDesktop/Launcher.cpp index 43fd5a7c3b..0d35c0c618 100644 --- a/Libraries/LibDesktop/Launcher.cpp +++ b/Libraries/LibDesktop/Launcher.cpp @@ -48,13 +48,13 @@ private: : IPC::ServerConnection<LaunchClientEndpoint, LaunchServerEndpoint>(*this, "/tmp/portal/launch") { } - virtual void handle(const Messages::LaunchClient::Dummy&) override { } + virtual void handle(const Messages::LaunchClient::Dummy&) override {} }; -bool Launcher::open(const URL& url) +bool Launcher::open(const URL& url, const String& handler_name) { auto connection = LaunchServerConnection::construct(); - return connection->send_sync<Messages::LaunchServer::OpenUrl>(url.to_string())->response(); + return connection->send_sync<Messages::LaunchServer::OpenUrl>(url.to_string(), handler_name)->response(); } Vector<String> Launcher::get_handlers_for_url(const URL& url) diff --git a/Libraries/LibDesktop/Launcher.h b/Libraries/LibDesktop/Launcher.h index 7acf2cc5b8..b8c792a01b 100644 --- a/Libraries/LibDesktop/Launcher.h +++ b/Libraries/LibDesktop/Launcher.h @@ -32,7 +32,7 @@ namespace Desktop { class Launcher { public: - static bool open(const URL&); + static bool open(const URL&, const String& handler_name = {}); static Vector<String> get_handlers_for_url(const URL&); }; diff --git a/Services/LaunchServer/ClientConnection.cpp b/Services/LaunchServer/ClientConnection.cpp index 35bc944b09..e69d6fce86 100644 --- a/Services/LaunchServer/ClientConnection.cpp +++ b/Services/LaunchServer/ClientConnection.cpp @@ -56,7 +56,7 @@ OwnPtr<Messages::LaunchServer::GreetResponse> ClientConnection::handle(const Mes OwnPtr<Messages::LaunchServer::OpenUrlResponse> ClientConnection::handle(const Messages::LaunchServer::OpenUrl& request) { URL url(request.url()); - auto result = Launcher::the().open_url(url); + auto result = Launcher::the().open_url(url, request.handler_name()); return make<Messages::LaunchServer::OpenUrlResponse>(result); } diff --git a/Services/LaunchServer/LaunchServer.ipc b/Services/LaunchServer/LaunchServer.ipc index f2bb3bbd1d..a4d31a00ed 100644 --- a/Services/LaunchServer/LaunchServer.ipc +++ b/Services/LaunchServer/LaunchServer.ipc @@ -1,6 +1,6 @@ endpoint LaunchServer = 101 { Greet() => (i32 client_id) - OpenUrl(String url) => (bool response) + OpenUrl(String url, String handler_name) => (bool response) GetHandlersForURL(String url) => (Vector<String> handlers) } diff --git a/Services/LaunchServer/Launcher.cpp b/Services/LaunchServer/Launcher.cpp index bda5ad70a0..62145eb17f 100644 --- a/Services/LaunchServer/Launcher.cpp +++ b/Services/LaunchServer/Launcher.cpp @@ -26,7 +26,9 @@ #include "Launcher.h" #include <AK/FileSystemPath.h> +#include <AK/Function.h> #include <LibCore/ConfigFile.h> +#include <LibCore/DirIterator.h> #include <stdio.h> #include <sys/stat.h> @@ -47,6 +49,33 @@ Launcher& Launcher::the() return *s_the; } +void Launcher::load_handlers(const String& af_dir) +{ + auto load_hashtable = [](auto& af, auto& key) { + HashTable<String> table; + + auto config_value = af->read_entry("Launcher", key, {}); + for (auto& key : config_value.split(',')) + table.set(key.to_lowercase()); + + return table; + }; + + Core::DirIterator dt(af_dir, Core::DirIterator::SkipDots); + while (dt.has_next()) { + auto af_name = dt.next_path(); + auto af_path = String::format("%s/%s", af_dir.characters(), af_name.characters()); + auto af = Core::ConfigFile::open(af_path); + if (!af->has_key("App", "Name") || !af->has_key("App", "Executable")) + continue; + auto app_name = af->read_entry("App", "Name"); + auto app_executable = af->read_entry("App", "Executable"); + auto file_types = load_hashtable(af, "FileTypes"); + auto protocols = load_hashtable(af, "Protocols"); + m_handlers.set(app_executable, { app_name, app_executable, file_types, protocols }); + } +} + void Launcher::load_config(const Core::ConfigFile& cfg) { for (auto key : cfg.keys("FileType")) { @@ -63,15 +92,35 @@ Vector<String> Launcher::handlers_for_url(const URL& url) if (url.protocol() == "file") return handlers_for_path(url.path()); - return { m_protocol_handlers.get(url.protocol()).value_or(m_protocol_handlers.get("*").value_or({})) }; + return handlers_for(url.protocol(), m_protocol_handlers, [](auto& handler, auto& key) { + return handler.protocols.contains(key); + }); } -bool Launcher::open_url(const URL& url) +bool Launcher::open_url(const URL& url, const String& handler_name) { + if (!handler_name.is_null()) + return open_with_handler_name(url, handler_name); + if (url.protocol() == "file") return open_file_url(url); - return open_with_handlers(m_protocol_handlers, url.protocol(), url.to_string(), "/bin/Browser"); + return open_with_user_preferences(m_protocol_handlers, url.protocol(), url.to_string(), "/bin/Browser"); +} + +bool Launcher::open_with_handler_name(const URL& url, const String& handler_name) +{ + auto handler_optional = m_handlers.get(handler_name); + if (!handler_optional.has_value()) + return false; + + auto& handler = handler_optional.value(); + String argument; + if (url.protocol() == "file") + argument = url.path(); + else + argument = url.to_string(); + return spawn(handler.executable, argument); } bool spawn(String executable, String argument) @@ -91,14 +140,14 @@ bool spawn(String executable, String argument) return true; } -bool Launcher::open_with_handlers(const HashMap<String, String>& handlers, 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 = handlers.get(key); + auto program_path = user_preferences.get(key); if (program_path.has_value()) return spawn(program_path.value(), argument); // There wasn't a handler for this, so try the fallback instead - program_path = handlers.get("*"); + program_path = user_preferences.get("*"); if (program_path.has_value()) return spawn(program_path.value(), argument); @@ -107,6 +156,29 @@ bool Launcher::open_with_handlers(const HashMap<String, String>& handlers, const 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) +{ + Vector<String> handlers; + + auto user_preferred = user_preference.get(key); + if (user_preferred.has_value()) + handlers.append(user_preferred.value()); + + for (auto& handler : m_handlers) { + // Skip over the existing item in the list + if (user_preferred.has_value() && user_preferred.value() == handler.value.executable) + continue; + if (handler_matches(handler.value, key)) + handlers.append(handler.value.executable); + } + + auto user_default = user_preference.get("*"); + if (handlers.size() == 0 && user_default.has_value()) + handlers.append(user_default.value()); + + return handlers; +} + Vector<String> Launcher::handlers_for_path(const String& path) { struct stat st; @@ -120,7 +192,10 @@ Vector<String> Launcher::handlers_for_path(const String& path) return { "/bin/FileManager" }; auto extension = FileSystemPath(path).extension().to_lowercase(); - return { m_file_handlers.get(extension).value_or(m_file_handlers.get("*").value_or({})) }; + + return handlers_for(extension, m_file_handlers, [](auto& handler, auto& key) { + return handler.file_types.contains(key); + }); } bool Launcher::open_file_url(const URL& url) @@ -142,7 +217,6 @@ bool Launcher::open_file_url(const URL& url) String extension = {}; if (extension_parts.size() > 1) extension = extension_parts.last(); - return open_with_handlers(m_file_handlers, extension, url.path(), "/bin/TextEdit"); + return open_with_user_preferences(m_file_handlers, extension, url.path(), "/bin/TextEdit"); } - } diff --git a/Services/LaunchServer/Launcher.h b/Services/LaunchServer/Launcher.h index 0559c3c04b..ff241c6c7f 100644 --- a/Services/LaunchServer/Launcher.h +++ b/Services/LaunchServer/Launcher.h @@ -27,26 +27,38 @@ #pragma once #include <AK/HashMap.h> +#include <AK/HashTable.h> #include <AK/URL.h> #include <LibCore/ConfigFile.h> namespace LaunchServer { +struct Handler { + String name; + String executable; + HashTable<String> file_types; + HashTable<String> protocols; +}; + class Launcher { public: Launcher(); static Launcher& the(); + void load_handlers(const String& af_dir); void load_config(const Core::ConfigFile&); - bool open_url(const URL&); + bool open_url(const URL&, const String& handler_name); Vector<String> handlers_for_url(const URL&); private: + HashMap<String, Handler> m_handlers; HashMap<String, String> m_protocol_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); Vector<String> handlers_for_path(const String&); bool open_file_url(const URL&); - bool open_with_handlers(const HashMap<String, String>& handlers, 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); }; } diff --git a/Services/LaunchServer/main.cpp b/Services/LaunchServer/main.cpp index 61b2533d69..97c83fcbfb 100644 --- a/Services/LaunchServer/main.cpp +++ b/Services/LaunchServer/main.cpp @@ -42,6 +42,7 @@ int main(int argc, char** argv) auto launcher = LaunchServer::Launcher(); + launcher.load_handlers("/res/apps"); launcher.load_config(Core::ConfigFile::get_for_app("LaunchServer")); if (pledge("stdio accept rpath proc exec", nullptr) < 0) { |