Skip to content

Commit c22f2c9

Browse files
authored
SnapshotDumper: trigger snapshot with keypress (#796)
* SnapshotDumper: trigger snapshot with keypress * cleanup and renaming * UI updates to SnapshotDumper * fix pimpl
1 parent 1a333f5 commit c22f2c9

File tree

9 files changed

+151
-35
lines changed

9 files changed

+151
-35
lines changed

SerialPrograms/Source/CommonFramework/VideoPipeline/UI/VideoDisplayWidget.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,15 @@ void VideoDisplayWidget::mouseMoveEvent(QMouseEvent* event){
231231
m_overlay_session.issue_mouse_move(x, y);
232232
}
233233

234+
void VideoDisplayWidget::on_key_press(QKeyEvent* event){
235+
m_overlay_session.issue_key_press(event);
236+
}
237+
238+
void VideoDisplayWidget::on_key_release(QKeyEvent* event){
239+
m_overlay_session.issue_key_release(event);
240+
}
241+
242+
234243
OverlayStatSnapshot VideoSourceFPS::get_current(){
235244
double fps = m_parent.m_video_session.fps_source();
236245
return OverlayStatSnapshot{

SerialPrograms/Source/CommonFramework/VideoPipeline/UI/VideoDisplayWidget.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ class VideoDisplayWidget : public WidgetStackFixedAspectRatio, private VideoSess
103103
void move_to_new_window();
104104
void move_back_from_window();
105105

106+
// likely triggered by SwitchSystemWidget::key_press().
107+
// this forwards the QKeyEvent to VideoOverlaySession::issue_key_press().
108+
void on_key_press(QKeyEvent* event);
109+
void on_key_release(QKeyEvent* event);
110+
106111
protected:
107112
virtual void post_startup(VideoSource* source) override;
108113
virtual void pre_shutdown() override;

SerialPrograms/Source/CommonFramework/VideoPipeline/VideoOverlay.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ namespace PokemonAutomation{
1313

1414

1515
struct VideoOverlay::Data{
16-
ListenerSet<MouseListener> m_listeners;
16+
ListenerSet<MouseListener> m_mouseevent_listeners;
17+
ListenerSet<KeyEventListener> m_keyevent_listeners;
1718
};
1819

1920

@@ -24,23 +25,38 @@ VideoOverlay::~VideoOverlay() = default;
2425

2526

2627

27-
void VideoOverlay::add_listener(MouseListener& listener){
28-
m_data->m_listeners.add(listener);
28+
void VideoOverlay::add_mouse_listener(MouseListener& listener){
29+
m_data->m_mouseevent_listeners.add(listener);
2930
}
30-
void VideoOverlay::remove_listener(MouseListener& listener){
31-
m_data->m_listeners.remove(listener);
31+
void VideoOverlay::remove_mouse_listener(MouseListener& listener){
32+
m_data->m_mouseevent_listeners.remove(listener);
3233
}
3334
void VideoOverlay::issue_mouse_press(double x, double y){
34-
m_data->m_listeners.run_method_unique(&MouseListener::on_mouse_press, x, y);
35+
m_data->m_mouseevent_listeners.run_method_unique(&MouseListener::on_mouse_press, x, y);
3536
}
3637
void VideoOverlay::issue_mouse_release(double x, double y){
37-
m_data->m_listeners.run_method_unique(&MouseListener::on_mouse_release, x, y);
38+
m_data->m_mouseevent_listeners.run_method_unique(&MouseListener::on_mouse_release, x, y);
3839
}
3940
void VideoOverlay::issue_mouse_move(double x, double y){
40-
m_data->m_listeners.run_method_unique(&MouseListener::on_mouse_move, x, y);
41+
m_data->m_mouseevent_listeners.run_method_unique(&MouseListener::on_mouse_move, x, y);
4142
}
4243

4344

45+
void VideoOverlay::add_keyevent_listener(KeyEventListener& listener){
46+
m_data->m_keyevent_listeners.add(listener);
47+
}
48+
void VideoOverlay::remove_keyevent_listener(KeyEventListener& listener){
49+
m_data->m_keyevent_listeners.remove(listener);
50+
}
51+
void VideoOverlay::issue_key_press(QKeyEvent* event){
52+
m_data->m_keyevent_listeners.run_method_unique(&KeyEventListener::on_key_press, event);
53+
}
54+
void VideoOverlay::issue_key_release(QKeyEvent* event){
55+
m_data->m_keyevent_listeners.run_method_unique(&KeyEventListener::on_key_release, event);
56+
}
57+
58+
59+
4460

4561

4662

SerialPrograms/Source/CommonFramework/VideoPipeline/VideoOverlay.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "Common/Cpp/Containers/Pimpl.h"
1414
#include "VideoOverlayTypes.h"
1515

16+
class QKeyEvent;
17+
1618
namespace PokemonAutomation{
1719

1820
// Interface to add overlay objects (e.g. bounding boxes and texts) on top of rendered
@@ -82,15 +84,30 @@ class VideoOverlay{
8284
virtual void on_mouse_release(double x, double y){};
8385
virtual void on_mouse_move(double x, double y){};
8486
};
85-
void add_listener(MouseListener& listener);
86-
void remove_listener(MouseListener& listener);
87+
void add_mouse_listener(MouseListener& listener);
88+
void remove_mouse_listener(MouseListener& listener);
8789
// Called by VideoDisplayWidget to call attached mouse listeners' on_mouse_press().
8890
void issue_mouse_press(double x, double y);
8991
// Called by VideoDisplayWidget to call attached mouse listeners' on_mouse_release().
9092
void issue_mouse_release(double x, double y);
9193
// Called by VideoDisplayWidget to call attached mouse listeners' on_mouse_move().
9294
void issue_mouse_move(double x, double y);
9395

96+
struct KeyEventListener{
97+
virtual void on_key_press(QKeyEvent* event){}
98+
virtual void on_key_release(QKeyEvent* event){};
99+
};
100+
void add_keyevent_listener(KeyEventListener& listener);
101+
void remove_keyevent_listener(KeyEventListener& listener);
102+
103+
// Keypresses are detected by SwitchSystemWidget::keyPressEvent, which overrides QWidget::keyPressEvent
104+
// this is then passed to SwitchSystemWidget::key_press. This then triggers the controller commands.
105+
// But we also want to eventually pass this event to VideoOverlay. So, we also trigger VideoDisplayWidget::on_key_press.
106+
// VideoDisplayWidget::on_key_press then passes the event to VideoOverlaySession::issue_key_press. VideoOverlaySession inherits VideoOverlay.
107+
// When issue_key_press is called, it triggers on_key_press in all KeyEventListeners.
108+
void issue_key_press(QKeyEvent* event);
109+
void issue_key_release(QKeyEvent* event);
110+
94111
private:
95112
struct Data;
96113
Pimpl<Data> m_data;

SerialPrograms/Source/ML/Programs/ML_LabelImagesWidget.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ QWidget* LabelImages::make_widget(QWidget& parent, PanelHolder& holder){
4040

4141

4242
LabelImages_Widget::~LabelImages_Widget(){
43-
m_display_session.overlay().remove_listener(*this);
43+
m_display_session.overlay().remove_mouse_listener(*this);
4444
m_display_session.video_session().remove_state_listener(*this);
4545

4646
delete m_image_display_widget;
@@ -54,7 +54,7 @@ LabelImages_Widget::LabelImages_Widget(
5454
, m_program(program)
5555
, m_display_session(m_program.m_display_session)
5656
{
57-
m_display_session.overlay().add_listener(*this);
57+
m_display_session.overlay().add_mouse_listener(*this);
5858
m_display_session.video_session().add_state_listener(*this);
5959

6060
m_embedding_info_label = new QLabel(this);

SerialPrograms/Source/NintendoSwitch/DevPrograms/BoxDraw.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class BoxDraw::DrawnBox : public ConfigOption::Listener, public VideoOverlay::Mo
6363
m_parent.WIDTH.add_listener(*this);
6464
m_parent.HEIGHT.add_listener(*this);
6565
m_parent.BOX_COORDINATES.add_listener(*this);
66-
overlay.add_listener(*this);
66+
overlay.add_mouse_listener(*this);
6767
}catch (...){
6868
detach();
6969
throw;
@@ -123,7 +123,7 @@ class BoxDraw::DrawnBox : public ConfigOption::Listener, public VideoOverlay::Mo
123123

124124
private:
125125
void detach(){
126-
m_overlay.remove_listener(*this);
126+
m_overlay.remove_mouse_listener(*this);
127127
m_parent.X.remove_listener(*this);
128128
m_parent.Y.remove_listener(*this);
129129
m_parent.WIDTH.remove_listener(*this);

SerialPrograms/Source/NintendoSwitch/Framework/UI/NintendoSwitch_SwitchSystemWidget.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,13 @@ void SwitchSystemWidget::update_ui(ProgramState state){
203203
void SwitchSystemWidget::key_press(QKeyEvent* event){
204204
// cout << "press: " << event->nativeVirtualKey() << endl;
205205
m_command->on_key_press(*event);
206+
m_video_display->on_key_press(event);
206207
}
207208

208209
void SwitchSystemWidget::key_release(QKeyEvent* event){
209210
// cout << "release: " << event->nativeVirtualKey() << endl;
210211
m_command->on_key_release(*event);
212+
m_video_display->on_key_release(event);
211213
}
212214

213215
void SwitchSystemWidget::focus_in(QFocusEvent* event){

SerialPrograms/Source/NintendoSwitch/Programs/NintendoSwitch_SnapshotDumper.cpp

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
#include <QDir>
8+
#include <QKeyEvent>
89
#include "Common/Cpp/PrettyPrint.h"
910
#include "CommonFramework/Globals.h"
1011
#include "CommonFramework/VideoPipeline/VideoFeed.h"
@@ -32,7 +33,7 @@ SnapshotDumper_Descriptor::SnapshotDumper_Descriptor()
3233
{}
3334

3435
SnapshotDumper::~SnapshotDumper(){
35-
CLICK_TO_SNAPSHOT.remove_listener(*this);
36+
SNAPSHOT_MODE.remove_listener(*this);
3637
}
3738

3839
SnapshotDumper::SnapshotDumper()
@@ -41,11 +42,16 @@ SnapshotDumper::SnapshotDumper()
4142
LockMode::UNLOCK_WHILE_RUNNING,
4243
1000
4344
)
44-
, CLICK_TO_SNAPSHOT(
45-
"<b>Click screen to trigger snapshot</b><br>",
46-
LockMode::UNLOCK_WHILE_RUNNING,
47-
false
48-
)
45+
, SNAPSHOT_MODE(
46+
"<b>Snapshot trigger:",
47+
{
48+
{SnapshotMode::KEYPRESS, "key-press", "Key Press. Press 'Page Down', while the screen is focused."},
49+
{SnapshotMode::MOUSE_CLICK, "mouse-click", "Mouse click on the screen."},
50+
{SnapshotMode::PERIODIC, "periodic", "Periodic: every X milliseconds as defined below."},
51+
},
52+
LockMode::LOCK_WHILE_RUNNING,
53+
SnapshotMode::KEYPRESS
54+
)
4955
, FORMAT(
5056
"<b>Image Format:</b>",
5157
{
@@ -56,29 +62,28 @@ SnapshotDumper::SnapshotDumper()
5662
Format::JPG
5763
)
5864
{
59-
PA_ADD_OPTION(CLICK_TO_SNAPSHOT);
65+
PA_ADD_OPTION(SNAPSHOT_MODE);
6066
PA_ADD_OPTION(PERIOD_MILLISECONDS);
6167
PA_ADD_OPTION(FORMAT);
62-
CLICK_TO_SNAPSHOT.add_listener(*this);
68+
SNAPSHOT_MODE.add_listener(*this);
6369
}
6470

6571
void SnapshotDumper::on_config_value_changed(void* object){
66-
PERIOD_MILLISECONDS.set_visibility(CLICK_TO_SNAPSHOT ? ConfigOptionState::HIDDEN : ConfigOptionState::ENABLED);
72+
PERIOD_MILLISECONDS.set_visibility(SNAPSHOT_MODE == SnapshotMode::PERIODIC ? ConfigOptionState::ENABLED : ConfigOptionState::HIDDEN);
6773
}
6874

69-
class SnapshotTrigger : public VideoOverlay::MouseListener{
75+
class SnapshotClickTrigger : public VideoOverlay::MouseListener{
7076
public:
71-
~SnapshotTrigger(){
77+
~SnapshotClickTrigger(){
7278
detach();
7379
}
74-
SnapshotTrigger(VideoStream& stream, VideoOverlay& overlay, Format format)
80+
SnapshotClickTrigger(VideoStream& stream, VideoOverlay& overlay, Format format)
7581
: m_stream(stream)
7682
, m_overlay(overlay)
77-
// , m_overlay_set(overlay)
7883
, m_format(format)
7984
{
8085
try{
81-
overlay.add_listener(*this);
86+
overlay.add_mouse_listener(*this);
8287
}catch (...){
8388
detach();
8489
throw;
@@ -92,31 +97,65 @@ class SnapshotTrigger : public VideoOverlay::MouseListener{
9297

9398
private:
9499
void detach(){
95-
m_overlay.remove_listener(*this);
100+
m_overlay.remove_mouse_listener(*this);
96101
}
97102

98103
private:
99104
VideoStream& m_stream;
100105
VideoOverlay& m_overlay;
101-
// VideoOverlaySet m_overlay_set;
102106
Format m_format;
103-
// std::mutex m_lock;
104107

105108
};
106109

110+
111+
SnapshotKeyTrigger::~SnapshotKeyTrigger(){
112+
detach();
113+
}
114+
SnapshotKeyTrigger::SnapshotKeyTrigger(VideoStream& stream, VideoOverlay& overlay, Format format)
115+
: m_stream(stream)
116+
, m_overlay(overlay)
117+
, m_format(format)
118+
{
119+
try{
120+
overlay.add_keyevent_listener(*this);
121+
}catch (...){
122+
detach();
123+
throw;
124+
}
125+
}
126+
127+
void SnapshotKeyTrigger::detach(){
128+
m_overlay.remove_keyevent_listener(*this);
129+
}
130+
131+
void SnapshotKeyTrigger::on_key_press(QKeyEvent* event){
132+
if (event->key() == Qt::Key::Key_PageDown){
133+
dump_snapshot(m_stream, "ScreenshotDumper", to_format_string(m_format));
134+
}
135+
}
136+
void SnapshotKeyTrigger::on_key_release(QKeyEvent* event){
137+
}
138+
139+
107140
void SnapshotDumper::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
108141
std::string folder_path = USER_FILE_PATH() + "ScreenshotDumper/";
109142
QDir().mkpath(folder_path.c_str());
110-
if (CLICK_TO_SNAPSHOT){
111-
SnapshotTrigger trigger(env.console, env.console.overlay(), FORMAT);
143+
144+
if (SNAPSHOT_MODE == SnapshotMode::KEYPRESS){
145+
SnapshotKeyTrigger key_trigger(env.console, env.console.overlay(), FORMAT);
112146
context.wait_until_cancel();
113-
}else{
147+
}else if (SNAPSHOT_MODE == SnapshotMode::MOUSE_CLICK){
148+
SnapshotClickTrigger click_trigger(env.console, env.console.overlay(), FORMAT);
149+
context.wait_until_cancel();
150+
}else if (SNAPSHOT_MODE == SnapshotMode::PERIODIC){
114151
while (true){
115152
VideoSnapshot last = env.console.video().snapshot();
116153
std::string filename = folder_path + now_to_filestring();
117154
last->save(filename + to_format_string(FORMAT));
118155
context.wait_until(last.timestamp + std::chrono::milliseconds(PERIOD_MILLISECONDS));
119156
}
157+
}else{
158+
throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Unexpected SNAPSHOT_MODE enum.");
120159
}
121160
}
122161

SerialPrograms/Source/NintendoSwitch/Programs/NintendoSwitch_SnapshotDumper.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
#ifndef PokemonAutomation_NintendoSwitch_SnapshotDumper_H
88
#define PokemonAutomation_NintendoSwitch_SnapshotDumper_H
99

10+
#include "CommonFramework/VideoPipeline/UI/VideoDisplayWidget.h"
1011
#include "Common/Cpp/Options/SimpleIntegerOption.h"
1112
#include "Common/Cpp/Options/EnumDropdownOption.h"
1213
#include "Common/Cpp/Options/BooleanCheckBoxOption.h"
14+
#include "CommonFramework/Panels/UI/PanelWidget.h"
15+
#include "CommonFramework/Panels/PanelInstance.h"
1316
#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h"
1417

1518
namespace PokemonAutomation{
@@ -38,10 +41,35 @@ class SnapshotDumper : public SingleSwitchProgramInstance, public ConfigOption::
3841

3942
private:
4043
SimpleIntegerOption<uint32_t> PERIOD_MILLISECONDS;
41-
BooleanCheckBoxOption CLICK_TO_SNAPSHOT;
44+
enum class SnapshotMode{
45+
KEYPRESS,
46+
MOUSE_CLICK,
47+
PERIODIC,
48+
};
49+
EnumDropdownOption<SnapshotMode> SNAPSHOT_MODE;
4250
EnumDropdownOption<Format> FORMAT;
4351
};
4452

53+
54+
class SnapshotKeyTrigger : public VideoOverlay::KeyEventListener{
55+
public:
56+
~SnapshotKeyTrigger();
57+
SnapshotKeyTrigger(VideoStream& stream, VideoOverlay& overlay, Format format);
58+
59+
60+
private:
61+
void detach();
62+
63+
virtual void on_key_press(QKeyEvent* event) override;
64+
virtual void on_key_release(QKeyEvent* event) override;
65+
66+
private:
67+
VideoStream& m_stream;
68+
VideoOverlay& m_overlay;
69+
Format m_format;
70+
};
71+
72+
4573
std::string to_format_string(Format format);
4674

4775
// takes a snapshot of the screen and saves it to the given folder_name

0 commit comments

Comments
 (0)