feat: support global shortcuts via GlobalShortcutsPortal feature with ozone/wayland (#45297)

This commit is contained in:
trop[bot] 2025-02-27 11:33:12 +01:00 committed by GitHub
parent 4417f74a5b
commit 18007f843e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 591 additions and 9 deletions

View file

@ -4,8 +4,14 @@
#include "shell/browser/api/electron_api_global_shortcut.h"
#include <string>
#include <vector>
#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"
@ -61,7 +67,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 (!command_callback_map_.contains(command_id)) {
// This should never occur, because if it does, GlobalAcceleratorListener
// notifies us with wrong command.
NOTREACHED();
}
command_callback_map_[command_id].Run();
}
bool GlobalShortcut::RegisterAll(
@ -102,13 +113,56 @@ bool GlobalShortcut::Register(const ui::Accelerator& accelerator,
}
#endif
if (!ui::GlobalAcceleratorListener::GetInstance()->RegisterAccelerator(
accelerator, this)) {
auto* instance = ui::GlobalAcceleratorListener::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) {
@ -126,8 +180,10 @@ void GlobalShortcut::Unregister(const ui::Accelerator& accelerator) {
}
#endif
ui::GlobalAcceleratorListener::GetInstance()->UnregisterAccelerator(
accelerator, this);
if (ui::GlobalAcceleratorListener::GetInstance()) {
ui::GlobalAcceleratorListener::GetInstance()->UnregisterAccelerator(
accelerator, this);
}
}
void GlobalShortcut::UnregisterSome(
@ -138,7 +194,12 @@ void GlobalShortcut::UnregisterSome(
}
bool GlobalShortcut::IsRegistered(const ui::Accelerator& accelerator) {
return accelerator_callback_map_.contains(accelerator);
if (accelerator_callback_map_.contains(accelerator)) {
return true;
}
const std::string command_str =
extensions::Command::AcceleratorToString(accelerator);
return command_callback_map_.contains(command_str);
}
void GlobalShortcut::UnregisterAll() {
@ -148,7 +209,9 @@ void GlobalShortcut::UnregisterAll() {
return;
}
accelerator_callback_map_.clear();
ui::GlobalAcceleratorListener::GetInstance()->UnregisterAccelerators(this);
if (ui::GlobalAcceleratorListener::GetInstance()) {
ui::GlobalAcceleratorListener::GetInstance()->UnregisterAccelerators(this);
}
}
// static