Include press duration option and option to limit key press to OBS

This commit is contained in:
WarmUpTill
2021-09-18 19:35:59 +02:00
committed by WarmUpTill
parent aca9b84fc6
commit 7c6428b27e
7 changed files with 261 additions and 19 deletions

View File

@@ -282,8 +282,9 @@ 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.onlyOBS="Send keypress only to OBS"
AdvSceneSwitcher.action.hotkey.disabled="Cannot simulate key presses - functionality disabled!"
AdvSceneSwitcher.action.hotkey.entry="Press {{keys}}"
AdvSceneSwitcher.action.hotkey.entry="Press {{keys}} for {{duration}} milliseconds"
AdvSceneSwitcher.action.sceneOrder="Scene item order"
AdvSceneSwitcher.action.sceneOrder.type.moveUp="Move up"
AdvSceneSwitcher.action.sceneOrder.type.moveDown="Move down"

View File

@@ -3,6 +3,7 @@
#include "hotkey.hpp"
#include <QCheckBox>
#include <QSpinBox>
#include <QHBoxLayout>
class MacroActionHotkey : public MacroAction {
@@ -27,6 +28,8 @@ public:
bool _rightAlt = false;
bool _leftMeta = false;
bool _rightMeta = false;
int _duration = 300; // in ms
bool _onlySendToObs = false;
private:
static bool _registered;
@@ -59,6 +62,8 @@ private slots:
void RAltChanged(int state);
void LMetaChanged(int state);
void RMetaChanged(int state);
void DurationChanged(int ms);
void OnlySendToOBSChanged(int state);
protected:
QComboBox *_keys;
@@ -70,10 +75,14 @@ protected:
QCheckBox *_rightAlt;
QCheckBox *_leftMeta;
QCheckBox *_rightMeta;
QSpinBox *_duration;
QCheckBox *_onlySendToOBS;
QLabel *_noKeyPressSimulationWarning;
std::shared_ptr<MacroActionHotkey> _entryData;
private:
void SetWarningVisibility();
QHBoxLayout *_mainLayout;
bool _loading = true;
};

View File

@@ -10,6 +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 PressKeys(const std::vector<HotkeyType> keys, int duration);
void PlatformInit();
void PlatformCleanup();

View File

