21 using std::make_shared;
43 , bindCmd (commandController_, *this, false)
44 , bindDefaultCmd (commandController_, *this, true)
45 , unbindCmd (commandController_, *this, false)
46 , unbindDefaultCmd(commandController_, *this, true)
47 , activateCmd (commandController_)
48 , deactivateCmd (commandController_)
49 , commandController(commandController_)
50 , eventDistributor(eventDistributor_)
52 initDefaultBindings();
102 void HotKey::initDefaultBindings()
108 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
110 "screenshot -guess-name"));
111 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
114 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
116 "toggle fastforward"));
117 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
120 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
123 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
125 "toggle fullscreen"));
126 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
131 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
Keys::K_PRINT),
132 "screenshot -guess-name"));
133 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
Keys::K_PAUSE),
135 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
Keys::K_F9),
136 "toggle fastforward"));
137 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
Keys::K_F10),
139 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
Keys::K_F11),
140 "toggle fullscreen"));
141 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
Keys::K_F12),
143 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
146 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
149 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
151 "toggle fullscreen"));
153 bindDefault(HotKeyInfo(make_shared<KeyDownEvent>(
Keys::K_BACK),
154 "quitmenu::quit_menu"));
158 static HotKey::EventPtr createEvent(
const TclObject& obj, Interpreter& interp)
161 if (!
dynamic_cast<const KeyEvent*
> (event.get()) &&
162 !
dynamic_cast<const MouseButtonEvent*
> (event.get()) &&
163 !
dynamic_cast<const GroupEvent*
> (event.get()) &&
164 !
dynamic_cast<const JoystickEvent*
> (event.get()) &&
165 !
dynamic_cast<const OsdControlEvent*
> (event.get()) &&
166 !
dynamic_cast<const FocusEvent*
> (event.get())) {
167 throw CommandException(
"Unsupported event type");
173 return createEvent(TclObject(str), interp);
184 const auto* bindingsElement = config.
findChild(
"bindings");
185 if (!bindingsElement)
return;
186 auto copy = *bindingsElement;
187 for (
const auto& elem :
copy.getChildren()) {
190 if (elem.getName() ==
"bind") {
191 bind(
HotKeyInfo(createEvent(elem.getAttribute(
"key"), interp),
193 elem.getAttributeAsBool(
"repeat",
false),
194 elem.getAttributeAsBool(
"event",
false)));
195 }
else if (elem.getName() ==
"unbind") {
196 unbind(createEvent(elem.getAttribute(
"key"), interp));
200 "Error while loading key bindings: ", e.
getMessage());
211 return event == *info.
event;
222 for (
const auto& k : boundKeys) {
224 auto& elem = bindingsElement.addChild(
"bind", info.command);
225 elem.addAttribute(
"key", k->toString());
227 elem.addAttribute(
"repeat",
"true");
229 if (info.passEvent) {
230 elem.addAttribute(
"event",
"true");
234 for (
const auto& k : unboundKeys) {
235 auto& elem = bindingsElement.addChild(
"unbind");
236 elem.addAttribute(
"key", k->toString());
241 static bool contains(
const vector<T>& v,
const Event& event)
247 static void erase(vector<T>& v,
const Event& event)
259 set.push_back(event);
263 template<
typename HotKeyInfo>
267 *it = std::forward<HotKeyInfo>(info);
269 map.push_back(std::forward<HotKeyInfo>(info));
273 void HotKey::bind(HotKeyInfo&& info)
275 erase(unboundKeys, *info.event);
276 erase(defaultMap, *info.event);
277 insert(boundKeys, info.event);
278 insert(cmdMap, std::move(info));
283 void HotKey::unbind(
const EventPtr& event)
286 it1 ==
end(boundKeys)) {
288 insert(unboundKeys, event);
294 erase(defaultMap, *event);
295 erase(cmdMap, *event);
300 void HotKey::bindDefault(HotKeyInfo&& info)
302 if (!contains( boundKeys, *info.event) &&
303 !contains(unboundKeys, *info.event)) {
305 insert(cmdMap, info);
307 insert(defaultMap, std::move(info));
310 void HotKey::unbindDefault(
const EventPtr& event)
312 if (!contains( boundKeys, *event) &&
313 !contains(unboundKeys, *event)) {
315 erase(cmdMap, *event);
317 erase(defaultMap, *event);
320 void HotKey::bindLayer(HotKeyInfo&& info,
const string& layer)
322 insert(layerMap[layer], std::move(info));
325 void HotKey::unbindLayer(
const EventPtr& event,
const string& layer)
327 erase(layerMap[layer], *event);
330 void HotKey::unbindFullLayer(
const string& layer)
332 layerMap.erase(layer);
335 void HotKey::activateLayer(std::string layer,
bool blocking)
341 activeLayers.push_back({std::move(layer), blocking});
344 void HotKey::deactivateLayer(std::string_view layer)
349 [&](
auto& info) {
return info.layer == layer; });
350 it != activeLayers.rend()) {
352 activeLayers.erase((it + 1).base());
356 static HotKey::BindMap::const_iterator findMatch(
360 return p.event->matches(event);
364 void HotKey::executeRT()
366 if (lastEvent) executeEvent(lastEvent);
369 int HotKey::signalEvent(
const EventPtr& event)
371 if (lastEvent != event) {
380 if (lastEvent && lastEvent->isRepeatStopper(*event)) {
384 return executeEvent(event);
387 int HotKey::executeEvent(
const EventPtr& event)
390 bool blocking =
false;
392 auto& cmap = layerMap[info.layer];
393 if (
auto it = findMatch(cmap, *event); it !=
end(cmap)) {
394 executeBinding(event, *it);
399 blocking = info.blocking;
404 if (
auto it = findMatch(cmdMap, *event); it !=
end(cmdMap)) {
405 executeBinding(event, *it);
414 void HotKey::executeBinding(
const EventPtr& event,
const HotKeyInfo& info)
430 TclObject command(info.command);
431 if (info.passEvent) {
436 command.addListElement(event->toTclList());
441 }
catch (CommandException& e) {
443 "Error executing hot key command: ", e.getMessage());
447 void HotKey::startRepeat(
const EventPtr& event)
458 static constexpr
unsigned PERIOD = 30;
460 unsigned delay = (lastEvent ? PERIOD : DELAY) * 1000;
465 void HotKey::stopRepeat()
474 static std::string_view getBindCmdName(
bool defaultCmd)
476 return defaultCmd ?
"bind_default" :
"bind";
479 HotKey::BindCmd::BindCmd(CommandController& commandController_, HotKey& hotKey_,
481 : Command(commandController_, getBindCmdName(defaultCmd_))
483 , defaultCmd(defaultCmd_)
487 static string formatBinding(
const HotKey::HotKeyInfo& info)
489 return strCat(info.event->toString(), (info.repeat ?
" [repeat]" :
""),
490 (info.passEvent ?
" [event]" :
""),
": ", info.command,
'\n');
498 bool passEvent =
false;
499 ArgsInfo parserInfo[] = {
506 if (defaultCmd && !layer.empty()) {
507 throw CommandException(
"Layers are not supported for default bindings");
510 auto& cMap = defaultCmd
512 : layer.empty() ? hotKey.cmdMap
513 : hotKey.layerMap[layer];
516 for (
const auto& [layerName, bindings] : hotKey.layerMap) {
519 if (!bindings.empty()) {
520 result.addListElement(layerName);
526 switch (arguments.size()) {
530 for (
auto& p : cMap) {
531 r += formatBinding(p);
539 EqualEvent(*createEvent(arguments[0], getInterpreter())));
540 if (it ==
end(cMap)) {
541 throw CommandException(
"Key not bound");
543 result = formatBinding(*it);
548 string command(arguments[1].getString());
549 for (
const auto& arg :
view::drop(arguments, 2)) {
550 strAppend(command,
' ', arg.getString());
552 HotKey::HotKeyInfo info(
553 createEvent(arguments[0], getInterpreter()),
554 command,
repeat, passEvent);
556 hotKey.bindDefault(std::move(info));
557 }
else if (layer.empty()) {
558 hotKey.bind(std::move(info));
560 hotKey.bindLayer(std::move(info), layer);
566 string HotKey::BindCmd::help(
const vector<string>& )
const
568 auto cmd = getBindCmdName(defaultCmd);
570 cmd,
" : show all bounded keys\n",
571 cmd,
" <key> : show binding for this key\n",
572 cmd,
" <key> [-repeat] [-event] <cmd> : bind key to command, optionally "
573 "repeat command while key remains pressed and also optionally "
574 "give back the event as argument (a list) to <cmd>\n"
575 "These 3 take an optional '-layer <layername>' option, "
576 "see activate_input_layer.\n",
577 cmd,
" -layers : show a list of layers with bound keys\n");
583 static string getUnbindCmdName(
bool defaultCmd)
585 return defaultCmd ?
"unbind_default" :
"unbind";
588 HotKey::UnbindCmd::UnbindCmd(CommandController& commandController_,
589 HotKey& hotKey_,
bool defaultCmd_)
590 : Command(commandController_, getUnbindCmdName(defaultCmd_))
592 , defaultCmd(defaultCmd_)
599 ArgsInfo info[] = {
valueArg(
"-layer", layer) };
601 if (defaultCmd && !layer.empty()) {
602 throw CommandException(
"Layers are not supported for default bindings");
605 if ((arguments.size() > 1) || (layer.empty() && (arguments.size() != 1))) {
610 if (arguments.size() == 1) {
611 event = createEvent(arguments[0], getInterpreter());
616 hotKey.unbindDefault(event);
617 }
else if (layer.empty()) {
619 hotKey.unbind(event);
622 hotKey.unbindLayer(event, layer);
624 hotKey.unbindFullLayer(layer);
628 string HotKey::UnbindCmd::help(
const vector<string>& )
const
630 auto cmd = getUnbindCmdName(defaultCmd);
632 cmd,
" <key> : unbind this key\n",
633 cmd,
" -layer <layername> <key> : unbind key in a specific layer\n",
634 cmd,
" -layer <layername> : unbind all keys in this layer\n");
640 HotKey::ActivateCmd::ActivateCmd(CommandController& commandController_)
641 : Command(commandController_,
"activate_input_layer")
647 bool blocking =
false;
648 ArgsInfo info[] = {
flagArg(
"-blocking", blocking) };
651 auto& hotKey =
OUTER(HotKey, activateCmd);
652 switch (args.size()) {
656 r += layerInfo.layer;
657 if (layerInfo.blocking) {
666 std::string_view layer = args[0].getString();
667 hotKey.activateLayer(
string(layer), blocking);
675 string HotKey::ActivateCmd::help(
const vector<string>& )
const
677 return "activate_input_layer "
678 ": show list of active layers (most recent on top)\n"
679 "activate_input_layer [-blocking] <layername> "
680 ": activate new layer, optionally in blocking mode\n";
686 HotKey::DeactivateCmd::DeactivateCmd(CommandController& commandController_)
687 : Command(commandController_,
"deactivate_input_layer")
693 checkNumArgs(tokens, 2,
"layer");
694 auto& hotKey =
OUTER(HotKey, deactivateCmd);
695 hotKey.deactivateLayer(tokens[1].getString());
698 string HotKey::DeactivateCmd::help(
const vector<string>& )
const
700 return "deactivate_input_layer <layername> : deactivate the given input layer";