Skip to content

Commit ea76bd9

Browse files
committed
Disable commands when config windows is open, and added test mode
1 parent 208e531 commit ea76bd9

6 files changed

Lines changed: 174 additions & 8 deletions

File tree

data/locale/en-US.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ JoypadToOBS.Field.AxisBothDirections="Use both directions (+/-)"
2222
JoypadToOBS.Field.AxisMinValue="Axis min"
2323
JoypadToOBS.Field.AxisMaxValue="Axis max"
2424
JoypadToOBS.Field.InvertAxis="Invert Axis"
25+
JoypadToOBS.Field.TestMode="Test mode"
2526
JoypadToOBS.Button.SetMin="Set min"
2627
JoypadToOBS.Button.SetMax="Set max"
2728
JoypadToOBS.AxisThreshold.Any="Any movement (> 0)"

data/locale/pt-BR.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ JoypadToOBS.Field.AxisBothDirections="Usar ambas direções (+/-)"
2222
JoypadToOBS.Field.AxisMinValue="Mínimo do eixo"
2323
JoypadToOBS.Field.AxisMaxValue="Máximo do eixo"
2424
JoypadToOBS.Field.InvertAxis="Inverter Eixo"
25+
JoypadToOBS.Field.TestMode="Modo de teste"
2526
JoypadToOBS.Button.SetMin="Definir mín."
2627
JoypadToOBS.Button.SetMax="Definir máx."
2728
JoypadToOBS.AxisThreshold.Any="Qualquer movimento (> 0)"

data/locale/pt-PT.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ JoypadToOBS.Field.AxisBothDirections="Usar ambas direções (+/-)"
2222
JoypadToOBS.Field.AxisMinValue="Mínimo do eixo"
2323
JoypadToOBS.Field.AxisMaxValue="Máximo do eixo"
2424
JoypadToOBS.Field.InvertAxis="Inverter Eixo"
25+
JoypadToOBS.Field.TestMode="Modo de teste"
2526
JoypadToOBS.Button.SetMin="Definir mín."
2627
JoypadToOBS.Button.SetMax="Definir máx."
2728
JoypadToOBS.AxisThreshold.Any="Qualquer movimento (> 0)"

src/joypad-plugin.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,21 +247,31 @@ bool obs_module_load(void)
247247
});
248248

