diff --git a/obs/data/locale/en-US.ini b/obs/data/locale/en-US.ini index b8bec046a742f30979e19c1ce878fca8d206355e..b0d6ef557e23278c29e94735ac33a002a7a20d62 100644 --- a/obs/data/locale/en-US.ini +++ b/obs/data/locale/en-US.ini @@ -350,3 +350,49 @@ Basic.AdvAudio.Mono="Downmix to Mono" Basic.AdvAudio.Panning="Panning" Basic.AdvAudio.SyncOffset="Sync Offset (ms)" Basic.AdvAudio.AudioTracks="Tracks" + +# hotkeys that may lack translation on certain operating systems +Hotkeys.Insert="Insert" +Hotkeys.Delete="Delete" +Hotkeys.Home="Home" +Hotkeys.End="End" +Hotkeys.PageUp="Page Up" +Hotkeys.PageDown="Page Down" +Hotkeys.NumLock="Num Lock" +Hotkeys.ScrollLock="Scroll Lock" +Hotkeys.CapsLock="Caps Lock" +Hotkeys.Backspace="Backspace" +Hotkeys.Tab="Tab" +Hotkeys.Print="Print" +Hotkeys.Pause="Pause" +Hotkeys.Left="Left" +Hotkeys.Right="Right" +Hotkeys.Up="Up" +Hotkeys.Down="Down" +Hotkeys.Windows="Windows" +Hotkeys.Super="Super" +Hotkeys.Menu="Menu" +Hotkeys.Space="Space" +Hotkeys.NumpadNum="Numpad %1" +Hotkeys.NumpadMultiply="Numpad Multiply" +Hotkeys.NumpadDivide="Numpad Divide" +Hotkeys.NumpadAdd="Numpad Add" +Hotkeys.NumpadSubtract="Numpad Subtract" +Hotkeys.NumpadDecimal="Numpad Decimal" +Hotkeys.AppleKeypadNum="%1 (Keypad)" +Hotkeys.AppleKeypadMultiply="* (Keypad)" +Hotkeys.AppleKeypadDivide="/ (Keypad)" +Hotkeys.AppleKeypadAdd="+ (Keypad)" +Hotkeys.AppleKeypadSubtract="- (Keypad)" +Hotkeys.AppleKeypadDecimal=". (Keypad)" +Hotkeys.AppleKeypadEqual="= (Keypad)" +Hotkeys.MouseButton="Mouse %1" + +# audio hotkeys +Mute="Mute" +Unmute="Unmute" +Push-to-talk="Push-to-talk" + +# scene item hotkeys +SceneItemShow="Show '%1'" +SceneItemHide="Hide '%1'" diff --git a/obs/obs-app.cpp b/obs/obs-app.cpp index a0190556f4b1c7a2645d14e17ed64c4be02451de..f67db32e0e160bed19641d854fd94dd94a828267 100644 --- a/obs/obs-app.cpp +++ b/obs/obs-app.cpp @@ -52,6 +52,112 @@ static log_handler_t def_log_handler; static string currentLogFile; static string lastLogFile; +QObject *CreateShortcutFilter() +{ + return new OBSEventFilter([](QObject *, QEvent *event) + { + auto mouse_event = [](QMouseEvent &event) + { + obs_key_combination_t hotkey = {0, OBS_KEY_NONE}; + bool pressed = event.type() == QEvent::MouseButtonPress; + + switch (event.button()) { + case Qt::NoButton: + case Qt::LeftButton: + case Qt::RightButton: + case Qt::AllButtons: + case Qt::MouseButtonMask: + return false; + + case Qt::MidButton: + hotkey.key = OBS_KEY_MOUSE3; + break; + +#define MAP_BUTTON(i, j) case Qt::ExtraButton ## i: \ + hotkey.key = OBS_KEY_MOUSE ## j; break; + MAP_BUTTON( 1, 4); + MAP_BUTTON( 2, 5); + MAP_BUTTON( 3, 6); + MAP_BUTTON( 4, 7); + MAP_BUTTON( 5, 8); + MAP_BUTTON( 6, 9); + MAP_BUTTON( 7, 10); + MAP_BUTTON( 8, 11); + MAP_BUTTON( 9, 12); + MAP_BUTTON(10, 13); + MAP_BUTTON(11, 14); + MAP_BUTTON(12, 15); + MAP_BUTTON(13, 16); + MAP_BUTTON(14, 17); + MAP_BUTTON(15, 18); + MAP_BUTTON(16, 19); + MAP_BUTTON(17, 20); + MAP_BUTTON(18, 21); + MAP_BUTTON(19, 22); + MAP_BUTTON(20, 23); + MAP_BUTTON(21, 24); + MAP_BUTTON(22, 25); + MAP_BUTTON(23, 26); + MAP_BUTTON(24, 27); +#undef MAP_BUTTON + } + + hotkey.modifiers = TranslateQtKeyboardEventModifiers( + event.modifiers()); + + obs_hotkey_inject_event(hotkey, pressed); + return true; + }; + + auto key_event = [](QKeyEvent *event) + { + obs_key_combination_t hotkey = {0, OBS_KEY_NONE}; + bool pressed = event->type() == QEvent::KeyPress; + + switch (event->key()) { + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Alt: + case Qt::Key_Meta: + break; + +#ifdef __APPLE__ + case Qt::Key_CapsLock: + // kVK_CapsLock == 57 + hotkey.key = obs_key_from_virtual_key(57); + pressed = true; + break; +#endif + + default: + hotkey.key = obs_key_from_virtual_key( + event->nativeVirtualKey()); + } + + hotkey.modifiers = TranslateQtKeyboardEventModifiers( + event->modifiers()); + + obs_hotkey_inject_event(hotkey, pressed); + }; + + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + return mouse_event(*static_cast(event)); + + /*case QEvent::MouseButtonDblClick: + case QEvent::Wheel:*/ + case QEvent::KeyPress: + case QEvent::KeyRelease: + key_event(static_cast(event)); + return true; + + default: + return false; + } + }); +} + string CurrentTimeString() { time_t now = time(0); @@ -330,6 +436,14 @@ bool OBSApp::OBSInit() mainWindow->OBSInit(); + connect(this, &QGuiApplication::applicationStateChanged, + [](Qt::ApplicationState state) + { + obs_hotkey_enable_background_press( + state != Qt::ApplicationActive); + }); + obs_hotkey_enable_background_press( + applicationState() != Qt::ApplicationActive); return true; } else { return false; diff --git a/obs/obs-app.hpp b/obs/obs-app.hpp index 79853f594c6d1c56f531f943c71fd345f20a834e..390f9dbb0b27c2a20b01f541af870a7fbf432274 100644 --- a/obs/obs-app.hpp +++ b/obs/obs-app.hpp @@ -31,6 +31,7 @@ std::string CurrentTimeString(); std::string CurrentDateTimeString(); std::string GenerateTimeDateFilename(const char *extension); +QObject *CreateShortcutFilter(); struct BaseLexer { lexer lex; diff --git a/obs/window-basic-adv-audio.cpp b/obs/window-basic-adv-audio.cpp index 9d2dff45f2412300031a5306b087a137b0846097..d0baa24050a0e8886e4e745b1fbe72316680284e 100644 --- a/obs/window-basic-adv-audio.cpp +++ b/obs/window-basic-adv-audio.cpp @@ -63,6 +63,8 @@ OBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent) vlayout->addWidget(scrollArea); setLayout(vlayout); + installEventFilter(CreateShortcutFilter()); + /* get global audio sources */ for (uint32_t i = 1; i <= 10; i++) { obs_source_t *source = obs_get_output_source(i); diff --git a/obs/window-basic-filters.cpp b/obs/window-basic-filters.cpp index c1b20d17b66aef56e722365f7d0046283784c8ab..2a865d99756be2641db8a84bba007df69a22817a 100644 --- a/obs/window-basic-filters.cpp +++ b/obs/window-basic-filters.cpp @@ -66,6 +66,8 @@ OBSBasicFilters::OBSBasicFilters(QWidget *parent, OBSSource source_) const char *name = obs_source_get_name(source); setWindowTitle(QTStr("Basic.Filters.Title").arg(QT_UTF8(name))); + installEventFilter(CreateShortcutFilter()); + connect(ui->preview, SIGNAL(DisplayResized()), this, SLOT(OnPreviewResized())); diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 79ea469f28374341cfbb16bfc71c8d7ab4d2c2ca..57632840132628462ff3b5f28e85b59c993c85a5 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -118,6 +118,7 @@ OBSBasic::OBSBasic(QWidget *parent) qRegisterMetaType ("OBSScene"); qRegisterMetaType("OBSSceneItem"); qRegisterMetaType ("OBSSource"); + qRegisterMetaType("obs_hotkey_id"); ui->scenes->setAttribute(Qt::WA_MacShowFocusRect, false); ui->sources->setAttribute(Qt::WA_MacShowFocusRect, false); @@ -132,6 +133,8 @@ OBSBasic::OBSBasic(QWidget *parent) stringstream name; name << "OBS " << App()->GetVersionString(); + installEventFilter(CreateShortcutFilter()); + blog(LOG_INFO, "%s", name.str().c_str()); setWindowTitle(QT_UTF8(name.str().c_str())); @@ -653,6 +656,7 @@ void OBSBasic::OBSInit() } InitOBSCallbacks(); + InitHotkeys(); AddExtraModulePaths(); obs_load_all_modules(); @@ -681,6 +685,71 @@ void OBSBasic::OBSInit() Qt::QueuedConnection); } +void OBSBasic::InitHotkeys() +{ + struct obs_hotkeys_translations t = {}; + t.insert = Str("Hotkeys.Insert"); + t.del = Str("Hotkeys.Delete"); + t.home = Str("Hotkeys.Home"); + t.end = Str("Hotkeys.End"); + t.page_up = Str("Hotkeys.PageUp"); + t.page_down = Str("Hotkeys.PageDown"); + t.num_lock = Str("Hotkeys.NumLock"); + t.scroll_lock = Str("Hotkeys.ScrollLock"); + t.caps_lock = Str("Hotkeys.CapsLock"); + t.backspace = Str("Hotkeys.Backspace"); + t.tab = Str("Hotkeys.Tab"); + t.print = Str("Hotkeys.Print"); + t.pause = Str("Hotkeys.Pause"); + t.left = Str("Hotkeys.Left"); + t.right = Str("Hotkeys.Right"); + t.up = Str("Hotkeys.Up"); + t.down = Str("Hotkeys.Down"); +#ifdef _WIN32 + t.meta = Str("Hotkeys.Windows"); +#else + t.meta = Str("Hotkeys.Super"); +#endif + t.menu = Str("Hotkeys.Menu"); + t.space = Str("Hotkeys.Space"); + t.numpad_num = Str("Hotkeys.NumpadNum"); + t.numpad_multiply = Str("Hotkeys.NumpadMultiply"); + t.numpad_divide = Str("Hotkeys.NumpadDivide"); + t.numpad_plus = Str("Hotkeys.NumpadAdd"); + t.numpad_minus = Str("Hotkeys.NumpadSubtract"); + t.numpad_decimal = Str("Hotkeys.NumpadDecimal"); + t.apple_keypad_num = Str("Hotkeys.AppleKeypadNum"); + t.apple_keypad_multiply = Str("Hotkeys.AppleKeypadMultiply"); + t.apple_keypad_divide = Str("Hotkeys.AppleKeypadDivide"); + t.apple_keypad_plus = Str("Hotkeys.AppleKeypadAdd"); + t.apple_keypad_minus = Str("Hotkeys.AppleKeypadSubtract"); + t.apple_keypad_decimal = Str("Hotkeys.AppleKeypadDecimal"); + t.apple_keypad_equal = Str("Hotkeys.AppleKeypadEqual"); + t.mouse_num = Str("Hotkeys.MouseButton"); + obs_hotkeys_set_translations(&t); + + obs_hotkeys_set_audio_hotkeys_translations(Str("Mute"), Str("Unmute"), + Str("Push-to-talk"), Str("Push-to-mute")); + + obs_hotkeys_set_sceneitem_hotkeys_translations( + Str("SceneItemShow"), Str("SceneItemHide")); + + obs_hotkey_enable_callback_rerouting(true); + obs_hotkey_set_callback_routing_func(OBSBasic::HotkeyTriggered, this); +} + +void OBSBasic::ProcessHotkey(obs_hotkey_id id, bool pressed) +{ + obs_hotkey_trigger_routed_callback(id, pressed); +} + +void OBSBasic::HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed) +{ + OBSBasic &basic = *static_cast(data); + QMetaObject::invokeMethod(&basic, "ProcessHotkey", + Q_ARG(obs_hotkey_id, id), Q_ARG(bool, pressed)); +} + OBSBasic::~OBSBasic() { bool previewEnabled = obs_preview_enabled(); @@ -694,6 +763,8 @@ OBSBasic::~OBSBasic() delete cpuUsageTimer; os_cpu_usage_info_destroy(cpuUsageInfo); + obs_hotkey_set_callback_routing_func(nullptr, nullptr); + service = nullptr; outputHandler.reset(); diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp index 3f601349868ce5eb99eaa6b01f91d19f5feb6283..91c8c22a26b0155b2b9d0549e82ebfbbe9808574 100644 --- a/obs/window-basic-main.hpp +++ b/obs/window-basic-main.hpp @@ -112,6 +112,8 @@ private: void Save(const char *file); void Load(const char *file); + void InitHotkeys(); + bool InitService(); bool InitBasicConfigDefaults(); @@ -177,6 +179,8 @@ private slots: void ReorderSources(OBSScene scene); + void ProcessHotkey(obs_hotkey_id id, bool pressed); + private: /* OBS Callbacks */ static void SceneReordered(void *data, calldata_t *params); @@ -199,6 +203,8 @@ private: void AddSourcePopupMenu(const QPoint &pos); void copyActionsDynamicProperties(); + static void HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed); + public: OBSScene GetCurrentScene(); diff --git a/obs/window-basic-properties.cpp b/obs/window-basic-properties.cpp index ee2cc5eb3c32267bcce35653b2e785e0164517dc..ff69aa255458c35712333cabb14f6d9c321d0a8a 100644 --- a/obs/window-basic-properties.cpp +++ b/obs/window-basic-properties.cpp @@ -78,6 +78,8 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_) view->setMinimumHeight(150); view->show(); + installEventFilter(CreateShortcutFilter()); + connect(view, SIGNAL(PropertiesResized()), this, SLOT(OnPropertiesResized())); diff --git a/obs/window-basic-settings.cpp b/obs/window-basic-settings.cpp index 5c2662662c9a62ce7d419205b748e8bd8870cccd..483bf8735eb92a4b63b01486e7bf60b1513f82b1 100644 --- a/obs/window-basic-settings.cpp +++ b/obs/window-basic-settings.cpp @@ -307,6 +307,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) // Initialize libff library ff_init(); + installEventFilter(CreateShortcutFilter()); + LoadServiceTypes(); LoadEncoderTypes(); LoadColorRanges(); diff --git a/obs/window-basic-source-select.cpp b/obs/window-basic-source-select.cpp index c426ae9c458b70f316caa9e246a99c0bc23d8e2f..aa9f8a4c278f9689515eef52f39b2ad102d7ef65 100644 --- a/obs/window-basic-source-select.cpp +++ b/obs/window-basic-source-select.cpp @@ -181,5 +181,7 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_) ui->sourceName->setFocus(); //Fixes deselect of text. ui->sourceName->selectAll(); + installEventFilter(CreateShortcutFilter()); + obs_enum_sources(EnumSources, this); } diff --git a/obs/window-basic-transform.cpp b/obs/window-basic-transform.cpp index 0dca0f49617108e1176f7b08d051eb2b320bf581..f9f9d98435f9bcebbd6752f18389e9cc58f126a4 100644 --- a/obs/window-basic-transform.cpp +++ b/obs/window-basic-transform.cpp @@ -50,6 +50,8 @@ OBSBasicTransform::OBSBasicTransform(OBSBasic *parent) HookWidget(ui->boundsWidth, DSCROLL_CHANGED, SLOT(OnControlChanged())); HookWidget(ui->boundsHeight, DSCROLL_CHANGED, SLOT(OnControlChanged())); + installEventFilter(CreateShortcutFilter()); + OBSScene curScene = main->GetCurrentScene(); SetScene(curScene); SetItem(FindASelectedItem(curScene)); diff --git a/obs/window-log-reply.cpp b/obs/window-log-reply.cpp index 0fc7e57fc83c1bdc4cf5bb0475d6ab4027887d3b..5f74a6d477a812f94cbdd4c999c46d93e9483d51 100644 --- a/obs/window-log-reply.cpp +++ b/obs/window-log-reply.cpp @@ -17,6 +17,7 @@ #include #include "window-log-reply.hpp" +#include "obs-app.hpp" OBSLogReply::OBSLogReply(QWidget *parent, const QString &url) : QDialog (parent), @@ -24,6 +25,8 @@ OBSLogReply::OBSLogReply(QWidget *parent, const QString &url) { ui->setupUi(this); ui->urlEdit->setText(url); + + installEventFilter(CreateShortcutFilter()); } void OBSLogReply::on_copyURL_clicked() diff --git a/obs/window-namedialog.cpp b/obs/window-namedialog.cpp index a51e444855285746d86b58d60c3da34bf5aeb104..380db0b196e0c08c28cf9bf39ce4719fed5a0f25 100644 --- a/obs/window-namedialog.cpp +++ b/obs/window-namedialog.cpp @@ -17,6 +17,7 @@ #include "window-namedialog.hpp" #include "ui_NameDialog.h" +#include "obs-app.hpp" using namespace std; @@ -25,6 +26,8 @@ NameDialog::NameDialog(QWidget *parent) ui (new Ui::NameDialog) { ui->setupUi(this); + + installEventFilter(CreateShortcutFilter()); } bool NameDialog::AskForName(QWidget *parent, const QString &title, diff --git a/obs/window-projector.cpp b/obs/window-projector.cpp index 44efe4ce1e95076f918c0ad48964671a4a7956b3..904e6eb36d53219bed96c88ceef683a03e2c91e6 100644 --- a/obs/window-projector.cpp +++ b/obs/window-projector.cpp @@ -15,6 +15,8 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_) "removed", OBSSourceRemoved, this) { setAttribute(Qt::WA_DeleteOnClose, true); + + installEventFilter(CreateShortcutFilter()); } OBSProjector::~OBSProjector() diff --git a/obs/window-remux.cpp b/obs/window-remux.cpp index f83f418cd8b2bc9256bb48456960854a668e77fe..2802f3ed6907bc5925b24cfa8a2c9b613fe77d73 100644 --- a/obs/window-remux.cpp +++ b/obs/window-remux.cpp @@ -47,7 +47,9 @@ OBSRemux::OBSRemux(const char *path, QWidget *parent) ui->progressBar->setMinimum(0); ui->progressBar->setMaximum(1000); ui->progressBar->setValue(0); - + + installEventFilter(CreateShortcutFilter()); + connect(ui->browseSource, &QPushButton::clicked, [&]() { BrowseInput(); }); connect(ui->browseTarget, &QPushButton::clicked,