31static constexpr bool META_HOT_KEYS =
42 , bindCmd (commandController_, *this, false)
43 , bindDefaultCmd (commandController_, *this, true)
44 , unbindCmd (commandController_, *this, false)
45 , unbindDefaultCmd(commandController_, *this, true)
46 , activateCmd (commandController_)
47 , deactivateCmd (commandController_)
48 , commandController(commandController_)
49 , eventDistributor(eventDistributor_)
53 initDefaultBindings();
57 : hotKey(hotKey_), priority(priority_)
59 auto& distributor = hotKey.eventDistributor;
65 distributor.registerEventListener(type, *
this, priority);
69HotKey::Listener::~Listener()
71 auto& distributor = hotKey.eventDistributor;
77 distributor.unregisterEventListener(type, *
this);
81void HotKey::initDefaultBindings()
85 if constexpr (META_HOT_KEYS) {
88 "screenshot -guess-name"));
92 "toggle fastforward"));
98 "toggle fullscreen"));
104 "screenshot -guess-name"));
108 "toggle fastforward"));
112 "toggle fullscreen"));
120 "toggle fullscreen"));
124static Event createEvent(
const TclObject& obj, Interpreter& interp)
132 throw CommandException(
"Unsupported event type");
136static Event createEvent(std::string_view str, Interpreter& interp)
138 return createEvent(TclObject(str), interp);
166 return event == info.
event;
171static bool contains(
auto&& range,
const Event& event)
177static void erase(std::vector<T>& v,
const Event& event)
189 set.push_back(event);
193template<
typename HotKeyInfo>
197 *it = std::forward<HotKeyInfo>(info);
199 map.push_back(std::forward<HotKeyInfo>(info));
203void HotKey::bind(HotKeyInfo&& info)
205 erase(unboundKeys, info.event);
206 erase(defaultMap, info.event);
207 insert(boundKeys, info.event);
208 insert(cmdMap, std::move(info));
211void HotKey::unbind(
const Event& event)
214 it1 ==
end(boundKeys)) {
216 insert(unboundKeys, event);
222 erase(defaultMap, event);
223 erase(cmdMap, event);
226void HotKey::bindDefault(HotKeyInfo&& info)
228 if (!contains( boundKeys, info.event) &&
229 !contains(unboundKeys, info.event)) {
231 insert(cmdMap, info);
233 insert(defaultMap, std::move(info));
236void HotKey::unbindDefault(
const Event& event)
238 if (!contains( boundKeys, event) &&
239 !contains(unboundKeys, event)) {
241 erase(cmdMap, event);
243 erase(defaultMap, event);
246void HotKey::bindLayer(HotKeyInfo&& info,
const string& layer)
248 insert(layerMap[layer], std::move(info));
251void HotKey::unbindLayer(
const Event& event,
const string& layer)
253 erase(layerMap[layer], event);
256void HotKey::unbindFullLayer(
const string& layer)
258 layerMap.erase(layer);
261void HotKey::activateLayer(std::string layer,
bool blocking)
267 activeLayers.emplace_back(std::move(layer), blocking);
270void HotKey::deactivateLayer(std::string_view layer)
275 it != activeLayers.rend()) {
277 activeLayers.erase((it + 1).base());
281static HotKey::BindMap::const_iterator findMatch(
285 return (p.msx == msx) &&
matches(p.event, event);
289void HotKey::executeRT()
297bool HotKey::Listener::signalEvent(
const Event& event)
299 return hotKey.signalEvent(event, priority);
304 if (lastEvent && *lastEvent != event) {
315 return executeEvent(event, priority);
323 bool blocking =
false;
324 for (
const auto& info :
view::
reverse(activeLayers)) {
325 auto& cmap = layerMap[info.layer];
326 if (
auto it = findMatch(cmap, event, msx); it !=
end(cmap)) {
327 executeBinding(event, *it);
332 blocking = info.blocking;
337 if (
auto it = findMatch(cmdMap, event, msx); it !=
end(cmdMap)) {
338 executeBinding(event, *it);
347void HotKey::executeBinding(
const Event& event,
const HotKeyInfo& info)
363 TclObject command(info.command);
364 if (info.passEvent) {
369 command.addListElement(
toTclList(event));
374 }
catch (CommandException& e) {
376 "Error executing hot key command: ",
e.getMessage());
380void HotKey::startRepeat(
const Event& event)
391 static constexpr unsigned PERIOD = 30;
393 unsigned delay = (lastEvent ? PERIOD : DELAY) * 1000;
398void HotKey::stopRepeat()
407static constexpr std::string_view getBindCmdName(
bool defaultCmd)
409 return defaultCmd ?
"bind_default" :
"bind";
412HotKey::BindCmd::BindCmd(CommandController& commandController_, HotKey& hotKey_,
414 : Command(commandController_, getBindCmdName(defaultCmd_))
416 , defaultCmd(defaultCmd_)
420static string formatBinding(
const HotKey::HotKeyInfo& info)
423 (info.msx ?
" [msx]" :
""),
424 (info.
repeat ?
" [repeat]" :
""),
425 (info.passEvent ?
" [event]" :
""),
426 ": ", info.command,
'\n');
429void HotKey::BindCmd::execute(std::span<const TclObject> tokens, TclObject& result)
434 bool passEvent =
false;
436 std::array parserInfo = {
443 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan<1>(), parserInfo);
444 if (defaultCmd && !layer.empty()) {
445 throw CommandException(
"Layers are not supported for default bindings");
448 auto& cMap = defaultCmd
450 : layer.empty() ? hotKey.cmdMap
451 : hotKey.layerMap[layer];
454 for (
const auto& [layerName, bindings] : hotKey.layerMap) {
457 if (!bindings.empty()) {
458 result.addListElement(layerName);
464 switch (arguments.size()) {
468 for (
const auto& p : cMap) {
469 r += formatBinding(p);
477 EqualEvent(createEvent(arguments[0], getInterpreter())));
478 if (it ==
end(cMap)) {
479 throw CommandException(
"Key not bound");
481 result = formatBinding(*it);
486 string command(arguments[1].getString());
487 for (
const auto& arg :
view::
drop(arguments, 2)) {
488 strAppend(command,
' ', arg.getString());
490 HotKey::HotKeyInfo info(
491 createEvent(arguments[0], getInterpreter()),
492 command,
repeat, passEvent, msx);
494 hotKey.bindDefault(std::move(info));
495 }
else if (layer.empty()) {
496 hotKey.bind(std::move(info));
498 hotKey.bindLayer(std::move(info), layer);
504string HotKey::BindCmd::help(std::span<const TclObject> )
const
506 auto cmd = getBindCmdName(defaultCmd);
508 cmd,
" : show all bounded keys\n",
509 cmd,
" <key> : show binding for this key\n",
510 cmd,
" <key> [-msx] [-repeat] [-event] <cmd> : bind key to command, optionally "
511 "repeat command while key remains pressed and also optionally "
512 "give back the event as argument (a list) to <cmd>.\n"
513 "When the '-msx' flag is given the binding only has effect "
514 "when the msx window has focus (not when the GUI has focus).\n"
515 "These 3 take an optional '-layer <layername>' option, "
516 "see activate_input_layer.\n",
517 cmd,
" -layers : show a list of layers with bound keys\n");
523static constexpr std::string_view getUnbindCmdName(
bool defaultCmd)
525 return defaultCmd ?
"unbind_default" :
"unbind";
528HotKey::UnbindCmd::UnbindCmd(CommandController& commandController_,
529 HotKey& hotKey_,
bool defaultCmd_)
530 : Command(commandController_, getUnbindCmdName(defaultCmd_))
532 , defaultCmd(defaultCmd_)
536void HotKey::UnbindCmd::execute(std::span<const TclObject> tokens, TclObject& )
539 std::array info = {
valueArg(
"-layer", layer)};
540 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan<1>(), info);
541 if (defaultCmd && !layer.empty()) {
542 throw CommandException(
"Layers are not supported for default bindings");
545 if ((arguments.size() > 1) || (layer.empty() && (arguments.size() != 1))) {
549 std::optional<Event> event;
550 if (arguments.size() == 1) {
551 event = createEvent(arguments[0], getInterpreter());
556 hotKey.unbindDefault(*event);
557 }
else if (layer.empty()) {
559 hotKey.unbind(*event);
562 hotKey.unbindLayer(*event, layer);
564 hotKey.unbindFullLayer(layer);
568string HotKey::UnbindCmd::help(std::span<const TclObject> )
const
570 auto cmd = getUnbindCmdName(defaultCmd);
572 cmd,
" <key> : unbind this key\n",
573 cmd,
" -layer <layername> <key> : unbind key in a specific layer\n",
574 cmd,
" -layer <layername> : unbind all keys in this layer\n");
580HotKey::ActivateCmd::ActivateCmd(CommandController& commandController_)
581 : Command(commandController_,
"activate_input_layer")
585void HotKey::ActivateCmd::execute(std::span<const TclObject> tokens, TclObject& result)
587 bool blocking =
false;
588 std::array info = {
flagArg(
"-blocking", blocking)};
589 auto args =
parseTclArgs(getInterpreter(), tokens.subspan(1), info);
591 auto& hotKey =
OUTER(HotKey, activateCmd);
592 switch (args.size()) {
595 for (
const auto& layerInfo :
view::
reverse(hotKey.activeLayers)) {
596 r += layerInfo.layer;
597 if (layerInfo.blocking) {
606 std::string_view layer = args[0].getString();
607 hotKey.activateLayer(
string(layer), blocking);
615string HotKey::ActivateCmd::help(std::span<const TclObject> )
const
617 return "activate_input_layer "
618 ": show list of active layers (most recent on top)\n"
619 "activate_input_layer [-blocking] <layername> "
620 ": activate new layer, optionally in blocking mode\n";
626HotKey::DeactivateCmd::DeactivateCmd(CommandController& commandController_)
627 : Command(commandController_,
"deactivate_input_layer")
631void HotKey::DeactivateCmd::execute(std::span<const TclObject> tokens, TclObject& )
633 checkNumArgs(tokens, 2,
"layer");
634 auto& hotKey =
OUTER(HotKey, deactivateCmd);
635 hotKey.deactivateLayer(tokens[1].getString());
638string HotKey::DeactivateCmd::help(std::span<const TclObject> )
const
640 return "deactivate_input_layer <layername> : deactivate the given input layer";
void printWarning(std::string_view message)
Priority
Priorities from high to low, higher priority listeners can block events for lower priority listeners.
Interpreter & getInterpreter() override
CliComm & getCliComm() override
HotKey(RTScheduler &rtScheduler, GlobalCommandController &commandController, EventDistributor &eventDistributor)
void loadUnbind(std::string_view key)
std::vector< HotKeyInfo > BindMap
void loadBind(const Data &data)
std::vector< Event > KeySet
static KeyDownEvent create(SDL_Keycode code, SDL_Keymod mod=KMOD_NONE)
void scheduleRT(uint64_t delta)
This file implemented 3 utility functions:
bool matches(const Event &self, const Event &other)
Does this event 'match' the given event.
ArgsInfo valueArg(std::string_view name, T &value)
std::vector< TclObject > parseTclArgs(Interpreter &interp, std::span< const TclObject > inArgs, std::span< const ArgsInfo > table)
TclObject toTclList(const Event &event)
Similar to toString(), but retains the structure of the event.
EventType getType(const Event &event)
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
ArgsInfo flagArg(std::string_view name, bool &flag)
auto find_if(InputRange &&range, UnaryPredicate pred)
auto find(InputRange &&range, const T &value)
constexpr bool any_of(InputRange &&range, UnaryPredicate pred)
constexpr auto reverse(Range &&range)
constexpr auto drop(Range &&range, size_t n)
#define OUTER(type, member)
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
void strAppend(std::string &result, Ts &&...ts)
EqualEvent(const Event &event_)
bool operator()(const HotKey::HotKeyInfo &info) const
bool operator()(const Event &e) const
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
constexpr auto end(const zstring_view &x)