Add macro action "hotkey" (#226)

This commit is contained in:
WarmUpTill 2021-06-13 16:21:23 -07:00 committed by GitHub
parent d692444687
commit ae4e861f7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1174 additions and 6 deletions

View File

@ -160,6 +160,7 @@ jobs:
libxcb-xinerama0-dev \
libxcomposite-dev \
libxinerama-dev \
libxtst-dev \
libmbedtls-dev \
pkg-config \
python3-dev \

View File

@ -82,6 +82,7 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro-action-edit.hpp
src/headers/macro-action-audio.hpp
src/headers/macro-action-filter.hpp
src/headers/macro-action-hotkey.hpp
src/headers/macro-action-macro.hpp
src/headers/macro-action-media.hpp
src/headers/macro-action-plugin-state.hpp
@ -115,6 +116,7 @@ set(advanced-scene-switcher_HEADERS
src/headers/macro.hpp
src/headers/macro-selection.hpp
src/headers/curl-helper.hpp
src/headers/hotkey.hpp
src/headers/screenshot-helper.hpp
src/headers/name-dialog.hpp
src/headers/duration-control.hpp
@ -153,6 +155,7 @@ set(advanced-scene-switcher_SOURCES
src/macro-action-edit.cpp
src/macro-action-audio.cpp
src/macro-action-filter.cpp
src/macro-action-hotkey.cpp
src/macro-action-macro.cpp
src/macro-action-media.cpp
src/macro-action-plugin-state.cpp

View File

@ -229,6 +229,17 @@ AdvSceneSwitcher.action.virtualCamera="Virtual camera"
AdvSceneSwitcher.action.virtualCamera.type.stop="Stop virtual camera"
AdvSceneSwitcher.action.virtualCamera.type.start="Start virtual camera"
AdvSceneSwitcher.action.virtualCamera.entry="{{actions}}"
AdvSceneSwitcher.action.hotkey="Hotkey"
AdvSceneSwitcher.action.hotkey.leftShift="Left Shift"
AdvSceneSwitcher.action.hotkey.rightShift="Right Shift"
AdvSceneSwitcher.action.hotkey.leftCtrl="Left Ctrl"
AdvSceneSwitcher.action.hotkey.rightCtrl="Right Ctrl"
AdvSceneSwitcher.action.hotkey.leftAlt="Left Alt"
AdvSceneSwitcher.action.hotkey.rightAlt="Right Alt"
AdvSceneSwitcher.action.hotkey.leftMeta="Left Meta"
AdvSceneSwitcher.action.hotkey.rightMeta="Right Meta"
AdvSceneSwitcher.action.hotkey.disabled="Cannot simulate key presses - functionality disabled!"
AdvSceneSwitcher.action.hotkey.entry="Press {{keys}}"
; Transition Tab
AdvSceneSwitcher.transitionTab.title="Transition"

View File

@ -433,6 +433,8 @@ extern "C" void FreeSceneSwitcher()
loaded_curl_lib = nullptr;
}
PlatformCleanup();
delete switcher;
switcher = nullptr;
}
@ -546,6 +548,8 @@ extern "C" void InitSceneSwitcher()
switcher->curl = f_curl_init();
}
PlatformInit();
auto cb = []() {
QMainWindow *window =
(QMainWindow *)obs_frontend_get_main_window();

125
src/headers/hotkey.hpp Normal file
View File

@ -0,0 +1,125 @@
#pragma once
extern bool canSimulateKeyPresses;
enum class HotkeyType {
Key_NoKey = 0,
Key_A,
Key_B,
Key_C,
Key_D,
Key_E,
Key_F,
Key_G,
Key_H,
Key_I,
Key_J,
Key_K,
Key_L,
Key_M,
Key_N,
Key_O,
Key_P,
Key_Q,
Key_R,
Key_S,
Key_T,
Key_U,
Key_V,
Key_W,
Key_X,
Key_Y,
Key_Z,
Key_0,
Key_1,
Key_2,
Key_3,
Key_4,
Key_5,
Key_6,
Key_7,
Key_8,
Key_9,
Key_F1,
Key_F2,
Key_F3,
Key_F4,
Key_F5,
Key_F6,
Key_F7,
Key_F8,
Key_F9,
Key_F10,
Key_F11,
Key_F12,
Key_F13,
Key_F14,
Key_F15,
Key_F16,
Key_F17,
Key_F18,
Key_F19,
Key_F20,
Key_F21,
Key_F22,
Key_F23,
Key_F24,
Key_Escape,
Key_Space,
Key_Return,
Key_Backspace,
Key_Tab,
Key_Shift_L,
Key_Shift_R,
Key_Control_L,
Key_Control_R,
Key_Alt_L,
Key_Alt_R,
Key_Win_L,
Key_Win_R,
Key_Apps,
Key_CapsLock,
Key_NumLock,
Key_ScrollLock,
Key_PrintScreen,
Key_Pause,
Key_Insert,
Key_Delete,
Key_PageUP,
Key_PageDown,
Key_Home,
Key_End,
Key_Left,
Key_Right,
Key_Up,
Key_Down,
Key_Numpad0,
Key_Numpad1,
Key_Numpad2,
Key_Numpad3,
Key_Numpad4,
Key_Numpad5,
Key_Numpad6,
Key_Numpad7,
Key_Numpad8,
Key_Numpad9,
Key_NumpadAdd,
Key_NumpadSubtract,
Key_NumpadMultiply,
Key_NumpadDivide,
Key_NumpadDecimal,
Key_NumpadEnter
};
void registerHotkeys();

View File

@ -0,0 +1,78 @@
#pragma once
#include "macro-action-edit.hpp"
#include "hotkey.hpp"
#include <QCheckBox>
class MacroActionHotkey : public MacroAction {
public:
bool PerformAction();
void LogAction();
bool Save(obs_data_t *obj);
bool Load(obs_data_t *obj);
std::string GetId() { return id; };
static std::shared_ptr<MacroAction> Create()
{
return std::make_shared<MacroActionHotkey>();
}
OBSWeakSource _HotkeySource;
HotkeyType _key = HotkeyType::Key_NoKey;
bool _leftShift = false;
bool _rightShift = false;
bool _leftCtrl = false;
bool _rightCtrl = false;
bool _leftAlt = false;
bool _rightAlt = false;
bool _leftMeta = false;
bool _rightMeta = false;
private:
static bool _registered;
static const std::string id;
};
class MacroActionHotkeyEdit : public QWidget {
Q_OBJECT
public:
MacroActionHotkeyEdit(
QWidget *parent,
std::shared_ptr<MacroActionHotkey> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionHotkeyEdit(
parent,
std::dynamic_pointer_cast<MacroActionHotkey>(action));
}
private slots:
void KeyChanged(int key);
void LShiftChanged(int state);
void RShiftChanged(int state);
void LCtrlChanged(int state);
void RCtrlChanged(int state);
void LAltChanged(int state);
void RAltChanged(int state);
void LMetaChanged(int state);
void RMetaChanged(int state);
protected:
QComboBox *_keys;
QCheckBox *_leftShift;
QCheckBox *_rightShift;
QCheckBox *_leftCtrl;
QCheckBox *_rightCtrl;
QCheckBox *_leftAlt;
QCheckBox *_rightAlt;
QCheckBox *_leftMeta;
QCheckBox *_rightMeta;
std::shared_ptr<MacroActionHotkey> _entryData;
private:
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@ -1,4 +1,5 @@
#pragma once
#include "hotkey.hpp"
void GetWindowList(std::vector<std::string> &windows);
void GetWindowList(QStringList &windows);
@ -9,3 +10,6 @@ std::pair<int, int> getCursorPos();
int secondsSinceLastInput();
void GetProcessList(QStringList &processes);
bool isInFocus(const QString &executable);
void PressKeys(const std::vector<HotkeyType> keys);
void PlatformInit();
void PlatformCleanup();

View File

@ -1,7 +1,8 @@
#include "headers/hotkey.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include <obs-module.h>
#include <fstream>
#include <regex>
#include "headers/advanced-scene-switcher.hpp"
void startHotkeyFunc(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey,
bool pressed)

View File

@ -1,7 +1,11 @@
#include "../headers/hotkey.hpp"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/extensions/scrnsaver.h>
#include <X11/extensions/XTest.h>
#undef Bool
#undef CursorShape
#undef Expose
@ -15,11 +19,18 @@
#undef Unsorted
#include <util/platform.h>
#include <vector>
#include <thread>
#include <QStringList>
#include <QRegularExpression>
#include <QLibrary>
static Display *xdisplay = 0;
static QLibrary *libXtstHandle = nullptr;
typedef int (*keyPressFunc)(Display *, unsigned int, bool, unsigned long);
static keyPressFunc pressFunc = nullptr;
bool canSimulateKeyPresses = false;
Display *disp()
{
if (!xdisplay)
@ -242,14 +253,13 @@ std::pair<int, int> getCursorPos()
int win_y;
unsigned int mask;
dpy = XOpenDisplay(NULL);
dpy = disp();
root = XDefaultRootWindow(dpy);
if (XQueryPointer(dpy, root, &ret_root, &ret_child, &root_x, &root_y,
&win_x, &win_y, &mask)) {
pos = std::pair<int, int>(root_x, root_y);
}
XCloseDisplay(dpy);
return pos;
}
@ -382,14 +392,186 @@ int secondsSinceLastInput()
mit_info = XScreenSaverAllocInfo();
if ((display = XOpenDisplay(NULL)) == NULL) {
return (-1);
if ((display = disp()) == NULL) {
return -1;
}
screen = DefaultScreen(display);
XScreenSaverQueryInfo(display, RootWindow(display, screen), mit_info);
idle_time = (mit_info->idle) / 1000;
XFree(mit_info);
XCloseDisplay(display);
return idle_time;
}
static std::unordered_map<HotkeyType, long> keyTable = {
// Chars
{HotkeyType::Key_A, XK_A},
{HotkeyType::Key_B, XK_B},
{HotkeyType::Key_C, XK_C},
{HotkeyType::Key_D, XK_D},
{HotkeyType::Key_E, XK_E},
{HotkeyType::Key_F, XK_F},
{HotkeyType::Key_G, XK_G},
{HotkeyType::Key_H, XK_H},
{HotkeyType::Key_I, XK_I},
{HotkeyType::Key_J, XK_J},
{HotkeyType::Key_K, XK_K},
{HotkeyType::Key_L, XK_L},
{HotkeyType::Key_M, XK_M},
{HotkeyType::Key_N, XK_N},
{HotkeyType::Key_O, XK_O},
{HotkeyType::Key_P, XK_P},
{HotkeyType::Key_Q, XK_Q},
{HotkeyType::Key_R, XK_R},
{HotkeyType::Key_S, XK_S},
{HotkeyType::Key_T, XK_T},
{HotkeyType::Key_U, XK_U},
{HotkeyType::Key_V, XK_V},
{HotkeyType::Key_W, XK_W},
{HotkeyType::Key_X, XK_X},
{HotkeyType::Key_Y, XK_Y},
{HotkeyType::Key_Z, XK_Z},
// Numbers
{HotkeyType::Key_0, XK_0},
{HotkeyType::Key_1, XK_1},
{HotkeyType::Key_2, XK_2},
{HotkeyType::Key_3, XK_3},
{HotkeyType::Key_4, XK_4},
{HotkeyType::Key_5, XK_5},
{HotkeyType::Key_6, XK_6},
{HotkeyType::Key_7, XK_7},
{HotkeyType::Key_8, XK_8},
{HotkeyType::Key_9, XK_9},
{HotkeyType::Key_F1, XK_F1},
{HotkeyType::Key_F2, XK_F2},
{HotkeyType::Key_F3, XK_F3},
{HotkeyType::Key_F4, XK_F4},
{HotkeyType::Key_F5, XK_F5},
{HotkeyType::Key_F6, XK_F6},
{HotkeyType::Key_F7, XK_F7},
{HotkeyType::Key_F8, XK_F8},
{HotkeyType::Key_F9, XK_F9},
{HotkeyType::Key_F10, XK_F10},
{HotkeyType::Key_F11, XK_F11},
{HotkeyType::Key_F12, XK_F12},
{HotkeyType::Key_F13, XK_F13},
{HotkeyType::Key_F14, XK_F14},
{HotkeyType::Key_F15, XK_F15},
{HotkeyType::Key_F16, XK_F16},
{HotkeyType::Key_F17, XK_F17},
{HotkeyType::Key_F18, XK_F18},
{HotkeyType::Key_F19, XK_F19},
{HotkeyType::Key_F20, XK_F20},
{HotkeyType::Key_F21, XK_F21},
{HotkeyType::Key_F22, XK_F22},
{HotkeyType::Key_F23, XK_F23},
{HotkeyType::Key_F24, XK_F24},
{HotkeyType::Key_Escape, XK_Escape},
{HotkeyType::Key_Space, XK_space},
{HotkeyType::Key_Return, XK_Return},
{HotkeyType::Key_Backspace, XK_BackSpace},
{HotkeyType::Key_Tab, XK_Tab},
{HotkeyType::Key_Shift_L, XK_Shift_L},
{HotkeyType::Key_Shift_R, XK_Shift_R},
{HotkeyType::Key_Control_L, XK_Control_L},
{HotkeyType::Key_Control_R, XK_Control_R},
{HotkeyType::Key_Alt_L, XK_Alt_L},
{HotkeyType::Key_Alt_R, XK_Alt_R},
{HotkeyType::Key_Win_L, XK_Super_L},
{HotkeyType::Key_Win_R, XK_Super_R},
{HotkeyType::Key_Apps, XK_Hyper_L},
{HotkeyType::Key_CapsLock, XK_Caps_Lock},
{HotkeyType::Key_NumLock, XK_Num_Lock},
{HotkeyType::Key_ScrollLock, XK_Scroll_Lock},
{HotkeyType::Key_PrintScreen, XK_Print},
{HotkeyType::Key_Pause, XK_Pause},
{HotkeyType::Key_Insert, XK_Insert},
{HotkeyType::Key_Delete, XK_Delete},
{HotkeyType::Key_PageUP, XK_Page_Up},
{HotkeyType::Key_PageDown, XK_Page_Down},
{HotkeyType::Key_Home, XK_Home},
{HotkeyType::Key_End, XK_End},
{HotkeyType::Key_Left, XK_Left},
{HotkeyType::Key_Up, XK_Up},
{HotkeyType::Key_Right, XK_Right},
{HotkeyType::Key_Down, XK_Down},
{HotkeyType::Key_Numpad0, XK_KP_0},
{HotkeyType::Key_Numpad1, XK_KP_1},
{HotkeyType::Key_Numpad2, XK_KP_2},
{HotkeyType::Key_Numpad3, XK_KP_3},
{HotkeyType::Key_Numpad4, XK_KP_4},
{HotkeyType::Key_Numpad5, XK_KP_5},
{HotkeyType::Key_Numpad6, XK_KP_6},
{HotkeyType::Key_Numpad7, XK_KP_7},
{HotkeyType::Key_Numpad8, XK_KP_8},
{HotkeyType::Key_Numpad9, XK_KP_9},
{HotkeyType::Key_NumpadAdd, XK_KP_Add},
{HotkeyType::Key_NumpadSubtract, XK_KP_Subtract},
{HotkeyType::Key_NumpadMultiply, XK_KP_Multiply},
{HotkeyType::Key_NumpadDivide, XK_KP_Divide},
{HotkeyType::Key_NumpadDecimal, XK_KP_Decimal},
{HotkeyType::Key_NumpadEnter, XK_KP_Enter},
};
void PressKeys(const std::vector<HotkeyType> keys)
{
if (!canSimulateKeyPresses) {
return;
}
Display *display = disp();
if (display == NULL)
return;
// Press keys
for (auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
pressFunc(display, XKeysymToKeycode(display, it->second), true,
CurrentTime);
}
XFlush(display);
std::this_thread::sleep_for(std::chrono::milliseconds(300));
// Release keys
for (auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
pressFunc(display, XKeysymToKeycode(display, it->second), false,
CurrentTime);
}
XFlush(display);
}
void PlatformInit()
{
libXtstHandle = new QLibrary("libXtst.so", nullptr);
pressFunc = (keyPressFunc)libXtstHandle->resolve("XTestFakeKeyEvent");
int _;
canSimulateKeyPresses = pressFunc &&
!XQueryExtension(disp(), "XTEST", &_, &_, &_);
}
void PlatformCleanup()
{
delete libXtstHandle;
libXtstHandle = nullptr;
cleanupDisplay();
}

377
src/macro-action-hotkey.cpp Normal file
View File

@ -0,0 +1,377 @@
#include "headers/macro-action-hotkey.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
const std::string MacroActionHotkey::id = "hotkey";
// TODO:
// I can't seem to get this to work so drop support for this functionality
// on MacOS
#if !__APPLE__
bool MacroActionHotkey::_registered = MacroActionFactory::Register(
MacroActionHotkey::id,
{MacroActionHotkey::Create, MacroActionHotkeyEdit::Create,
"AdvSceneSwitcher.action.hotkey"});
#endif
bool MacroActionHotkey::PerformAction()
{
std::vector<HotkeyType> keys;
if (_leftShift) {
keys.push_back(HotkeyType::Key_Shift_L);
}
if (_rightShift) {
keys.push_back(HotkeyType::Key_Shift_R);
}
if (_leftCtrl) {
keys.push_back(HotkeyType::Key_Control_L);
}
if (_rightCtrl) {
keys.push_back(HotkeyType::Key_Control_R);
}
if (_leftAlt) {
keys.push_back(HotkeyType::Key_Alt_L);
}
if (_rightAlt) {
keys.push_back(HotkeyType::Key_Alt_R);
}
if (_key != HotkeyType::Key_NoKey) {
keys.push_back(_key);
}
if (!keys.empty()) {
std::thread t([keys]() { PressKeys(keys); });
t.detach();
}
return true;
}
void MacroActionHotkey::LogAction()
{
vblog(LOG_INFO, "sent hotkey");
}
bool MacroActionHotkey::Save(obs_data_t *obj)
{
MacroAction::Save(obj);
obs_data_set_int(obj, "key", static_cast<int>(_key));
obs_data_set_bool(obj, "left_shift", _leftShift);
obs_data_set_bool(obj, "right_shift", _rightShift);
obs_data_set_bool(obj, "left_ctrl", _leftCtrl);
obs_data_set_bool(obj, "right_ctrl", _rightCtrl);
obs_data_set_bool(obj, "left_alt", _leftAlt);
obs_data_set_bool(obj, "right_alt", _rightAlt);
obs_data_set_bool(obj, "left_meta", _leftMeta);
obs_data_set_bool(obj, "right_meta", _rightMeta);
return true;
}
bool MacroActionHotkey::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_key = static_cast<HotkeyType>(obs_data_get_int(obj, "key"));
_leftShift = obs_data_get_bool(obj, "left_shift");
_rightShift = obs_data_get_bool(obj, "right_shift");
_leftCtrl = obs_data_get_bool(obj, "left_ctrl");
_rightCtrl = obs_data_get_bool(obj, "right_ctrl");
_leftAlt = obs_data_get_bool(obj, "left_alt");
_rightAlt = obs_data_get_bool(obj, "right_alt");
_leftMeta = obs_data_get_bool(obj, "left_meta");
_rightMeta = obs_data_get_bool(obj, "right_meta");
return true;
}
static inline void populateKeySelection(QComboBox *list)
{
list->addItem("No key");
list->addItem("A");
list->addItem("B");
list->addItem("C");
list->addItem("D");
list->addItem("E");
list->addItem("F");
list->addItem("G");
list->addItem("H");
list->addItem("I");
list->addItem("J");
list->addItem("K");
list->addItem("L");
list->addItem("M");
list->addItem("N");
list->addItem("O");
list->addItem("P");
list->addItem("Q");
list->addItem("R");
list->addItem("S");
list->addItem("T");
list->addItem("U");
list->addItem("V");
list->addItem("W");
list->addItem("X");
list->addItem("Y");
list->addItem("Z");
list->addItem("0");
list->addItem("1");
list->addItem("2");
list->addItem("3");
list->addItem("4");
list->addItem("5");
list->addItem("6");
list->addItem("7");
list->addItem("8");
list->addItem("9");
list->addItem("F1");
list->addItem("F2");
list->addItem("F3");
list->addItem("F4");
list->addItem("F5");
list->addItem("F6");
list->addItem("F7");
list->addItem("F8");
list->addItem("F9");
list->addItem("F10");
list->addItem("F11");
list->addItem("F12");
list->addItem("F13");
list->addItem("F14");
list->addItem("F15");
list->addItem("F16");
list->addItem("F17");
list->addItem("F18");
list->addItem("F19");
list->addItem("F20");
list->addItem("F21");
list->addItem("F22");
list->addItem("F23");
list->addItem("F24");
list->addItem("Escape");
list->addItem("Space");
list->addItem("Return");
list->addItem("Backspace");
list->addItem("Tab");
list->addItem("Shift_L");
list->addItem("Shift_R");
list->addItem("Control_L");
list->addItem("Control_R");
list->addItem("Alt_L");
list->addItem("Alt_R");
list->addItem("Win_L");
list->addItem("Win_R");
list->addItem("Apps");
list->addItem("CapsLock");
list->addItem("NumLock");
list->addItem("ScrollLock");
list->addItem("PrintScreen");
list->addItem("Pause");
list->addItem("Insert");
list->addItem("Delete");
list->addItem("PageUP");
list->addItem("PageDown");
list->addItem("Home");
list->addItem("End");
list->addItem("Left");
list->addItem("Right");
list->addItem("Up");
list->addItem("Down");
list->addItem("Numpad0");
list->addItem("Numpad1");
list->addItem("Numpad2");
list->addItem("Numpad3");
list->addItem("Numpad4");
list->addItem("Numpad5");
list->addItem("Numpad6");
list->addItem("Numpad7");
list->addItem("Numpad8");
list->addItem("Numpad9");
list->addItem("NumpadAdd");
list->addItem("NumpadSubtract");
list->addItem("NumpadMultiply");
list->addItem("NumpadDivide");
list->addItem("NumpadDecimal");
list->addItem("NumpadEnter");
}
MacroActionHotkeyEdit::MacroActionHotkeyEdit(
QWidget *parent, std::shared_ptr<MacroActionHotkey> entryData)
: QWidget(parent)
{
_keys = new QComboBox();
_leftShift = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftShift"));
_rightShift = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.rightShift"));
_leftCtrl = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftCtrl"));
_rightCtrl = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.rightCtrl"));
_leftAlt = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftAlt"));
_rightAlt = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.rightAlt"));
_leftMeta = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftMeta"));
_rightMeta = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.rightMeta"));
populateKeySelection(_keys);
QWidget::connect(_keys, SIGNAL(currentIndexChanged(int)), this,
SLOT(KeyChanged(int)));
QWidget::connect(_leftShift, SIGNAL(stateChanged(int)), this,
SLOT(LShiftChanged(int)));
QWidget::connect(_rightShift, SIGNAL(stateChanged(int)), this,
SLOT(RShiftChanged(int)));
QWidget::connect(_leftCtrl, SIGNAL(stateChanged(int)), this,
SLOT(LCtrlChanged(int)));
QWidget::connect(_rightCtrl, SIGNAL(stateChanged(int)), this,
SLOT(RCtrlChanged(int)));
QWidget::connect(_leftAlt, SIGNAL(stateChanged(int)), this,
SLOT(LAltChanged(int)));
QWidget::connect(_rightAlt, SIGNAL(stateChanged(int)), this,
SLOT(RAltChanged(int)));
QWidget::connect(_leftMeta, SIGNAL(stateChanged(int)), this,
SLOT(LMetaChanged(int)));
QWidget::connect(_rightMeta, SIGNAL(stateChanged(int)), this,
SLOT(RMetaChanged(int)));
QHBoxLayout *line1Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{keys}}", _keys},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.hotkey.entry"),
line1Layout, widgetPlaceholders);
QHBoxLayout *line2Layout = new QHBoxLayout;
line2Layout->addWidget(_leftShift);
line2Layout->addWidget(_rightShift);
line2Layout->addWidget(_leftCtrl);
line2Layout->addWidget(_rightCtrl);
line2Layout->addWidget(_leftAlt);
line2Layout->addWidget(_rightAlt);
line2Layout->addWidget(_leftMeta);
line2Layout->addWidget(_rightMeta);
line2Layout->addStretch();
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
if (!canSimulateKeyPresses) {
mainLayout->addWidget(new QLabel(obs_module_text(
"AdvSceneSwitcher.action.hotkey.disabled")));
}
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionHotkeyEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_keys->setCurrentIndex(static_cast<int>(_entryData->_key));
_leftShift->setChecked(_entryData->_leftShift);
_rightShift->setChecked(_entryData->_rightShift);
_leftCtrl->setChecked(_entryData->_leftCtrl);
_rightCtrl->setChecked(_entryData->_rightCtrl);
_leftAlt->setChecked(_entryData->_leftAlt);
_rightAlt->setChecked(_entryData->_rightAlt);
_leftMeta->setChecked(_entryData->_leftMeta);
_rightMeta->setChecked(_entryData->_rightMeta);
}
void MacroActionHotkeyEdit::LShiftChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_leftShift = state;
}
void MacroActionHotkeyEdit::RShiftChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_rightShift = state;
}
void MacroActionHotkeyEdit::LCtrlChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_leftCtrl = state;
}
void MacroActionHotkeyEdit::RCtrlChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_rightCtrl = state;
}
void MacroActionHotkeyEdit::LAltChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_leftAlt = state;
}
void MacroActionHotkeyEdit::RAltChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_rightAlt = state;
}
void MacroActionHotkeyEdit::LMetaChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_leftMeta = state;
}
void MacroActionHotkeyEdit::RMetaChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_rightMeta = state;
}
void MacroActionHotkeyEdit::KeyChanged(int key)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_key = static_cast<HotkeyType>(key);
}

