25 : eventDistributor(eventDistributor_)
26 , globalSettings(globalSettings_)
27 , joystickManager(commandController)
29 commandController,
"grabinput",
30 "This setting controls if openMSX takes over mouse and keyboard input",
32 , escapeGrabCmd(commandController)
46 if (!SDL_WasInit(SDL_INIT_VIDEO)) {
50 if (SDL_WaitEvent(
nullptr)) {
86 SDL_Event event1, event2;
91 while (SDL_PollEvent(curr)) {
94 if ((prev->type == SDL_KEYDOWN) && (curr->type == SDL_TEXTINPUT)) {
95 const char*
utf8 = curr->text.text;
97 handleKeyDown(prev->key, unicode);
99 splitText(curr->text.timestamp,
utf8);
107 if (curr->type == SDL_KEYDOWN) {
109 std::swap(curr, prev);
119void InputEventGenerator::setNewOsdControlButtonState(
unsigned newState)
121 unsigned deltaState = osdControlButtonsState ^ newState;
123 for (
auto b : {LEFT, RIGHT, UP, DOWN,
A,
B}) {
124 if (deltaState & (1 << std::to_underlying(b))) {
125 if (newState & (1 << std::to_underlying(b))) {
132 osdControlButtonsState = newState;
135void InputEventGenerator::triggerOsdControlEventsFromJoystickAxisMotion(
136 unsigned axis,
int value)
138 auto [neg_button, pos_button] = [&] {
142 return std::pair{1u << std::to_underlying(LEFT),
143 1u << std::to_underlying(RIGHT)};
145 return std::pair{1u << std::to_underlying(UP),
146 1u << std::to_underlying(DOWN)};
150 return std::pair{0u, 0u};
156 setNewOsdControlButtonState(
157 (osdControlButtonsState | neg_button) & ~pos_button);
158 }
else if (value < 0) {
160 setNewOsdControlButtonState(
161 (osdControlButtonsState | pos_button) & ~neg_button);
164 setNewOsdControlButtonState(
165 osdControlButtonsState | neg_button | pos_button);
169void InputEventGenerator::triggerOsdControlEventsFromJoystickHat(
int value)
173 if (!(value & SDL_HAT_UP )) dir |= 1 << std::to_underlying(UP);
174 if (!(value & SDL_HAT_DOWN )) dir |= 1 << std::to_underlying(DOWN);
175 if (!(value & SDL_HAT_LEFT )) dir |= 1 << std::to_underlying(LEFT);
176 if (!(value & SDL_HAT_RIGHT)) dir |= 1 << std::to_underlying(RIGHT);
177 unsigned ab = osdControlButtonsState & ((1 << std::to_underlying(
A)) |
178 (1 << std::to_underlying(
B)));
179 setNewOsdControlButtonState(ab | dir);
182void InputEventGenerator::osdControlChangeButton(
bool down,
unsigned changedButtonMask)
184 auto newButtonState = down
185 ? osdControlButtonsState & ~changedButtonMask
186 : osdControlButtonsState | changedButtonMask;
187 setNewOsdControlButtonState(newButtonState);
190void InputEventGenerator::triggerOsdControlEventsFromJoystickButtonEvent(
unsigned button,
bool down)
193 osdControlChangeButton(
195 ((button & 1) ? (1 << std::to_underlying(
B))
196 : (1 <<
std::to_underlying(
A))));
199void InputEventGenerator::triggerOsdControlEventsFromKeyEvent(SDLKey key,
bool repeat)
201 unsigned buttonMask = [&] {
202 switch (key.sym.sym) {
204 case SDLK_LEFT:
return 1 << std::to_underlying(LEFT);
205 case SDLK_RIGHT:
return 1 << std::to_underlying(RIGHT);
206 case SDLK_UP:
return 1 << std::to_underlying(UP);
207 case SDLK_DOWN:
return 1 << std::to_underlying(DOWN);
208 case SDLK_SPACE:
return 1 << std::to_underlying(
A);
209 case SDLK_RETURN:
return 1 << std::to_underlying(
A);
210 case SDLK_ESCAPE:
return 1 << std::to_underlying(
B);
216 osdControlChangeButton(!key.down, buttonMask);
218 osdControlChangeButton(key.down, buttonMask);
222static constexpr Uint16 normalizeModifier(SDL_Keycode sym, Uint16 mod)
229 return (sym ==
one_of(SDLK_LCTRL, SDLK_LSHIFT, SDLK_LALT, SDLK_LGUI,
230 SDLK_RCTRL, SDLK_RSHIFT, SDLK_RALT, SDLK_RGUI,
236void InputEventGenerator::handleKeyDown(
const SDL_KeyboardEvent& key, uint32_t unicode)
250 evt.key = SDL_KeyboardEvent{};
251 memcpy(&evt.key, &key,
sizeof(key));
252 evt.key.keysym.mod = normalizeModifier(key.keysym.sym, key.keysym.mod);
253 evt.key.keysym.unused = unicode;
254 Event event = KeyDownEvent(evt);
255 triggerOsdControlEventsFromKeyEvent(SDLKey{key.keysym,
true}, key.repeat);
260void InputEventGenerator::splitText(uint32_t timestamp,
const char*
utf8)
264 if (unicode == 0)
return;
270void InputEventGenerator::handle(
const SDL_Event& evt)
272 std::optional<Event> event;
292 e.key = SDL_KeyboardEvent{};
293 memcpy(&
e.key, &evt.key,
sizeof(evt.key));
294 e.key.keysym.mod = normalizeModifier(evt.key.keysym.sym, evt.key.keysym.mod);
295 event = KeyUpEvent(e);
298 triggerOsdControlEventsFromKeyEvent(SDLKey{
e.key.keysym, down},
repeat);
302 handleKeyDown(evt.key, 0);
305 case SDL_MOUSEBUTTONUP:
306 event = MouseButtonUpEvent(evt);
308 case SDL_MOUSEBUTTONDOWN:
309 event = MouseButtonDownEvent(evt);
312 event = MouseWheelEvent(evt);
314 case SDL_MOUSEMOTION:
315 event = MouseMotionEvent(evt);
316 if (
auto* window = SDL_GL_GetCurrentWindow(); SDL_GetWindowGrab(window)) {
318 SDL_GetWindowSize(window, &w, &h);
320 SDL_GetMouseState(&x, &y);
333 static constexpr int MARGIN = 10;
334 int xn = std::clamp(x, MARGIN, w - 1 - MARGIN);
335 int yn = std::clamp(y, MARGIN, h - 1 - MARGIN);
336 if (xn != x || yn != y) SDL_WarpMouseInWindow(window, xn, yn);
339 case SDL_JOYBUTTONUP:
341 event = JoystickButtonUpEvent(evt);
342 triggerOsdControlEventsFromJoystickButtonEvent(
343 evt.jbutton.button,
false);
346 case SDL_JOYBUTTONDOWN:
348 event = JoystickButtonDownEvent(evt);
349 triggerOsdControlEventsFromJoystickButtonEvent(
350 evt.jbutton.button,
true);
353 case SDL_JOYAXISMOTION: {
357 int deadZone =
setting->getInt();
358 int threshold = (deadZone * 32768) / 100;
359 auto value = (evt.jaxis.value < -threshold) ? evt.jaxis.value
360 : (evt.jaxis.value > threshold) ? evt.jaxis.value
362 event = JoystickAxisMotionEvent(evt);
363 triggerOsdControlEventsFromJoystickAxisMotion(
364 evt.jaxis.axis, value);
368 case SDL_JOYHATMOTION:
370 event = JoystickHatEvent(evt);
371 triggerOsdControlEventsFromJoystickHat(evt.jhat.value);
375 case SDL_JOYDEVICEADDED:
376 joystickManager.
add(evt.jdevice.which);
379 case SDL_JOYDEVICEREMOVED:
380 joystickManager.
remove(evt.jdevice.which);
384 splitText(evt.text.timestamp, evt.text.text);
385 event = TextEvent(evt);
388 case SDL_WINDOWEVENT:
389 switch (evt.window.event) {
390 case SDL_WINDOWEVENT_CLOSE:
397 event = WindowEvent(evt);
403 event = FileDropEvent(
405 SDL_free(evt.drop.file);
418 std::cerr <<
"SDL event converted to: " <<
toString(event) <<
'\n';
420 std::cerr <<
"SDL event was of unknown type, not converted to an openMSX event\n";
430 escapeGrabState = ESCAPE_GRAB_WAIT_CMD;
434bool InputEventGenerator::signalEvent(
const Event& event)
438 const auto& evt = e.getSdlWindowEvent();
439 if (e.isMainWindow() &&
440 evt.event ==
one_of(SDL_WINDOWEVENT_FOCUS_GAINED, SDL_WINDOWEVENT_FOCUS_LOST)) {
441 switch (escapeGrabState) {
442 case ESCAPE_GRAB_WAIT_CMD:
445 case ESCAPE_GRAB_WAIT_LOST:
446 if (evt.event == SDL_WINDOWEVENT_FOCUS_LOST) {
447 escapeGrabState = ESCAPE_GRAB_WAIT_GAIN;
450 case ESCAPE_GRAB_WAIT_GAIN:
451 if (evt.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
452 escapeGrabState = ESCAPE_GRAB_WAIT_CMD;
466void InputEventGenerator::setGrabInput(
bool grab)
const
468 SDL_SetWindowGrab(SDL_GL_GetCurrentWindow(), grab ? SDL_TRUE : SDL_FALSE);
474InputEventGenerator::EscapeGrabCmd::EscapeGrabCmd(
475 CommandController& commandController_)
476 : Command(commandController_,
"escape_grab")
480void InputEventGenerator::EscapeGrabCmd::execute(
481 std::span<const TclObject> , TclObject& )
483 auto& inputEventGenerator =
OUTER(InputEventGenerator, escapeGrabCmd);
484 if (inputEventGenerator.grabInput.getBoolean()) {
485 inputEventGenerator.escapeGrabState =
486 InputEventGenerator::ESCAPE_GRAB_WAIT_LOST;
487 inputEventGenerator.setGrabInput(
false);
491std::string InputEventGenerator::EscapeGrabCmd::help(
492 std::span<const TclObject> )
const
494 return "Temporarily release input grab.";
bool getBoolean() const noexcept
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void distributeEvent(Event &&event)
Schedule the given event for delivery.
void registerEventListener(EventType type, EventListener &listener, Priority priority=Priority::OTHER)
Registers a given object to receive certain events.
This class contains settings that are used by several other class (including some singletons).
void add(int deviceIndex)
IntegerSetting * getJoyDeadZoneSetting(JoystickId joyId) const
void remove(int instanceId)
std::optional< JoystickId > translateSdlInstanceId(SDL_Event &evt) const
static KeyDownEvent create(SDL_Keycode code, SDL_Keymod mod=KMOD_NONE)
bool isMainWindow() const
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
This file implemented 3 utility functions:
std::string toString(const BooleanInput &input)
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
std::array< const EDStorage, 4 > A
uint32_t next(octet_iterator &it)
#define OUTER(type, member)
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.