249249
g_input.SetOnButtonPressed([](const JoypadEvent &event) {
250+
if (JoypadUiEmulateBindingDialogAction(event, &g_actions)) {
251+
return;
252+
}
253+
if (JoypadUiIsBindingDialogOpen()) {
254+
return;
255+
}
250256
auto matches = g_config.FindMatchingBindings(event);
251257
for (const auto &binding : matches) {
252258
g_actions.Execute(binding);
253259
}
254260
});
255261
g_input.SetOnAxisChanged([](const JoypadEvent &event) {
262+
if (JoypadUiEmulateBindingDialogAction(event, &g_actions)) {
263+
return;
264+
}
265+
if (JoypadUiIsBindingDialogOpen()) {
266+
return;
267+
}
256268
if (!event.is_axis) {
257269
return;
258270
}
259271
auto matches = g_config.FindMatchingBindings(event);
260272
if (matches.empty()) {
261273
return;
262274
}
263-
obs_log(LOG_INFO, "axis raw: device=%s axis=%d value=%.3f", event.device_name.c_str(), event.axis_index,
264-
event.axis_raw_value);
265275
for (const auto &binding : matches) {
266276
g_actions.Execute(binding);
267277
if (binding.action == JoypadActionType::SetSourceVolumePercent) {

src/joypad-ui.cpp

Lines changed: 155 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
1717
*/
1818

1919
#include "joypad-ui.h"
20+
#include "joypad-actions.h"
2021

2122
#include <obs-frontend-api.h>
2223
#include <obs-module.h>
@@ -54,14 +55,24 @@ with this program. If not, see <https://www.gnu.org/licenses/>
5455
#include <QSpinBox>
5556

5657
#include <algorithm>
58+
#include <atomic>
5759
#include <cmath>
60+
#include <mutex>
5861
#include <vector>
5962

6063
namespace {
6164
constexpr int kDeviceIdRole = Qt::UserRole;
6265
constexpr int kDeviceStableIdRole = Qt::UserRole + 1;
6366
constexpr int kDeviceTypeIdRole = Qt::UserRole + 2;
6467

68+
std::atomic<int> g_binding_dialog_open_count{0};
69+
struct DialogTestState {
70+
bool enabled = false;
71+
JoypadBinding binding;
72+
};
73+
std::mutex g_dialog_test_mutex;
74+
DialogTestState g_dialog_test_state;
75+
6576
inline QString L(const char *key)
6677
{
6778
return QString::fromUtf8(obs_module_text(key));
@@ -339,6 +350,26 @@ QString input_label_from_binding(const JoypadBinding &binding)
339350
return L("JoypadToOBS.Common.ButtonNumber").arg(binding.button);
340351
}
341352

353+
double map_axis_raw_to_percent_for_test(const JoypadBinding &binding, double raw)
354+
{
355+
double minv = binding.axis_min_value;
356+
double maxv = binding.axis_max_value;
357+
if (maxv <= minv) {
358+
minv = 0.0;
359+
maxv = 1024.0;
360+
}
361+
double percent = ((raw - minv) / (maxv - minv)) * 100.0;
362+
if (binding.axis_inverted) {
363+
percent = 100.0 - percent;
364+
}
365+
percent = std::clamp(percent, 0.0, 100.0);
366+
double base = std::clamp(percent / 100.0, 0.0, 1.0);
367+
double gamma = binding.slider_gamma > 0.0 ? binding.slider_gamma : 0.6;
368+
gamma = std::clamp(gamma, 0.1, 50.0);
369+
double curved = std::pow(base, gamma);
370+
return std::clamp(curved * 100.0, 0.0, 100.0);
371+
}
372+
342373
class JoypadBindingDialog : public QDialog {
343374
public:
344375
JoypadBindingDialog(QWidget *parent, JoypadConfigStore *config, JoypadInputManager *input,
@@ -348,6 +379,7 @@ class JoypadBindingDialog : public QDialog {
348379
input_(input),
349380
existing_(existing)
350381
{
382+
g_binding_dialog_open_count.fetch_add(1, std::memory_order_relaxed);
351383
setWindowTitle(L("JoypadToOBS.Dialog.AddTitle"));
352384
setModal(true);
353385

@@ -476,6 +508,7 @@ class JoypadBindingDialog : public QDialog {
476508
volume_spin_ = new QDoubleSpinBox(action_group);
477509
volume_allow_above_unity_ = new QCheckBox(L("JoypadToOBS.Field.AllowAboveDb"), action_group);
478510
invert_axis_checkbox_ = new QCheckBox(L("JoypadToOBS.Field.InvertAxis"), action_group);
511+
test_mode_checkbox_ = new QCheckBox(L("JoypadToOBS.Field.TestMode"), action_group);
479512
volume_spin_->setRange(-60.0, 20.0);
480513
volume_spin_->setSingleStep(1.0);
481514
volume_spin_->setValue(0.0);
@@ -489,6 +522,7 @@ class JoypadBindingDialog : public QDialog {
489522
action_layout->addWidget(volume_spin_, 2, 1);
490523
action_layout->addWidget(volume_allow_above_unity_, 3, 0, 1, 2);
491524
action_layout->addWidget(invert_axis_checkbox_, 4, 0, 1, 2);
525+
action_layout->addWidget(test_mode_checkbox_, 5, 0, 1, 2);
492526

493527
layout->addWidget(action_group);
494528
layout->addWidget(target_group);
@@ -498,8 +532,19 @@ class JoypadBindingDialog : public QDialog {
498532

499533
connect(buttons, &QDialogButtonBox::accepted, this, &JoypadBindingDialog::accept);
500534
connect(buttons, &QDialogButtonBox::rejected, this, &JoypadBindingDialog::reject);
535+
connect(test_mode_checkbox_, &QCheckBox::toggled, this, [this](bool) { PublishTestBinding(); });
501536
connect(listen_button_, &QPushButton::clicked, this, &JoypadBindingDialog::OnListen);
502537
connect(action_combo_, &QComboBox::currentIndexChanged, this, &JoypadBindingDialog::UpdateActionUi);
538+
connect(action_combo_, &QComboBox::currentIndexChanged, this, [this](int) { PublishTestBinding(); });
539+
connect(device_combo_, &QComboBox::currentIndexChanged, this, [this](int) { PublishTestBinding(); });
540+
connect(scene_combo_, &QComboBox::currentIndexChanged, this, [this](int) { PublishTestBinding(); });
541+
connect(source_combo_, &QComboBox::currentIndexChanged, this, [this](int) { PublishTestBinding(); });
542+
connect(filter_combo_, &QComboBox::currentIndexChanged, this, [this](int) { PublishTestBinding(); });
543+
connect(bool_checkbox_, &QCheckBox::toggled, this, [this](bool) { PublishTestBinding(); });
544+
connect(use_current_scene_, &QCheckBox::toggled, this, [this](bool) { PublishTestBinding(); });
545+
connect(axis_both_checkbox_, &QCheckBox::toggled, this, [this](bool) { PublishTestBinding(); });
546+
connect(axis_threshold_combo_, &QComboBox::currentIndexChanged, this,
547+
[this](int) { PublishTestBinding(); });
503548
connect(volume_spin_, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, [this](double value) {
504549
if (CurrentAction() != JoypadActionType::SetSourceVolumePercent) {
505550
return;
@@ -513,9 +558,11 @@ class JoypadBindingDialog : public QDialog {
513558
axis_value_slider_->setValue((int)percent);
514559
axis_live_value_label_->setText(L("JoypadToOBS.Common.PercentValue").arg(percent, 0, 'f', 0) +
515560
" " + L("JoypadToOBS.Common.DbValue").arg(db, 0, 'f', 1));
561+
PublishTestBinding();
516562
});
517563
connect(invert_axis_checkbox_, &QCheckBox::toggled, this, [this](bool) {
518564
if (CurrentAction() != JoypadActionType::SetSourceVolumePercent) {
565+
PublishTestBinding();
519566
return;
520567
}
521568
if (!learned_event_.is_axis) {
@@ -526,19 +573,24 @@ class JoypadBindingDialog : public QDialog {
526573
axis_value_slider_->setValue((int)percent);
527574
axis_live_value_label_->setText(L("JoypadToOBS.Common.PercentValue").arg(percent, 0, 'f', 0) +
528575
" " + L("JoypadToOBS.Common.DbValue").arg(db, 0, 'f', 1));
576+
PublishTestBinding();
577+
});
578+
connect(volume_allow_above_unity_, &QCheckBox::toggled, this, [this](bool checked) {
579+
binding_.allow_above_unity = checked;
580+
PublishTestBinding();
529581
});
530-
connect(volume_allow_above_unity_, &QCheckBox::toggled, this,
531-
[this](bool checked) { binding_.allow_above_unity = checked; });
532582
connect(source_combo_, &QComboBox::currentIndexChanged, this, &JoypadBindingDialog::ReloadFilters);
533583
connect(axis_set_min_button_, &QPushButton::clicked, this, [this]() {
534584
binding_.axis_min_value = last_axis_value_;
535585
axis_min_label_->setText(L("JoypadToOBS.Field.AxisMinValue") + ": " +
536586
QString::number(binding_.axis_min_value, 'f', 2));
587+
PublishTestBinding();
537588
});
538589
connect(axis_set_max_button_, &QPushButton::clicked, this, [this]() {
539590
binding_.axis_max_value = last_axis_value_;
540591
axis_max_label_->setText(L("JoypadToOBS.Field.AxisMaxValue") + ": " +
541592
QString::number(binding_.axis_max_value, 'f', 2));
593+
PublishTestBinding();
542594
});
543595
if (input_) {
544596
axis_handler_id_ = input_->AddOnAxisChanged([this](const JoypadEvent &event) {
@@ -604,13 +656,18 @@ class JoypadBindingDialog : public QDialog {
604656
UpdateActionUi();
605657
UpdateAxisUi(false);
606658
}
659+
PublishTestBinding();
607660
refresh_timer_ = new QTimer(this);
608661
connect(refresh_timer_, &QTimer::timeout, this, &JoypadBindingDialog::RefreshDeviceList);
609662
refresh_timer_->start(1000);
610663
}
611664

612665
~JoypadBindingDialog() override
613666
{
667+
{
668+
std::lock_guard<std::mutex> lock(g_dialog_test_mutex);
669+
g_dialog_test_state.enabled = false;
670+
}
614671
if (refresh_timer_) {
615672
refresh_timer_->stop();
616673
refresh_timer_ = nullptr;
@@ -622,20 +679,43 @@ class JoypadBindingDialog : public QDialog {
622679
axis_handler_id_ = 0;
623680
}
624681
}
682+
g_binding_dialog_open_count.fetch_sub(1, std::memory_order_relaxed);
625683
}
626684

627685
JoypadBinding Binding() const { return binding_; }
628686

629687
protected:
630688
void accept() override
631689
{
632-
if (!ReadBinding()) {
690+
if (!ReadBinding(true)) {
633691
return;
634692
}
635693
QDialog::accept();
636694
}
637695

638696
private:
697+
void PublishTestBinding()
698+
{
699+
if (!test_mode_checkbox_ || !test_mode_checkbox_->isChecked()) {
700+
std::lock_guard<std::mutex> lock(g_dialog_test_mutex);
701+
g_dialog_test_state.enabled = false;
702+
return;
703+
}
704+
if (!ReadBinding(false)) {
705+
std::lock_guard<std::mutex> lock(g_dialog_test_mutex);
706+
g_dialog_test_state.enabled = false;
707+
return;
708+
}
709+
JoypadBinding test = binding_;
710+
if (test.action == JoypadActionType::SetSourceVolumePercent) {
711+
double percent = learned_event_.is_axis ? MapRawToPercent(last_axis_value_) : 50.0;
712+
test.volume_value = std::clamp(percent, 0.0, 100.0);
713+
}
714+
std::lock_guard<std::mutex> lock(g_dialog_test_mutex);
715+
g_dialog_test_state.enabled = true;
716+
g_dialog_test_state.binding = test;
717+
}
718+
639719
void RefreshDeviceList()
640720
{
641721
if (!input_) {
@@ -1058,6 +1138,7 @@ class JoypadBindingDialog : public QDialog {
10581138
}
10591139
}
10601140
SelectDevice(event);
1141+
PublishTestBinding();
10611142
},
10621143
Qt::QueuedConnection);
10631144
});
@@ -1085,9 +1166,9 @@ class JoypadBindingDialog : public QDialog {
10851166
}
10861167
}
10871168

1088-
bool ReadBinding()
1169+
bool ReadBinding(bool require_input)
10891170
{
1090-
if (!learned_event_.is_axis && learned_event_.button <= 0) {
1171+
if (require_input && !learned_event_.is_axis && learned_event_.button <= 0) {
10911172
button_label_->setText(L("JoypadToOBS.Common.PressButtonOrAxisFirst"));
10921173
return false;
10931174
}
@@ -1098,7 +1179,8 @@ class JoypadBindingDialog : public QDialog {
10981179
binding_.device_type_id = device_combo_->currentData(kDeviceTypeIdRole).toString().toStdString();
10991180
binding_.device_name = device_combo_->currentText().toStdString();
11001181

1101-
if (CurrentAction() == JoypadActionType::SetSourceVolumePercent && !learned_event_.is_axis) {
1182+
if (require_input && CurrentAction() == JoypadActionType::SetSourceVolumePercent &&
1183+
!learned_event_.is_axis) {
11021184
button_label_->setText(L("JoypadToOBS.Common.AxisOnlyForSlider"));
11031185
return false;
11041186
}
@@ -1242,13 +1324,80 @@ class JoypadBindingDialog : public QDialog {
12421324
QDoubleSpinBox *volume_spin_ = nullptr;
12431325
QCheckBox *volume_allow_above_unity_ = nullptr;
12441326
QCheckBox *invert_axis_checkbox_ = nullptr;
1327+
QCheckBox *test_mode_checkbox_ = nullptr;
12451328
int axis_handler_id_ = 0;
12461329
QTimer *refresh_timer_ = nullptr;
12471330
bool is_listening_ = false;
12481331
};
12491332

12501333
} // namespace
12511334

1335+
bool JoypadUiIsBindingDialogOpen()
1336+
{
1337+
return g_binding_dialog_open_count.load(std::memory_order_relaxed) > 0;
1338+
}
1339+
1340+
bool JoypadUiEmulateBindingDialogAction(const JoypadEvent &event, JoypadActionEngine *actions)
1341+
{
1342+
DialogTestState state;
1343+
{
1344+
std::lock_guard<std::mutex> lock(g_dialog_test_mutex);
1345+
if (!g_dialog_test_state.enabled) {
1346+
return false;
1347+
}
1348+
state = g_dialog_test_state;
1349+
}
1350+
1351+
if (!actions) {
1352+
return true;
1353+
}
1354+
1355+
const JoypadBinding &binding = state.binding;
1356+
JoypadBinding adjusted = binding;
1357+
1358+
if (binding.input_type == JoypadInputType::Button) {
1359+
if (event.is_axis) {
1360+
return true;
1361+
}
1362+
if (binding.button > 0 && event.button != binding.button) {
1363+
return true;
1364+
}
1365+
actions->Execute(adjusted);
1366+
return true;
1367+
}
1368+
1369+
if (!event.is_axis) {
1370+
return true;
1371+
}
1372+
if (binding.axis_index >= 0 && event.axis_index != binding.axis_index) {
1373+
return true;
1374+
}
1375+
1376+
double value = binding.axis_inverted ? -event.axis_value : event.axis_value;
1377+
double abs_value = std::fabs(value);
1378+
if (binding.action == JoypadActionType::SetSourceVolumePercent) {
1379+
adjusted.volume_value = map_axis_raw_to_percent_for_test(binding, event.axis_raw_value);
1380+
actions->Execute(adjusted);
1381+
return true;
1382+
}
1383+
if (binding.axis_direction != JoypadAxisDirection::Both) {
1384+
int dir = value >= 0.0 ? 1 : -1;
1385+
if (dir != (int)binding.axis_direction) {
1386+
return true;
1387+
}
1388+
}
1389+
if (abs_value < binding.axis_threshold) {
1390+
return true;
1391+
}
1392+
if (binding.action == JoypadActionType::AdjustSourceVolume) {
1393+
double sign = value >= 0.0 ? 1.0 : -1.0;
1394+
double intensity = std::clamp(abs_value, 0.0, 1.0);
1395+
adjusted.volume_value = std::fabs(binding.volume_value) * intensity * sign;
1396+
}
1397+
actions->Execute(adjusted);
1398+
return true;
1399+
}
1400+
12521401
JoypadToolsDialog::JoypadToolsDialog(QWidget *parent, JoypadConfigStore *config, JoypadInputManager *input)
12531402
: QDialog(parent),
12541403
config_(config),

src/joypad-ui.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class QLabel;
3030
class QComboBox;
3131
class QTimer;
3232
class QPlainTextEdit;
33+
class JoypadActionEngine;
3334

3435
class JoypadToolsDialog : public QDialog {
3536
public:
@@ -56,3 +57,6 @@ class JoypadToolsDialog : public QDialog {
5657
QTimer *update_timer_ = nullptr;
5758
QTimer *comment_debounce_timer_ = nullptr;
5859
};
60+
61+
bool JoypadUiIsBindingDialogOpen();
62+
bool JoypadUiEmulateBindingDialogAction(const JoypadEvent &event, JoypadActionEngine *actions);

0 commit comments

Comments
 (0)