View File

@ -1,3 +1,5 @@
#include "../headers/hotkey.hpp"
#import <AppKit/AppKit.h>
#import <CoreFoundation/CoreFoundation.h>
#import <CoreGraphics/CGEvent.h>
@ -7,6 +9,10 @@
#include <vector>
#include <QStringList>
#include <QRegularExpression>
#include <map>
#include <thread>
bool canSimulateKeyPresses = true;
void GetWindowList(std::vector<std::string> &windows)
{
@ -281,3 +287,216 @@ bool isInFocus(const QString &executable)
return (equals || matches);
}
static std::map<HotkeyType, CGKeyCode> keyTable = {
// Chars
{HotkeyType::Key_A, kVK_ANSI_A},
{HotkeyType::Key_B, kVK_ANSI_B},
{HotkeyType::Key_C, kVK_ANSI_C},
{HotkeyType::Key_D, kVK_ANSI_D},
{HotkeyType::Key_E, kVK_ANSI_E},
{HotkeyType::Key_F, kVK_ANSI_F},
{HotkeyType::Key_G, kVK_ANSI_G},
{HotkeyType::Key_H, kVK_ANSI_H},
{HotkeyType::Key_I, kVK_ANSI_I},
{HotkeyType::Key_J, kVK_ANSI_J},
{HotkeyType::Key_K, kVK_ANSI_K},
{HotkeyType::Key_L, kVK_ANSI_L},
{HotkeyType::Key_M, kVK_ANSI_M},
{HotkeyType::Key_N, kVK_ANSI_N},
{HotkeyType::Key_O, kVK_ANSI_O},
{HotkeyType::Key_P, kVK_ANSI_P},
{HotkeyType::Key_Q, kVK_ANSI_Q},
{HotkeyType::Key_R, kVK_ANSI_R},
{HotkeyType::Key_S, kVK_ANSI_S},
{HotkeyType::Key_T, kVK_ANSI_T},
{HotkeyType::Key_U, kVK_ANSI_U},
{HotkeyType::Key_V, kVK_ANSI_V},
{HotkeyType::Key_W, kVK_ANSI_W},
{HotkeyType::Key_X, kVK_ANSI_X},
{HotkeyType::Key_Y, kVK_ANSI_Y},
{HotkeyType::Key_Z, kVK_ANSI_Z},
// Numbers
{HotkeyType::Key_0, kVK_ANSI_0},
{HotkeyType::Key_1, kVK_ANSI_1},
{HotkeyType::Key_2, kVK_ANSI_2},
{HotkeyType::Key_3, kVK_ANSI_3},
{HotkeyType::Key_4, kVK_ANSI_4},
{HotkeyType::Key_5, kVK_ANSI_5},
{HotkeyType::Key_6, kVK_ANSI_6},
{HotkeyType::Key_7, kVK_ANSI_7},
{HotkeyType::Key_8, kVK_ANSI_8},
{HotkeyType::Key_9, kVK_ANSI_9},
{HotkeyType::Key_F1, kVK_F1},
{HotkeyType::Key_F2, kVK_F2},
{HotkeyType::Key_F3, kVK_F3},
{HotkeyType::Key_F4, kVK_F4},
{HotkeyType::Key_F5, kVK_F5},
{HotkeyType::Key_F6, kVK_F6},
{HotkeyType::Key_F7, kVK_F7},
{HotkeyType::Key_F8, kVK_F8},
{HotkeyType::Key_F9, kVK_F9},
{HotkeyType::Key_F10, kVK_F10},
{HotkeyType::Key_F11, kVK_F11},
{HotkeyType::Key_F12, kVK_F12},
{HotkeyType::Key_F13, kVK_F13},
{HotkeyType::Key_F14, kVK_F14},
{HotkeyType::Key_F15, kVK_F15},
{HotkeyType::Key_F16, kVK_F16},
{HotkeyType::Key_F17, kVK_F17},
{HotkeyType::Key_F18, kVK_F18},
{HotkeyType::Key_F19, kVK_F19},
{HotkeyType::Key_F20, kVK_F20},
// F21-F24 does not exist on MacOS
{HotkeyType::Key_Escape, kVK_Escape},
{HotkeyType::Key_Space, kVK_Space},
{HotkeyType::Key_Return, kVK_Return},
{HotkeyType::Key_Backspace, kVK_Delete},
{HotkeyType::Key_Tab, kVK_Tab},
{HotkeyType::Key_Shift_L, kVK_Shift},
{HotkeyType::Key_Shift_R, kVK_RightShift},
{HotkeyType::Key_Control_L, kVK_Control},
{HotkeyType::Key_Control_R, kVK_RightControl},
{HotkeyType::Key_Alt_L, kVK_Option},
{HotkeyType::Key_Alt_R, kVK_RightOption},
{HotkeyType::Key_Win_L, kVK_Command},
{HotkeyType::Key_Win_R, kVK_RightCommand},
// Not sure what Key_Apps would correspond to
{HotkeyType::Key_CapsLock, kVK_CapsLock},
{HotkeyType::Key_NumLock, kVK_ANSI_KeypadClear},
{HotkeyType::Key_ScrollLock, kVK_F14},
{HotkeyType::Key_PrintScreen, kVK_F13},
{HotkeyType::Key_Pause, kVK_F15},
{HotkeyType::Key_Insert, kVK_Help},
{HotkeyType::Key_Delete, kVK_ForwardDelete},
{HotkeyType::Key_PageUP, kVK_PageUp},
{HotkeyType::Key_PageDown, kVK_PageDown},
{HotkeyType::Key_Home, kVK_Home},
{HotkeyType::Key_End, kVK_End},
{HotkeyType::Key_Left, kVK_LeftArrow},
{HotkeyType::Key_Up, kVK_UpArrow},
{HotkeyType::Key_Right, kVK_RightArrow},
{HotkeyType::Key_Down, kVK_DownArrow},
{HotkeyType::Key_Numpad0, kVK_ANSI_Keypad0},
{HotkeyType::Key_Numpad1, kVK_ANSI_Keypad1},
{HotkeyType::Key_Numpad2, kVK_ANSI_Keypad2},
{HotkeyType::Key_Numpad3, kVK_ANSI_Keypad3},
{HotkeyType::Key_Numpad4, kVK_ANSI_Keypad4},
{HotkeyType::Key_Numpad5, kVK_ANSI_Keypad5},
{HotkeyType::Key_Numpad6, kVK_ANSI_Keypad6},
{HotkeyType::Key_Numpad7, kVK_ANSI_Keypad7},
{HotkeyType::Key_Numpad8, kVK_ANSI_Keypad8},
{HotkeyType::Key_Numpad9, kVK_ANSI_Keypad9},
{HotkeyType::Key_NumpadAdd, kVK_ANSI_KeypadPlus},
{HotkeyType::Key_NumpadSubtract, kVK_ANSI_KeypadMinus},
{HotkeyType::Key_NumpadMultiply, kVK_ANSI_KeypadMultiply},
{HotkeyType::Key_NumpadDivide, kVK_ANSI_KeypadDivide},
{HotkeyType::Key_NumpadDecimal, kVK_ANSI_KeypadDecimal},
{HotkeyType::Key_NumpadEnter, kVK_ANSI_KeypadEnter},
};
void PressKeys(const std::vector<HotkeyType> keys)
{
// TODO:
// I can't seem to get this to work so drop support for this functionality
// on MacOS
canSimulateKeyPresses = false;
return;
// Check premissions
NSDictionary *options =
@{(__bridge id)kAXTrustedCheckOptionPrompt: @NO};
if (!AXIsProcessTrustedWithOptions((CFDictionaryRef)options)) {
NSString *urlString =
@"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
[[NSWorkspace sharedWorkspace]
openURL:[NSURL URLWithString:urlString]];
canSimulateKeyPresses = false;
return;
}
long modifierFlags = 0;
auto source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
if (!source) {
canSimulateKeyPresses = false;
return;
}
// Press keys
for (auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
CGKeyCode inputKeyCode = it->second;
CGEventRef keyPress =
CGEventCreateKeyboardEvent(source, inputKeyCode, true);
if (!keyPress) {
canSimulateKeyPresses = false;
CFRelease(source);
return;
}
CGEventSetFlags(keyPress, modifierFlags);
CGEventPost(kCGAnnotatedSessionEventTap, keyPress);
CFRelease(keyPress);
switch (it->first) {
case HotkeyType::Key_Shift_L:
case HotkeyType::Key_Shift_R:
modifierFlags |= NX_SHIFTMASK;
break;
case HotkeyType::Key_Control_L:
case HotkeyType::Key_Control_R:
modifierFlags |= NX_CONTROLMASK;
break;
case HotkeyType::Key_Alt_L:
case HotkeyType::Key_Alt_R:
modifierFlags |= NX_ALTERNATEMASK;
break;
case HotkeyType::Key_Win_L:
case HotkeyType::Key_Win_R:
modifierFlags |= NX_COMMANDMASK;
break;
default:
break;
}
}
// When instantly releasing the key presses OBS might miss them
std::this_thread::sleep_for(std::chrono::milliseconds(300));
// Release keys
for (auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
CGKeyCode inputKeyCode = it->second;
CGEventRef keyRelease =
CGEventCreateKeyboardEvent(source, inputKeyCode, false);
if (!keyRelease) {
canSimulateKeyPresses = false;
CFRelease(source);
return;
}
CGEventPost(kCGAnnotatedSessionEventTap, keyRelease);
CFRelease(keyRelease);
}
CFRelease(source);
}
void PlatformCleanup() {}
void PlatformInit() {}

