feat: support global shortcuts via GlobalShortcutsPortal feature with ozone/wayland (#45171)
* fix: backport patch to fix systemd unit activation in Chromium This backports a patch from Chromium, which fixes systemd unit activation. That is, a globalShortcuts feature that Chromium has needs to create a systemd unit and rename it properly. Portal's global shortcuts uses that name afterwards to map the app with the shortcuts bound. However, there might be a race between Chromium binding shortcuts and renaming the unit. This is a first step to add Portal's globalShortcuts to Electron. * feat: Support global shortcuts via GlobalShortcutsPortal feature Chromium has a new feature called GlobalShortcutsPortal. It allows clients to use Portal's globalShortcuts to register and listen to shortcuts. This patches adds necessary bits, which allows Electron to use that feature. In order to make it work, one has to add --enable-features=GlobalShortcutsPortal Test: tested manually with a sample app. * docs: add GlobalShortcutsPortal feature to globalShortcuts docs Electron supports Portal's globalShortcuts API now via Chromium, and Electron apps can use that in a Wayland session. Update the docs with the required feature flag that must be passed to be able to use that implementation.
This commit is contained in:
parent
cf67dc8898
commit
3ea623364b
7 changed files with 591 additions and 9 deletions
|
@ -4,9 +4,15 @@
|
|||
|
||||
#include "shell/browser/api/electron_api_global_shortcut.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/uuid.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "electron/shell/browser/electron_browser_context.h"
|
||||
#include "electron/shell/common/electron_constants.h"
|
||||
#include "extensions/common/command.h"
|
||||
#include "gin/dictionary.h"
|
||||
#include "gin/handle.h"
|
||||
|
@ -62,7 +68,12 @@ void GlobalShortcut::OnKeyPressed(const ui::Accelerator& accelerator) {
|
|||
|
||||
void GlobalShortcut::ExecuteCommand(const extensions::ExtensionId& extension_id,
|
||||
const std::string& command_id) {
|
||||
// Ignore extension commands
|
||||
if (!base::Contains(command_callback_map_, command_id)) {
|
||||
// This should never occur, because if it does, GlobalShortcutListener
|
||||
// notifies us with wrong command.
|
||||
NOTREACHED();
|
||||
}
|
||||
command_callback_map_[command_id].Run();
|
||||
}
|
||||
|
||||
bool GlobalShortcut::RegisterAll(
|
||||
|
@ -103,13 +114,56 @@ bool GlobalShortcut::Register(const ui::Accelerator& accelerator,
|
|||
}
|
||||
#endif
|
||||
|
||||
if (!GlobalShortcutListener::GetInstance()->RegisterAccelerator(accelerator,
|
||||
this)) {
|
||||
auto* instance = GlobalShortcutListener::GetInstance();
|
||||
if (!instance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
accelerator_callback_map_[accelerator] = callback;
|
||||
return true;
|
||||
if (instance->IsRegistrationHandledExternally()) {
|
||||
auto* context = ElectronBrowserContext::From("", false);
|
||||
PrefService* prefs = context->prefs();
|
||||
|
||||
// Need a unique profile id. Set one if not generated yet, otherwise re-use
|
||||
// the same so that the session for the globalShortcuts is able to get
|
||||
// already registered shortcuts from the previous session. This will be used
|
||||
// by GlobalShortcutListenerLinux as a session key.
|
||||
std::string profile_id = prefs->GetString(kElectronGlobalShortcutsUuid);
|
||||
if (profile_id.empty()) {
|
||||
profile_id = base::Uuid::GenerateRandomV4().AsLowercaseString();
|
||||
prefs->SetString(kElectronGlobalShortcutsUuid, profile_id);
|
||||
}
|
||||
|
||||
// There is no way to get command id for the accelerator as it's extensions'
|
||||
// thing. Instead, we can convert it to string in a following example form
|
||||
// - std::string("Alt+Shift+K"). That must be sufficient enough for us to
|
||||
// map this accelerator with registered commands.
|
||||
const std::string command_str =
|
||||
extensions::Command::AcceleratorToString(accelerator);
|
||||
ui::CommandMap commands;
|
||||
extensions::Command command(
|
||||
command_str, base::UTF8ToUTF16("Electron shortcut " + command_str),
|
||||
/*accelerator=*/std::string(), /*global=*/true);
|
||||
command.set_accelerator(accelerator);
|
||||
commands[command_str] = command;
|
||||
|
||||
// In order to distinguish the shortcuts, we must register multiple commands
|
||||
// as different extensions. Otherwise, each shortcut will be an alternative
|
||||
// for the very first registered and we'll not be able to distinguish them.
|
||||
// For example, if Alt+Shift+K is registered first, registering and pressing
|
||||
// Alt+Shift+M will trigger global shortcuts, but the command id that is
|
||||
// received by GlobalShortcut will correspond to Alt+Shift+K as our command
|
||||
// id is basically a stringified accelerator.
|
||||
const std::string fake_extension_id = command_str + "+" + profile_id;
|
||||
instance->OnCommandsChanged(fake_extension_id, profile_id, commands, this);
|
||||
command_callback_map_[command_str] = callback;
|
||||
return true;
|
||||
} else {
|
||||
if (instance->RegisterAccelerator(accelerator, this)) {
|
||||
accelerator_callback_map_[accelerator] = callback;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GlobalShortcut::Unregister(const ui::Accelerator& accelerator) {
|
||||
|
@ -127,8 +181,10 @@ void GlobalShortcut::Unregister(const ui::Accelerator& accelerator) {
|
|||
}
|
||||
#endif
|
||||
|
||||
GlobalShortcutListener::GetInstance()->UnregisterAccelerator(accelerator,
|
||||
this);
|
||||
if (GlobalShortcutListener::GetInstance()) {
|
||||
GlobalShortcutListener::GetInstance()->UnregisterAccelerator(accelerator,
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalShortcut::UnregisterSome(
|
||||
|
@ -139,7 +195,12 @@ void GlobalShortcut::UnregisterSome(
|
|||
}
|
||||
|
||||
bool GlobalShortcut::IsRegistered(const ui::Accelerator& accelerator) {
|
||||
return base::Contains(accelerator_callback_map_, accelerator);
|
||||
if (base::Contains(accelerator_callback_map_, accelerator)) {
|
||||
return true;
|
||||
}
|
||||
const std::string command_str =
|
||||
extensions::Command::AcceleratorToString(accelerator);
|
||||
return base::Contains(command_callback_map_, command_str);
|
||||
}
|
||||
|
||||
void GlobalShortcut::UnregisterAll() {
|
||||
|
@ -149,7 +210,9 @@ void GlobalShortcut::UnregisterAll() {
|
|||
return;
|
||||
}
|
||||
accelerator_callback_map_.clear();
|
||||
GlobalShortcutListener::GetInstance()->UnregisterAccelerators(this);
|
||||
if (GlobalShortcutListener::GetInstance()) {
|
||||
GlobalShortcutListener::GetInstance()->UnregisterAccelerators(this);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue