21 using std::make_shared;
44 virtual string execute(
const vector<string>& tokens);
45 virtual string help(
const vector<string>& tokens)
const;
47 string formatBinding(
const HotKey::BindMap::value_type& p);
50 const bool defaultCmd;
58 virtual string execute(
const vector<string>& tokens);
59 virtual string help(
const vector<string>& tokens)
const;
62 const bool defaultCmd;
69 virtual string execute(
const vector<string>& tokens);
70 virtual string help(
const vector<string>& tokens)
const;
79 virtual string execute(
const vector<string>& tokens);
80 virtual string help(
const vector<string>& tokens)
const;
89 commandController_, *this, false))
91 commandController_, *this, false))
93 commandController_, *this, true))
95 commandController_, *this, true))
97 commandController_, *this))
99 commandController_, *this))
103 , commandController(commandController_)
104 , eventDistributor(eventDistributor_)
106 initDefaultBindings();
147 void HotKey::initDefaultBindings()
153 bindDefault(make_shared<KeyDownEvent>(
155 HotKeyInfo(
"screenshot -guess-name"));
156 bindDefault(make_shared<KeyDownEvent>(
158 HotKeyInfo(
"toggle pause"));
159 bindDefault(make_shared<KeyDownEvent>(
161 HotKeyInfo(
"toggle throttle"));
162 bindDefault(make_shared<KeyDownEvent>(
164 HotKeyInfo(
"toggle console"));
165 bindDefault(make_shared<KeyDownEvent>(
167 HotKeyInfo(
"toggle mute"));
168 bindDefault(make_shared<KeyDownEvent>(
170 HotKeyInfo(
"toggle fullscreen"));
171 bindDefault(make_shared<KeyDownEvent>(
177 HotKeyInfo(
"screenshot -guess-name"));
179 HotKeyInfo(
"toggle pause"));
180 bindDefault(make_shared<KeyDownEvent>(
Keys::K_F9),
181 HotKeyInfo(
"toggle throttle"));
182 bindDefault(make_shared<KeyDownEvent>(
Keys::K_F10),
183 HotKeyInfo(
"toggle console"));
184 bindDefault(make_shared<KeyDownEvent>(
Keys::K_F11),
185 HotKeyInfo(
"toggle mute"));
186 bindDefault(make_shared<KeyDownEvent>(
Keys::K_F12),
187 HotKeyInfo(
"toggle fullscreen"));
188 bindDefault(make_shared<KeyDownEvent>(
191 bindDefault(make_shared<KeyDownEvent>(
194 bindDefault(make_shared<KeyDownEvent>(
196 HotKeyInfo(
"toggle fullscreen"));
205 HotKeyInfo(
"quitmenu::quit_menu"));
213 if (!dynamic_cast<const KeyEvent*> (event.get()) &&
214 !dynamic_cast<const MouseButtonEvent*> (event.get()) &&
215 !dynamic_cast<const MouseMotionGroupEvent*>(event.get()) &&
216 !dynamic_cast<const JoystickEvent*> (event.get()) &&
217 !dynamic_cast<const OsdControlEvent*> (event.get()) &&
218 !dynamic_cast<const FocusEvent*> (event.get())) {
219 throw CommandException(
"Unsupported event type");
230 cmdMap.insert(defaultMap.begin(), defaultMap.end());
233 auto* bindingsElement = config.
findChild(
"bindings");
234 if (!bindingsElement)
return;
235 auto copy = *bindingsElement;
236 for (
auto& elem : copy.getChildren()) {
238 if (elem.getName() ==
"bind") {
239 bind(createEvent(elem.getAttribute(
"key")),
241 elem.getAttributeAsBool(
"repeat",
false)));
242 }
else if (elem.getName() ==
"unbind") {
243 unbind(createEvent(elem.getAttribute(
"key")));
247 "Error while loading key bindings: " + e.
getMessage());
258 for (
auto& k : boundKeys) {
259 auto it2 = cmdMap.find(k);
260 assert(it2 != cmdMap.end());
261 auto& info = it2->second;
267 bindingsElement.addChild(std::move(elem));
270 for (
auto& k : unboundKeys) {
273 bindingsElement.addChild(std::move(elem));
277 void HotKey::bind(
const EventPtr& event,
const HotKeyInfo& info)
279 unboundKeys.erase(event);
280 boundKeys.insert(event);
281 defaultMap.erase(event);
282 cmdMap[event] = info;
287 void HotKey::unbind(
const EventPtr& event)
289 if (boundKeys.find(event) == boundKeys.end()) {
291 unboundKeys.insert(event);
293 boundKeys.erase(event);
294 defaultMap.erase(event);
300 void HotKey::bindDefault(
const EventPtr& event,
const HotKeyInfo& info)
302 if ((unboundKeys.find(event) == unboundKeys.end()) &&
303 (boundKeys.find(event) == boundKeys.end())) {
305 cmdMap[event] = info;
307 defaultMap[event] = info;
310 void HotKey::unbindDefault(
const EventPtr& event)
312 if ((unboundKeys.find(event) == unboundKeys.end()) &&
313 (boundKeys.find(event) == boundKeys.end())) {
317 defaultMap.erase(event);
320 void HotKey::bindLayer(
const EventPtr& event,
const HotKeyInfo& info,
323 layerMap[layer][event] = info;
326 void HotKey::unbindLayer(
const EventPtr& event,
const string& layer)
328 layerMap[layer].erase(event);
331 void HotKey::unbindFullLayer(
const string& layer)
333 layerMap.erase(layer);
336 void HotKey::activateLayer(
const std::string& layer,
bool blocking)
344 info.blocking = blocking;
345 activeLayers.push_back(info);
348 void HotKey::deactivateLayer(
const std::string& layer)
352 auto it = std::find_if(activeLayers.rbegin(), activeLayers.rend(),
353 [&](
const LayerInfo& info) {
return info.layer == layer; });
354 if (it != activeLayers.rend()) {
356 activeLayers.erase((it + 1).base());
360 static HotKey::BindMap::const_iterator findMatch(
363 return find_if(map.begin(), map.end(),
364 [&](
const HotKey::BindMap::value_type& p) {
365 return p.first->matches(event);
369 int HotKey::signalEvent(
const EventPtr& event_)
374 if (!lastEvent.get())
return true;
376 }
else if (lastEvent.get() !=
event.get()) {
385 if (lastEvent.get() && lastEvent->isRepeatStopper(*event)) {
391 bool blocking =
false;
392 for (
auto it = activeLayers.rbegin(); it != activeLayers.rend(); ++it) {
393 auto& cmap = layerMap[it->layer];
394 auto it2 = findMatch(cmap, *event);
395 if (it2 != cmap.end()) {
396 executeBinding(event, it2->second);
401 blocking = it->blocking;
406 auto it = findMatch(cmdMap, *event);
407 if (it != cmdMap.end()) {
408 executeBinding(event, it->second);
417 void HotKey::executeBinding(
const EventPtr& event,
const HotKeyInfo& info)
425 }
catch (CommandException& e) {
427 "Error executing hot key command: " + e.getMessage());
431 void HotKey::startRepeat(
const EventPtr& event)
442 static const unsigned PERIOD = 30;
444 unsigned delay = (lastEvent.get() ? PERIOD : DELAY) * 1000;
446 repeatAlarm->schedule(delay);
449 void HotKey::stopRepeat()
452 repeatAlarm->cancel();
458 static string getBindCmdName(
bool defaultCmd)
460 return defaultCmd ?
"bind_default" :
"bind";
465 :
Command(commandController, getBindCmdName(defaultCmd_))
467 , defaultCmd(defaultCmd_)
471 string BindCmd::formatBinding(
const HotKey::BindMap::value_type& p)
473 auto& info = p.second;
474 return p.first->toString() + (info.repeat ?
" [repeat]" :
"") +
475 ": " + info.command +
'\n';
478 static vector<string> parse(
bool defaultCmd, vector<string> tokens,
479 string& layer,
bool& layers)
482 for (
size_t i = 1; i < tokens.size(); ) {
483 if (tokens[i] ==
"-layer") {
484 if (i == (tokens.size() - 1)) {
485 throw CommandException(
"Missing layer name");
488 throw CommandException(
489 "Layers are not supported for default bindings");
491 layer = tokens[i + 1];
493 auto it = tokens.begin() + i;
494 tokens.erase(it, it + 2);
495 }
else if (tokens[i] ==
"-layers") {
497 tokens.erase(tokens.begin() + i);
509 auto tokens = parse(defaultCmd, tokens_, layer, layers);
511 auto& cmdMap = defaultCmd
513 : layer.empty() ? hotKey.cmdMap
514 : hotKey.layerMap[layer];
518 for (
auto& p : hotKey.layerMap) {
521 if (!p.second.empty()) {
529 switch (tokens.size()) {
534 for (
auto& p : cmdMap) {
535 result += formatBinding(p);
540 auto it = cmdMap.find(createEvent(tokens[1]));
541 if (it == cmdMap.end()) {
544 result = formatBinding(*it);
552 if (tokens[2] ==
"-repeat") {
556 for (
unsigned i = start; i < tokens.size(); ++i) {
557 if (i != start) command +=
' ';
558 command += tokens[i];
561 auto event = createEvent(tokens[1]);
563 hotKey.bindDefault(event, info);
564 }
else if (layer.empty()) {
565 hotKey.bind(event, info);
567 hotKey.bindLayer(event, info, layer);
576 string cmd = getBindCmdName(defaultCmd);
577 return cmd +
" : show all bounded keys\n" +
578 cmd +
" <key> : show binding for this key\n" +
579 cmd +
" <key> [-repeat] <cmd> : bind key to command, optionally "
580 "repeat command while key remains pressed\n"
581 "These 3 take an optional '-layer <layername>' option, "
582 "see activate_input_layer." +
583 cmd +
" -layers : show a list of layers with bound keys\n";
589 static string getUnbindCmdName(
bool defaultCmd)
591 return defaultCmd ?
"unbind_default" :
"unbind";
595 HotKey& hotKey_,
bool defaultCmd_)
596 :
Command(commandController, getUnbindCmdName(defaultCmd_))
598 , defaultCmd(defaultCmd_)
606 auto tokens = parse(defaultCmd, tokens_, layer, layers);
610 if ((tokens.size() > 2) || (layer.empty() && (tokens.size() != 2))) {
615 if (tokens.size() == 2) {
616 event = createEvent(tokens[1]);
621 hotKey.unbindDefault(event);
622 }
else if (layer.empty()) {
624 hotKey.unbind(event);
627 hotKey.unbindLayer(event, layer);
629 hotKey.unbindFullLayer(layer);
636 string cmd = getUnbindCmdName(defaultCmd);
637 return cmd +
" <key> : unbind this key\n" +
638 cmd +
" -layer <layername> <key> : unbind key in a specific layer\n" +
639 cmd +
" -layer <layername> : unbind all keys in this layer\n";
646 :
Command(commandController,
"activate_input_layer")
654 bool blocking =
false;
655 for (
size_t i = 1; i < tokens.size(); ++i) {
656 if (tokens[i] ==
"-blocking") {
659 if (!layer.empty()) {
668 for (
auto it = hotKey.activeLayers.rbegin();
669 it != hotKey.activeLayers.rend(); ++it) {
672 result +=
" -blocking";
677 hotKey.activateLayer(layer, blocking);
684 return "activate_input_layer "
685 ": show list of active layers (most recent on top)\n"
686 "activate_input_layer [-blocking] <layername> "
687 ": activate new layer, optionally in blocking mode\n";
694 :
Command(commandController,
"deactivate_input_layer")
701 if (tokens.size() != 2) {
704 hotKey.deactivateLayer(tokens[1]);
710 return "deactivate_input_layer <layername> : deactive the given input layer";