View File

@ -1,3 +1,5 @@
#include "../headers/hotkey.hpp"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <util/platform.h>
@ -9,9 +11,12 @@
#include <vector>
#include <QStringList>
#include <QRegularExpression>
#include "..\headers\platform-funcs.hpp"
#define MAX_SEARCH 1000
bool canSimulateKeyPresses = true;
static bool GetWindowTitle(HWND window, std::string &title)
{
size_t len = (size_t)GetWindowTextLengthW(window);
@ -266,6 +271,160 @@ bool isInFocus(const QString &executable)
return (equals || matches);
}
static std::unordered_map<HotkeyType, long> keyTable = {
// Chars
{HotkeyType::Key_A, 0x41},
{HotkeyType::Key_B, 0x42},
{HotkeyType::Key_C, 0x43},
{HotkeyType::Key_D, 0x44},
{HotkeyType::Key_E, 0x45},
{HotkeyType::Key_F, 0x46},
{HotkeyType::Key_G, 0x47},
{HotkeyType::Key_H, 0x48},
{HotkeyType::Key_I, 0x49},
{HotkeyType::Key_J, 0x4A},
{HotkeyType::Key_K, 0x4B},
{HotkeyType::Key_L, 0x4C},
{HotkeyType::Key_M, 0x4D},
{HotkeyType::Key_N, 0x4E},
{HotkeyType::Key_O, 0x4F},
{HotkeyType::Key_P, 0x50},
{HotkeyType::Key_Q, 0x51},
{HotkeyType::Key_R, 0x52},
{HotkeyType::Key_S, 0x53},
{HotkeyType::Key_T, 0x54},
{HotkeyType::Key_U, 0x55},
{HotkeyType::Key_V, 0x56},
{HotkeyType::Key_W, 0x57},
{HotkeyType::Key_X, 0x58},
{HotkeyType::Key_Y, 0x59},
{HotkeyType::Key_Z, 0x5A},
// Numbers
{HotkeyType::Key_0, 0x30},
{HotkeyType::Key_1, 0x31},
{HotkeyType::Key_2, 0x32},
{HotkeyType::Key_3, 0x33},
{HotkeyType::Key_4, 0x34},
{HotkeyType::Key_5, 0x35},
{HotkeyType::Key_6, 0x36},
{HotkeyType::Key_7, 0x37},
{HotkeyType::Key_8, 0x38},
{HotkeyType::Key_9, 0x39},
{HotkeyType::Key_F1, VK_F1},
{HotkeyType::Key_F2, VK_F2},
{HotkeyType::Key_F3, VK_F3},
{HotkeyType::Key_F4, VK_F4},
{HotkeyType::Key_F5, VK_F5},
{HotkeyType::Key_F6, VK_F6},
{HotkeyType::Key_F7, VK_F7},
{HotkeyType::Key_F8, VK_F8},
{HotkeyType::Key_F9, VK_F9},
{HotkeyType::Key_F10, VK_F10},
{HotkeyType::Key_F11, VK_F11},
{HotkeyType::Key_F12, VK_F12},
{HotkeyType::Key_F13, VK_F13},
{HotkeyType::Key_F14, VK_F14},
{HotkeyType::Key_F15, VK_F15},
{HotkeyType::Key_F16, VK_F16},
{HotkeyType::Key_F17, VK_F17},
{HotkeyType::Key_F18, VK_F18},
{HotkeyType::Key_F19, VK_F19},
{HotkeyType::Key_F20, VK_F20},
{HotkeyType::Key_F21, VK_F21},
{HotkeyType::Key_F22, VK_F22},
{HotkeyType::Key_F23, VK_F23},
{HotkeyType::Key_F24, VK_F24},
{HotkeyType::Key_Escape, VK_ESCAPE},
{HotkeyType::Key_Space, VK_SPACE},
{HotkeyType::Key_Return, VK_RETURN},
{HotkeyType::Key_Backspace, VK_BACK},
{HotkeyType::Key_Tab, VK_TAB},
{HotkeyType::Key_Shift_L, VK_LSHIFT},
{HotkeyType::Key_Shift_R, VK_RSHIFT},
{HotkeyType::Key_Control_L, VK_LCONTROL},
{HotkeyType::Key_Control_R, VK_RCONTROL},
{HotkeyType::Key_Alt_L, VK_LMENU},
{HotkeyType::Key_Alt_R, VK_RMENU},
{HotkeyType::Key_Win_L, VK_LWIN},
{HotkeyType::Key_Win_R, VK_RWIN},
{HotkeyType::Key_Apps, VK_APPS},
{HotkeyType::Key_CapsLock, VK_CAPITAL},
{HotkeyType::Key_NumLock, VK_NUMLOCK},
{HotkeyType::Key_ScrollLock, VK_SCROLL},
{HotkeyType::Key_PrintScreen, VK_SNAPSHOT},
{HotkeyType::Key_Pause, VK_PAUSE},
{HotkeyType::Key_Insert, VK_INSERT},
{HotkeyType::Key_Delete, VK_DELETE},
{HotkeyType::Key_PageUP, VK_PRIOR},
{HotkeyType::Key_PageDown, VK_NEXT},
{HotkeyType::Key_Home, VK_HOME},
{HotkeyType::Key_End, VK_END},
{HotkeyType::Key_Left, VK_LEFT},
{HotkeyType::Key_Up, VK_UP},
{HotkeyType::Key_Right, VK_RIGHT},
{HotkeyType::Key_Down, VK_DOWN},
{HotkeyType::Key_Numpad0, VK_NUMPAD0},
{HotkeyType::Key_Numpad1, VK_NUMPAD1},
{HotkeyType::Key_Numpad2, VK_NUMPAD2},
{HotkeyType::Key_Numpad3, VK_NUMPAD3},
{HotkeyType::Key_Numpad4, VK_NUMPAD4},
{HotkeyType::Key_Numpad5, VK_NUMPAD5},
{HotkeyType::Key_Numpad6, VK_NUMPAD6},
{HotkeyType::Key_Numpad7, VK_NUMPAD7},
{HotkeyType::Key_Numpad8, VK_NUMPAD8},
{HotkeyType::Key_Numpad9, VK_NUMPAD9},
{HotkeyType::Key_NumpadAdd, VK_ADD},
{HotkeyType::Key_NumpadSubtract, VK_SUBTRACT},
{HotkeyType::Key_NumpadMultiply, VK_MULTIPLY},
{HotkeyType::Key_NumpadDivide, VK_DIVIDE},
{HotkeyType::Key_NumpadDecimal, VK_DECIMAL},
{HotkeyType::Key_NumpadEnter, VK_RETURN},
};
void PressKeys(const std::vector<HotkeyType> keys)
{
INPUT ip;
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = 0;
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;
// Press keys
ip.ki.dwFlags = 0;
for (auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
ip.ki.wVk = it->second;
SendInput(1, &ip, sizeof(INPUT));
}
// When instantly releasing the key presses OBS might miss them
Sleep(300);
// Release keys
ip.ki.dwFlags = KEYEVENTF_KEYUP;
for (auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
ip.ki.wVk = it->second;
SendInput(1, &ip, sizeof(INPUT));
}
}
int getLastInputTime()
{
LASTINPUTINFO lastInputInfo;
@ -285,3 +444,7 @@ int secondsSinceLastInput()
{
return (getTime() - getLastInputTime()) / 1000;
}
void PlatformInit() {}
void PlatformCleanup() {}