@@ -524,7 +524,7 @@ static std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_NumpadEnter, XK_KP_Enter},
};
void PressKeys(const std::vector<HotkeyType> keys)
void PressKeys(const std::vector<HotkeyType> keys, int duration)
{
if (!canSimulateKeyPresses) {
return;
@@ -545,7 +545,7 @@ void PressKeys(const std::vector<HotkeyType> keys)
}
XFlush(display);
std::this_thread::sleep_for(std::chrono::milliseconds(300));
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
// Release keys
for (auto &key : keys) {

View File

@@ -1,18 +1,196 @@
#include "headers/macro-action-hotkey.hpp"
#include "headers/advanced-scene-switcher.hpp"
#include "headers/utility.hpp"
#include <obs-interaction.h>
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
static std::unordered_map<HotkeyType, long> keyTable = {
// Chars
{HotkeyType::Key_A, OBS_KEY_A},
{HotkeyType::Key_B, OBS_KEY_B},
{HotkeyType::Key_C, OBS_KEY_C},
{HotkeyType::Key_D, OBS_KEY_D},
{HotkeyType::Key_E, OBS_KEY_E},
{HotkeyType::Key_F, OBS_KEY_F},
{HotkeyType::Key_G, OBS_KEY_G},
{HotkeyType::Key_H, OBS_KEY_H},
{HotkeyType::Key_I, OBS_KEY_I},
{HotkeyType::Key_J, OBS_KEY_J},
{HotkeyType::Key_K, OBS_KEY_K},
{HotkeyType::Key_L, OBS_KEY_L},
{HotkeyType::Key_M, OBS_KEY_M},
{HotkeyType::Key_N, OBS_KEY_N},
{HotkeyType::Key_O, OBS_KEY_O},
{HotkeyType::Key_P, OBS_KEY_P},
{HotkeyType::Key_Q, OBS_KEY_Q},
{HotkeyType::Key_R, OBS_KEY_R},
{HotkeyType::Key_S, OBS_KEY_S},
{HotkeyType::Key_T, OBS_KEY_T},
{HotkeyType::Key_U, OBS_KEY_U},
{HotkeyType::Key_V, OBS_KEY_V},
{HotkeyType::Key_W, OBS_KEY_W},
{HotkeyType::Key_X, OBS_KEY_X},
{HotkeyType::Key_Y, OBS_KEY_Y},
{HotkeyType::Key_Z, OBS_KEY_Z},
// Numbers
{HotkeyType::Key_0, OBS_KEY_0},
{HotkeyType::Key_1, OBS_KEY_1},
{HotkeyType::Key_2, OBS_KEY_2},
{HotkeyType::Key_3, OBS_KEY_3},
{HotkeyType::Key_4, OBS_KEY_4},
{HotkeyType::Key_5, OBS_KEY_5},
{HotkeyType::Key_6, OBS_KEY_6},
{HotkeyType::Key_7, OBS_KEY_7},
{HotkeyType::Key_8, OBS_KEY_8},
{HotkeyType::Key_9, OBS_KEY_9},
{HotkeyType::Key_F1, OBS_KEY_F1},
{HotkeyType::Key_F2, OBS_KEY_F2},
{HotkeyType::Key_F3, OBS_KEY_F3},
{HotkeyType::Key_F4, OBS_KEY_F4},
{HotkeyType::Key_F5, OBS_KEY_F5},
{HotkeyType::Key_F6, OBS_KEY_F6},
{HotkeyType::Key_F7, OBS_KEY_F7},
{HotkeyType::Key_F8, OBS_KEY_F8},
{HotkeyType::Key_F9, OBS_KEY_F9},
{HotkeyType::Key_F10, OBS_KEY_F10},
{HotkeyType::Key_F11, OBS_KEY_F11},
{HotkeyType::Key_F12, OBS_KEY_F12},
{HotkeyType::Key_F13, OBS_KEY_F13},
{HotkeyType::Key_F14, OBS_KEY_F14},
{HotkeyType::Key_F15, OBS_KEY_F15},
{HotkeyType::Key_F16, OBS_KEY_F16},
{HotkeyType::Key_F17, OBS_KEY_F17},
{HotkeyType::Key_F18, OBS_KEY_F18},
{HotkeyType::Key_F19, OBS_KEY_F19},
{HotkeyType::Key_F20, OBS_KEY_F20},
{HotkeyType::Key_F21, OBS_KEY_F21},
{HotkeyType::Key_F22, OBS_KEY_F22},
{HotkeyType::Key_F23, OBS_KEY_F23},
{HotkeyType::Key_F24, OBS_KEY_F24},
{HotkeyType::Key_Escape, OBS_KEY_ESCAPE},
{HotkeyType::Key_Space, OBS_KEY_SPACE},
{HotkeyType::Key_Return, OBS_KEY_RETURN},
{HotkeyType::Key_Backspace, OBS_KEY_BACKSPACE},
{HotkeyType::Key_Tab, OBS_KEY_TAB},
{HotkeyType::Key_Shift_L, OBS_KEY_SHIFT},
{HotkeyType::Key_Shift_R, OBS_KEY_SHIFT},
{HotkeyType::Key_Control_L, OBS_KEY_CONTROL},
{HotkeyType::Key_Control_R, OBS_KEY_CONTROL},
{HotkeyType::Key_Alt_L, OBS_KEY_ALT},
{HotkeyType::Key_Alt_R, OBS_KEY_ALT},
{HotkeyType::Key_Win_L, OBS_KEY_META},
{HotkeyType::Key_Win_R, OBS_KEY_META},
{HotkeyType::Key_Apps, OBS_KEY_APPLICATIONLEFT},
{HotkeyType::Key_CapsLock, OBS_KEY_CAPSLOCK},
{HotkeyType::Key_NumLock, OBS_KEY_NUMLOCK},
{HotkeyType::Key_ScrollLock, OBS_KEY_SCROLLLOCK},
{HotkeyType::Key_PrintScreen, OBS_KEY_PRINT},
{HotkeyType::Key_Pause, OBS_KEY_PAUSE},
{HotkeyType::Key_Insert, OBS_KEY_INSERT},
{HotkeyType::Key_Delete, OBS_KEY_DELETE},
{HotkeyType::Key_PageUP, OBS_KEY_PAGEUP},
{HotkeyType::Key_PageDown, OBS_KEY_PAGEDOWN},
{HotkeyType::Key_Home, OBS_KEY_HOME},
{HotkeyType::Key_End, OBS_KEY_END},
{HotkeyType::Key_Left, OBS_KEY_LEFT},
{HotkeyType::Key_Up, OBS_KEY_UP},
{HotkeyType::Key_Right, OBS_KEY_RIGHT},
{HotkeyType::Key_Down, OBS_KEY_DOWN},
{HotkeyType::Key_Numpad0, OBS_KEY_NUM0},
{HotkeyType::Key_Numpad1, OBS_KEY_NUM1},
{HotkeyType::Key_Numpad2, OBS_KEY_NUM2},
{HotkeyType::Key_Numpad3, OBS_KEY_NUM3},
{HotkeyType::Key_Numpad4, OBS_KEY_NUM4},
{HotkeyType::Key_Numpad5, OBS_KEY_NUM5},
{HotkeyType::Key_Numpad6, OBS_KEY_NUM6},
{HotkeyType::Key_Numpad7, OBS_KEY_NUM7},
{HotkeyType::Key_Numpad8, OBS_KEY_NUM8},
{HotkeyType::Key_Numpad9, OBS_KEY_NUM9},
{HotkeyType::Key_NumpadAdd, OBS_KEY_NUMPLUS},
{HotkeyType::Key_NumpadSubtract, OBS_KEY_NUMMINUS},
{HotkeyType::Key_NumpadMultiply, OBS_KEY_NUMASTERISK},
{HotkeyType::Key_NumpadDivide, OBS_KEY_NUMSLASH},
{HotkeyType::Key_NumpadDecimal, OBS_KEY_NUMPERIOD},
//{HotkeyType::Key_NumpadEnter, ???},
};
obs_key_combination keysToOBSKeycombo(const std::vector<HotkeyType> &keys)
{
obs_key_combination combo{};
auto it = keyTable.find(keys.back());
if (it != keyTable.end()) {
combo.key = (obs_key_t)it->second;
}
if (keys.size() == 1) {
return combo;
}
for (int i = 0; i < keys.size() - 1; i++) {
switch (keys[i]) {
case HotkeyType::Key_Shift_L:
combo.modifiers |= INTERACT_SHIFT_KEY;
break;
case HotkeyType::Key_Shift_R:
combo.modifiers |= INTERACT_SHIFT_KEY;
break;
case HotkeyType::Key_Control_L:
combo.modifiers |= INTERACT_CONTROL_KEY;
break;
case HotkeyType::Key_Control_R:
combo.modifiers |= INTERACT_CONTROL_KEY;
break;
case HotkeyType::Key_Alt_L:
combo.modifiers |= INTERACT_ALT_KEY;
break;
case HotkeyType::Key_Alt_R:
combo.modifiers |= INTERACT_ALT_KEY;
break;
case HotkeyType::Key_Win_L:
combo.modifiers |= INTERACT_COMMAND_KEY;
break;
case HotkeyType::Key_Win_R:
combo.modifiers |= INTERACT_COMMAND_KEY;
break;
case HotkeyType::Key_CapsLock:
combo.modifiers |= INTERACT_CAPS_KEY;
break;
default:
break;
}
}
return combo;
}
void InjectKeys(const std::vector<HotkeyType> &keys, int duration)
{
auto combo = keysToOBSKeycombo(keys);
if (obs_key_combination_is_empty(combo)) {
return;
}
// I am not sure why this is necessary
obs_hotkey_inject_event(combo, false);
obs_hotkey_inject_event(combo, true);
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
obs_hotkey_inject_event(combo, false);
}
bool MacroActionHotkey::PerformAction()
{
@@ -35,13 +213,25 @@ bool MacroActionHotkey::PerformAction()
if (_rightAlt) {
keys.push_back(HotkeyType::Key_Alt_R);
}
if (_leftMeta) {
keys.push_back(HotkeyType::Key_Win_L);
}
if (_rightMeta) {
keys.push_back(HotkeyType::Key_Win_R);
}
if (_key != HotkeyType::Key_NoKey) {
keys.push_back(_key);
}
if (!keys.empty()) {
std::thread t([keys]() { PressKeys(keys); });
t.detach();
int dur = _duration;
if (_onlySendToObs) {
std::thread t([keys, dur]() { InjectKeys(keys, dur); });
t.detach();
} else {
std::thread t([keys, dur]() { PressKeys(keys, dur); });
t.detach();
}
}
return true;
@@ -64,6 +254,8 @@ bool MacroActionHotkey::Save(obs_data_t *obj)
obs_data_set_bool(obj, "right_alt", _rightAlt);
obs_data_set_bool(obj, "left_meta", _leftMeta);
obs_data_set_bool(obj, "right_meta", _rightMeta);
obs_data_set_int(obj, "duration", _duration);
obs_data_set_bool(obj, "onlyOBS", _onlySendToObs);
return true;
}
@@ -79,6 +271,8 @@ bool MacroActionHotkey::Load(obs_data_t *obj)
_rightAlt = obs_data_get_bool(obj, "right_alt");
_leftMeta = obs_data_get_bool(obj, "left_meta");
_rightMeta = obs_data_get_bool(obj, "right_meta");
_duration = obs_data_get_int(obj, "duration");
_onlySendToObs = obs_data_get_bool(obj, "onlyOBS");
return true;
}
@@ -213,6 +407,12 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
obs_module_text("AdvSceneSwitcher.action.hotkey.leftMeta"));
_rightMeta = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.rightMeta"));
_duration = new QSpinBox();
_duration->setMaximum(5000);
_onlySendToOBS = new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.hotkey.onlyOBS"));
_noKeyPressSimulationWarning = new QLabel(
obs_module_text("AdvSceneSwitcher.action.hotkey.disabled"));
populateKeySelection(_keys);
@@ -234,10 +434,15 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
SLOT(LMetaChanged(int)));
QWidget::connect(_rightMeta, SIGNAL(stateChanged(int)), this,
SLOT(RMetaChanged(int)));
QWidget::connect(_duration, SIGNAL(valueChanged(int)), this,
SLOT(DurationChanged(int)));
QWidget::connect(_onlySendToOBS, SIGNAL(stateChanged(int)), this,
SLOT(OnlySendToOBSChanged(int)));
QHBoxLayout *line1Layout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{keys}}", _keys},
{"{{duration}}", _duration},
};
placeWidgets(obs_module_text("AdvSceneSwitcher.action.hotkey.entry"),
line1Layout, widgetPlaceholders);
@@ -256,11 +461,8 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
if (!canSimulateKeyPresses) {
mainLayout->addWidget(new QLabel(obs_module_text(
"AdvSceneSwitcher.action.hotkey.disabled")));
}
mainLayout->addWidget(_onlySendToOBS);
mainLayout->addWidget(_noKeyPressSimulationWarning);
setLayout(mainLayout);
@@ -269,6 +471,12 @@ MacroActionHotkeyEdit::MacroActionHotkeyEdit(
_loading = false;
}
void MacroActionHotkeyEdit::SetWarningVisibility()
{
_noKeyPressSimulationWarning->setVisible(!_entryData->_onlySendToObs &&
!canSimulateKeyPresses);
}
void MacroActionHotkeyEdit::UpdateEntryData()
{
if (!_entryData) {
@@ -284,6 +492,9 @@ void MacroActionHotkeyEdit::UpdateEntryData()
_rightAlt->setChecked(_entryData->_rightAlt);
_leftMeta->setChecked(_entryData->_leftMeta);
_rightMeta->setChecked(_entryData->_rightMeta);
_duration->setValue(_entryData->_duration);
_onlySendToOBS->setChecked(_entryData->_onlySendToObs);
SetWarningVisibility();
}
void MacroActionHotkeyEdit::LShiftChanged(int state)
@@ -366,6 +577,27 @@ void MacroActionHotkeyEdit::RMetaChanged(int state)
_entryData->_rightMeta = state;
}
void MacroActionHotkeyEdit::DurationChanged(int ms)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_duration = ms;
}
void MacroActionHotkeyEdit::OnlySendToOBSChanged(int state)
{
if (_loading || !_entryData) {
return;
}
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_onlySendToObs = state;
SetWarningVisibility();
}
void MacroActionHotkeyEdit::KeyChanged(int key)
{
if (_loading || !_entryData) {

View File

@@ -405,7 +405,7 @@ static std::map<HotkeyType, CGKeyCode> keyTable = {
{HotkeyType::Key_NumpadEnter, kVK_ANSI_KeypadEnter},
};
void PressKeys(const std::vector<HotkeyType> keys)
void PressKeys(const std::vector<HotkeyType> keys, int duration)
{
// TODO:
// I can't seem to get this to work so drop support for this functionality

View File

@@ -391,7 +391,7 @@ static std::unordered_map<HotkeyType, long> keyTable = {
{HotkeyType::Key_NumpadEnter, VK_RETURN},
};
void PressKeys(const std::vector<HotkeyType> keys)
void PressKeys(const std::vector<HotkeyType> keys, int duration)
{
INPUT ip;
ip.type = INPUT_KEYBOARD;
@@ -411,7 +411,7 @@ void PressKeys(const std::vector<HotkeyType> keys)
}
// When instantly releasing the key presses OBS might miss them
Sleep(300);
Sleep(duration);
// Release keys
ip.ki.dwFlags = KEYEVENTF_KEYUP;