feat: support global shortcuts via GlobalShortcutsPortal
feature with ozone/wayland (#45297)
This commit is contained in:
parent
4417f74a5b
commit
18007f843e
7 changed files with 591 additions and 9 deletions
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue