diff options
author | Benjamin Barenblat <bbaren@mit.edu> | 2015-09-05 09:32:30 -0400 |
---|---|---|
committer | Benjamin Barenblat <bbaren@mit.edu> | 2015-09-05 09:32:30 -0400 |
commit | ea76b6988ccafaa6a4d4ed90f2489d0e49e1f180 (patch) | |
tree | b01dd19f5c53406d9d636b18bc49916bfdd6d3bc /g_src/enabler_input.cpp |
Imported Upstream version 0.40.24upstream/0.40.24upstream
Diffstat (limited to 'g_src/enabler_input.cpp')
-rwxr-xr-x | g_src/enabler_input.cpp | 1095 |
1 files changed, 1095 insertions, 0 deletions
diff --git a/g_src/enabler_input.cpp b/g_src/enabler_input.cpp new file mode 100755 index 0000000..e16486d --- /dev/null +++ b/g_src/enabler_input.cpp @@ -0,0 +1,1095 @@ +#include <SDL/SDL.h> +#include <map> +#include <vector> +#include <iostream> +#include <sstream> +#include <algorithm> +#include <stdlib.h> +#include <math.h> +using namespace std; + +#include "enabler_input.h" +#include "init.h" +extern initst init; +#include "platform.h" +#include "files.h" +#include "find_files.h" +#include "svector.h" +#include "curses.h" + +// The timeline events we actually pass back from get_input. Well, no, +// that's just k, but.. +struct Event { + Repeat r; + InterfaceKey k; + int repeats; // Starts at 0, increments once per repeat + int serial; + int time; + int tick; // The sim-tick at which we last returned this event + bool macro; // Created as part of macro playback. + + bool operator== (const Event &other) const { + if (r != other.r) return false; + if (k != other.k) return false; + if (repeats != other.repeats) return false; + if (serial != other.serial) return false; + if (time != other.time) return false; + if (macro != other.macro) return false; + return true; + } + + // We sort by time first, and then serial number. + // The order of the other bits is unimportant. + bool operator< (const Event &o) const { + if (time != o.time) return time < o.time; + if (serial != o.serial) return serial < o.serial; + if (r != o.r) return r < o.r; + if (k != o.k) return k < o.k; + if (repeats != o.repeats) return repeats < o.repeats; + if (macro != o.macro) return macro < o.macro; + return false; + } +}; + +// Used to decide which key-binding to display. As a heuristic, we +// prefer whichever display string is shortest. +struct less_sz { + bool operator() (const string &a, const string &b) const { + if (a.size() < b.size()) return true; + if (a.size() > b.size()) return false; + return a < b; + } +}; + +// These change dynamically in the normal process of DF +static int last_serial = 0; // Input serial number, to differentiate distinct physical presses +static set<Event> timeline; // A timeline of pending key events (for next get_input) +static set<EventMatch> pressed_keys; // Keys we consider "pressed" +static int modState; // Modifier state + +// These do not change as part of the normal dynamics of DF, only at startup/when editing. +static multimap<EventMatch,InterfaceKey> keymap; +static map<InterfaceKey,Repeat> repeatmap; +static map<InterfaceKey,set<string,less_sz> > keydisplay; // Used only for display, not for meaning + +// Macro recording +static bool macro_recording = false; +static macro active_macro; // Active macro +static map<string,macro> macros; +static Time macro_end = 0; // Time at which the currently playing macro will end + +// Prefix command state +static bool in_prefix_command = false; +static string prefix_command; + +// Keybinding editing +static bool key_registering = false; +static list<EventMatch> stored_keys; + +// Interface-file last loaded +static string interfacefile; + + +// Returns an unused serial number +static Time next_serial() { + return ++last_serial; +} + +static void update_keydisplay(InterfaceKey binding, string display) { + // Need to filter out space/tab, for obvious reasons. + if (display == " ") display = "Space"; + if (display == "\t") display = "Tab"; + map<InterfaceKey,set<string,less_sz> >::iterator it = keydisplay.find(binding); + if (it == keydisplay.end()) { + set<string,less_sz> s; s.insert(display); + keydisplay[binding] = s; + } else { + keydisplay[binding].insert(display); + } +} + +static void assertgood(ifstream &s) { + if (s.eof()) + MessageBox(NULL, "EOF while parsing keyboard bindings", 0, 0); + else if (!s.good()) + MessageBox(NULL, "I/O error while parsing keyboard bindings", 0, 0); + else + return; + abort(); +} + +// Decodes an UTF-8 encoded string into a /single/ UTF-8 character, +// discarding any overflow. Returns 0 on parse error. +int decode_utf8(const string &s) { + int unicode = 0, length, i; + if (s.length() == 0) return 0; + length = decode_utf8_predict_length(s[0]); + switch (length) { + case 1: unicode = s[0]; break; + case 2: unicode = s[0] & 0x1f; break; + case 3: unicode = s[0] & 0x0f; break; + case 4: unicode = s[0] & 0x07; break; + default: return 0; + } + + // Concatenate the follow-up bytes + if (s.length() < length) return 0; + for (i = 1; i < length; i++) { + if ((s[i] & 0xc0) != 0x80) return 0; + unicode = (unicode << 6) | (s[i] & 0x3f); + } + return unicode; +} + +// Returns the length of an utf-8 sequence, based on its first byte +int decode_utf8_predict_length(char byte) { + if ((byte & 0x80) == 0) return 1; + if ((byte & 0xe0) == 0xc0) return 2; + if ((byte & 0xf0) == 0xe0) return 3; + if ((byte & 0xf8) == 0xf0) return 4; + return 0; // Invalid start byte +} + +// Encode an arbitrary unicode value as a string. Returns an empty +// string if the value is out of range. +string encode_utf8(int unicode) { + string s; + int i; + if (unicode < 0 || unicode > 0x10ffff) return ""; // Out of range for utf-8 + else if (unicode <= 0x007f) { // 1-byte utf-8 + s.resize(1, 0); + } + else if (unicode <= 0x07ff) { // 2-byte utf-8 + s.resize(2, 0); + s[0] = 0xc0; + } + else if (unicode <= 0xffff) { // 3-byte utf-8 + s.resize(3, 0); + s[0] = 0xe0; + } + else { // 4-byte utf-8 + s.resize(4, 0); + s[0] = 0xf0; + } + + // Build up the string, right to left + for (i = s.length()-1; i > 0; i--) { + s[i] = 0x80 | (unicode & 0x3f); + unicode >>= 6; + } + // Finally, what's left goes in the low bits of s[0] + s[0] |= unicode; + return s; +} + +string translate_mod(Uint8 mod) { + string ret; + if (mod & 1) ret += "Shift+"; + if (mod & 2) ret += "Ctrl+"; + if (mod & 4) ret += "Alt+"; + return ret; +} + +static string display(const EventMatch &match) { + ostringstream ret; + ret << translate_mod(match.mod); + switch (match.type) { + case type_unicode: ret << (char)match.unicode; break; + case type_key: { + map<SDLKey,string>::iterator it = sdlNames.left.find(match.key); + if (it != sdlNames.left.end()) + ret << it->second; + else + ret << "SDL+" << (int)match.key; + break; + } + case type_button: + ret << "Button " << (int)match.button; + break; + } + return ret.str(); +} + +static string translate_repeat(Repeat r) { + switch (r) { + case REPEAT_NOT: return "REPEAT_NOT"; + case REPEAT_SLOW: return "REPEAT_SLOW"; + case REPEAT_FAST: return "REPEAT_FAST"; + default: return "REPEAT_BROKEN"; + } +} + +// Update the modstate, since SDL_getModState doesn't /work/ for alt +static void update_modstate(const SDL_Event &e) { + if (e.type == SDL_KEYUP) { + switch (e.key.keysym.sym) { + case SDLK_RSHIFT: + case SDLK_LSHIFT: + modState &= ~1; + break; + case SDLK_RCTRL: + case SDLK_LCTRL: + modState &= ~2; + break; + case SDLK_RALT: + case SDLK_LALT: + modState &= ~4; + break; + } + } else if (e.type == SDL_KEYDOWN) { + switch (e.key.keysym.sym) { + case SDLK_RSHIFT: + case SDLK_LSHIFT: + modState |= 1; + break; + case SDLK_RCTRL: + case SDLK_LCTRL: + modState |= 2; + break; + case SDLK_RALT: + case SDLK_LALT: + modState |= 4; + break; + } + } +} + +// Converts SDL mod states to ours, collapsing left/right shift/alt/ctrl +Uint8 getModState() { + return modState; +} + +// Not sure what to call this, but it ain't using regexes. +static bool parse_line(const string &line, const string ®ex, vector<string> &parts) { + parts.clear(); + parts.push_back(line); + int bytes; + for (int l = 0, r = 0; r < regex.length();) { + switch (regex[r]) { + case '*': // Read until ], : or the end of the line, but at least one character. + { + const int start = l; + for (; l < line.length() && (l == start || (line[l] != ']' && line[l] != ':')); l++) + ; + parts.push_back(line.substr(start, l - start)); + r++; + } + break; + default: + if (line[l] != regex[r]) return false; + r++; l++; + break; + } + } + // We've made it this far, clearly the string parsed + return true; +} + +void enabler_inputst::load_keybindings(const string &file) { + cout << "Loading bindings from " << file << endl; + interfacefile = file; + ifstream s(file.c_str()); + if (!s.good()) { + MessageBox(NULL, (file + " not found, or I/O error encountered").c_str(), 0, 0); + abort(); + } + + list<string> lines; + while (s.good()) { + string line; + getline(s, line); + lines.push_back(line); + } + + static const string bind("[BIND:*:*]"); + static const string sym("[SYM:*:*]"); + static const string key("[KEY:*]"); + static const string button("[BUTTON:*:*]"); + + list<string>::iterator line = lines.begin(); + vector<string> match; + + while (line != lines.end()) { + if (parse_line(*line, bind, match)) { + map<string,InterfaceKey>::iterator it = bindingNames.right.find(match[1]); + if (it != bindingNames.right.end()) { + InterfaceKey binding = it->second; + // Parse repeat data + if (match[2] == "REPEAT_FAST") + repeatmap[(InterfaceKey)binding] = REPEAT_FAST; + else if (match[2] == "REPEAT_SLOW") + repeatmap[(InterfaceKey)binding] = REPEAT_SLOW; + else if (match[2] == "REPEAT_NOT") + repeatmap[(InterfaceKey)binding] = REPEAT_NOT; + else { + repeatmap[(InterfaceKey)binding] = REPEAT_NOT; + cout << "Broken repeat request: " << match[2] << endl; + } + ++line; + // Add symbols/keys/buttons + while (line != lines.end()) { + EventMatch matcher; + // SDL Keys + if (parse_line(*line, sym, match)) { + map<string,SDLKey>::iterator it = sdlNames.right.find(match[2]); + if (it != sdlNames.right.end()) { + matcher.mod = atoi(string(match[1]).c_str()); + matcher.type = type_key; + matcher.key = it->second; + keymap.insert(pair<EventMatch,InterfaceKey>(matcher, (InterfaceKey)binding)); + update_keydisplay(binding, display(matcher)); + } else { + cout << "Unknown SDLKey: " << match[2] << endl; + } + ++line; + } // Unicode + else if (parse_line(*line, key, match)) { + matcher.type = type_unicode; + matcher.unicode = decode_utf8(match[1]); + matcher.mod = KMOD_NONE; + if (matcher.unicode) { + keymap.insert(make_pair(matcher, (InterfaceKey)binding)); + if (matcher.unicode < 256) { + // This unicode key is part of the latin-1 mapped portion of unicode, so we can + // actually display it. Nice. + char c[2] = {char(matcher.unicode), 0}; + update_keydisplay(binding, display(matcher)); + } + } else { + cout << "Broken unicode: " << *line << endl; + } + ++line; + } // Mouse buttons + else if (parse_line(*line, button, match)) { + matcher.type = type_button; + string str = match[2]; + matcher.button = atoi(str.c_str()); + if (matcher.button) { + matcher.mod = atoi(string(match[1]).c_str()); + keymap.insert(pair<EventMatch,InterfaceKey>(matcher, (InterfaceKey)binding)); + update_keydisplay(binding, display(matcher)); + } else { + cout << "Broken button (should be [BUTTON:#:#]): " << *line << endl; + } + ++line; + } else { + break; + } + } + } else { + cout << "Unknown binding: " << match[1] << endl; + ++line; + } + } else { + // Retry with next line + ++line; + } + } +} + +void enabler_inputst::save_keybindings(const string &file) { + cout << "Saving bindings to " << file << endl; + string temporary = file + ".partial"; + ofstream s(temporary.c_str()); + multimap<InterfaceKey,EventMatch> map; + InterfaceKey last_key = INTERFACEKEY_NONE; + + if (!s.good()) { + string t = "Failed to open " + temporary + " for writing"; + MessageBox(NULL, t.c_str(), 0, 0); + s.close(); + return; + } + // Invert keyboard map + for (multimap<EventMatch,InterfaceKey>::iterator it = keymap.begin(); it != keymap.end(); ++it) + map.insert(pair<InterfaceKey,EventMatch>(it->second,it->first)); + // Insert an empty line for the benefit of note/wordpad + s << endl; + // And write. + for (multimap<InterfaceKey,EventMatch>::iterator it = map.begin(); it != map.end(); ++it) { + if (!s.good()) { + MessageBox(NULL, "I/O error while writing keyboard mapping", 0, 0); + s.close(); + return; + } + if (it->first != last_key) { + last_key = it->first; + s << "[BIND:" << bindingNames.left[it->first] << ":" + << translate_repeat(repeatmap[it->first]) << "]" << endl; + } + switch (it->second.type) { + case type_unicode: + s << "[KEY:" << encode_utf8(it->second.unicode) << "]" << endl; + break; + case type_key: + s << "[SYM:" << (int)it->second.mod << ":" << sdlNames.left[it->second.key] << "]" << endl; + break; + case type_button: + s << "[BUTTON:" << (int)it->second.mod << ":" << (int)it->second.button << "]" << endl; + break; + } + } + s.close(); + replace_file(temporary, file); +} + +void enabler_inputst::save_keybindings() { + save_keybindings(interfacefile); +} + +void enabler_inputst::add_input(SDL_Event &e, Uint32 now) { + // Before we can use this input, there are some issues to deal with: + // - SDL provides unicode translations only for key-press events, not + // releases. We need to keep track of pressed keys, and generate + // unicode release events whenever any modifiers are hit, or if + // that raw keycode is released. + // - Generally speaking, when modifiers are hit/released, we discard those + // events and generate press/release events for all pressed non-modifiers. + // - It's possible for multiple events to be generated on the same tick. + // These are of course separate keypresses, and must be kept separate. + // That's what the serial is for. + + set<EventMatch>::iterator pkit; + list<pair<KeyEvent, int> > synthetics; + update_modstate(e); + + // Convert modifier state changes + if ((e.type == SDL_KEYUP || e.type == SDL_KEYDOWN) && + (e.key.keysym.sym == SDLK_RSHIFT || + e.key.keysym.sym == SDLK_LSHIFT || + e.key.keysym.sym == SDLK_RCTRL || + e.key.keysym.sym == SDLK_LCTRL || + e.key.keysym.sym == SDLK_RALT || + e.key.keysym.sym == SDLK_LALT )) { + for (pkit = pressed_keys.begin(); pkit != pressed_keys.end(); ++pkit) { + // Release currently pressed keys + KeyEvent synth; + synth.release = true; + synth.match = *pkit; + synthetics.push_back(make_pair(synth, next_serial())); + // Re-press them, with new modifiers, if they aren't unicode. We can't re-translate unicode. + if (synth.match.type != type_unicode) { + synth.release = false; + synth.match.mod = getModState(); + if (!key_registering) // We don't want extras when registering keys + synthetics.push_back(make_pair(synth, next_serial())); + } + } + } else { + // It's not a modifier. If this is a key release, then we still need + // to find and release pressed unicode keys with this scancode + if (e.type == SDL_KEYUP) { + for (pkit = pressed_keys.begin(); pkit != pressed_keys.end(); ++pkit) { + if (pkit->type == type_unicode && pkit->scancode == e.key.keysym.scancode) { + KeyEvent synth; + synth.release = true; + synth.match = *pkit; + synthetics.push_back(make_pair(synth, next_serial())); + } + } + } + // Since it's not a modifier, we also pass on symbolic/button + // (always) and unicode (if defined) events + // + // However, since SDL ignores(?) ctrl and alt when translating to + // unicode, we want to ignore unicode events if those are set. + const int serial = next_serial(); + + KeyEvent real; + real.release = (e.type == SDL_KEYUP || e.type == SDL_MOUSEBUTTONUP) ? true : false; + real.match.mod = getModState(); + if (e.type == SDL_MOUSEBUTTONUP || e.type == SDL_MOUSEBUTTONDOWN) { + real.match.type = type_button; + real.match.scancode = 0; + real.match.button = e.button.button; + synthetics.push_back(make_pair(real, serial)); + } + if (e.type == SDL_KEYUP || e.type == SDL_KEYDOWN) { + real.match.type = type_key; + real.match.scancode = e.key.keysym.scancode; + real.match.key = e.key.keysym.sym; + synthetics.push_back(make_pair(real, serial)); + } + if (e.type == SDL_KEYDOWN && e.key.keysym.unicode && getModState() < 2) { + real.match.mod = KMOD_NONE; + real.match.type = type_unicode; + real.match.scancode = e.key.keysym.scancode; + real.match.unicode = e.key.keysym.unicode; + synthetics.push_back(make_pair(real, serial)); + } + if (e.type == SDL_QUIT) { + // This one, we insert directly into the timeline. + Event e = {REPEAT_NOT, (InterfaceKey)INTERFACEKEY_OPTIONS, 0, next_serial(), now, 0}; + timeline.insert(e); + } + } + + list<pair<KeyEvent, int> >::iterator lit; + for (lit = synthetics.begin(); lit != synthetics.end(); ++lit) { + // Add or remove the key from pressed_keys, keeping that up to date + if (lit->first.release) pressed_keys.erase(lit->first.match); + else pressed_keys.insert(lit->first.match); + // And pass the event on deeper. + add_input_refined(lit->first, now, lit->second); + } +} + +// Input encoding: +// 1 and up are ncurses symbols, as returned by getch. +// -1 and down are unicode values. +// esc is true if this key was part of an escape sequence. +#ifdef CURSES +void enabler_inputst::add_input_ncurses(int key, Time now, bool esc) { + // TODO: Deal with shifted arrow keys, etc. See man 5 terminfo and tgetent. + + EventMatch sdl, uni; // Each key may provoke an unicode event, an "SDL-key" event, or both + const int serial = next_serial(); + sdl.type = type_key; + uni.type = type_unicode; + sdl.scancode = uni.scancode = 0; // We don't use this.. hang on, who does? ..nobody. FIXME! + sdl.mod = uni.mod = 0; + sdl.key = SDLK_UNKNOWN; + uni.unicode = 0; + + if (esc) { // Escape sequence, meaning alt was held. I hope. + sdl.mod = uni.mod = DFMOD_ALT; + } + + if (key == -10) { // Return + sdl.key = SDLK_RETURN; + uni.unicode = '\n'; + } else if (key == -9) { // Tab + sdl.key = SDLK_TAB; + uni.unicode = '\t'; + } else if (key == -27) { // If we see esc here, it's the actual esc key. Hopefully. + sdl.key = SDLK_ESCAPE; + } else if (key == -127) { // Backspace/del + sdl.key = SDLK_BACKSPACE; + } else if (key < 0 && key >= -26) { // Control-a through z (but not ctrl-j, or ctrl-i) + sdl.mod |= DFMOD_CTRL; + sdl.key = (SDLKey)(SDLK_a + (-key) - 1); + } else if (key <= -32 && key >= -126) { // ASCII character set + uni.unicode = -key; + sdl.key = (SDLKey)-key; // Most of this maps directly to SDL keys, except.. + if (sdl.key > 64 && sdl.key < 91) { // Uppercase + sdl.key = (SDLKey)(sdl.key + 32); // Maps to lowercase, and + sdl.mod |= DFMOD_SHIFT; // Add shift. + } + } else if (key < -127) { // Unicode, no matching SDL keys + uni.unicode = -key; + } else if (key > 0) { // Symbols such as arrow-keys, etc, no matching unicode. + switch (key) { + case KEY_DOWN: sdl.key = SDLK_DOWN; break; + case KEY_UP: sdl.key = SDLK_UP; break; + case KEY_LEFT: sdl.key = SDLK_LEFT; break; + case KEY_RIGHT: sdl.key = SDLK_RIGHT; break; + case KEY_BACKSPACE: sdl.key = SDLK_BACKSPACE; break; + case KEY_F(1): sdl.key = SDLK_F1; break; + case KEY_F(2): sdl.key = SDLK_F2; break; + case KEY_F(3): sdl.key = SDLK_F3; break; + case KEY_F(4): sdl.key = SDLK_F4; break; + case KEY_F(5): sdl.key = SDLK_F5; break; + case KEY_F(6): sdl.key = SDLK_F6; break; + case KEY_F(7): sdl.key = SDLK_F7; break; + case KEY_F(8): sdl.key = SDLK_F8; break; + case KEY_F(9): sdl.key = SDLK_F9; break; + case KEY_F(10): sdl.key = SDLK_F10; break; + case KEY_F(11): sdl.key = SDLK_F11; break; + case KEY_F(12): sdl.key = SDLK_F12; break; + case KEY_F(13): sdl.key = SDLK_F13; break; + case KEY_F(14): sdl.key = SDLK_F14; break; + case KEY_F(15): sdl.key = SDLK_F15; break; + case KEY_DC: sdl.key = SDLK_DELETE; break; + case KEY_NPAGE: sdl.key = SDLK_PAGEDOWN; break; + case KEY_PPAGE: sdl.key = SDLK_PAGEUP; break; + case KEY_ENTER: sdl.key = SDLK_RETURN; break; + } + } + + // We may be registering a new mapping, in which case we skip the + // rest of this function. + if (key_registering) { + if (uni.unicode) { + stored_keys.push_back(uni); + } + if (sdl.key) { + stored_keys.push_back(sdl); + } + Event e; e.r = REPEAT_NOT; e.repeats = 0; e.time = now; e.serial = serial; e.k = INTERFACEKEY_KEYBINDING_COMPLETE; e.tick = enabler.simticks.read(); + timeline.insert(e); + key_registering = false; + return; + } + + // Key repeat is handled by the terminal, and we don't get release + // events anyway. + KeyEvent kev; kev.release = false; + Event e; e.r = REPEAT_NOT; e.repeats = 0; e.time = now; + if (sdl.key) { + set<InterfaceKey> events = key_translation(sdl); + for (set<InterfaceKey>::iterator k = events.begin(); k != events.end(); ++k) { + e.serial = serial; + e.k = *k; + timeline.insert(e); + } + } + if (uni.unicode) { + set<InterfaceKey> events = key_translation(uni); + for (set<InterfaceKey>::iterator k = events.begin(); k != events.end(); ++k) { + e.serial = serial; + e.k = *k; + timeline.insert(e); + } + } +} +#endif + +void enabler_inputst::add_input_refined(KeyEvent &e, Uint32 now, int serial) { + // We may be registering a new mapping, in which case we skip the + // rest of this function. + if (key_registering && !e.release) { + stored_keys.push_back(e.match); + Event e; e.r = REPEAT_NOT; e.repeats = 0; e.time = now; e.serial = serial; e.k = INTERFACEKEY_KEYBINDING_COMPLETE; e.tick = enabler.simticks.read(); + timeline.insert(e); + return; + } + + // If this is a key-press event, we add it to the timeline. If it's + // a release, we remove any pending repeats, but not those that + // haven't repeated yet (which are on their first cycle); those we + // just set to non-repeating. + set<InterfaceKey> keys = key_translation(e.match); + if (e.release) { + set<Event>::iterator it = timeline.begin(); + while (it != timeline.end()) { + set<Event>::iterator el = it++; + if (keys.count(el->k)) { + if (el->repeats) { + timeline.erase(el); + } else { + Event new_el = *el; + new_el.r = REPEAT_NOT; + timeline.erase(el); + timeline.insert(new_el); + } + } + } + } else { + set<InterfaceKey>::iterator key; + // As policy, when the user hits a non-repeating key we'd want to + // also cancel any keys that are currently repeating. This allows + // for easy recovery from stuck keys. + // + // Unfortunately, each key may be bound to multiple + // commands. So, lacking information on which commands are + // accepted at the moment, there is no way we can know if it's + // okay to cancel repeats unless /all/ the bindings are + // non-repeating. + for (set<InterfaceKey>::iterator k = keys.begin(); k != keys.end(); ++k) { + Event e = {key_repeat(*k), *k, 0, serial, now, enabler.simticks.read()}; + timeline.insert(e); + } + // if (cancel_ok) { + // // Set everything on the timeline to non-repeating + // multimap<Time,Event>::iterator it; + // for (it = timeline.begin(); it != timeline.end(); ++it) { + // it->second.r = REPEAT_NOT; + // } + } +} + + +void enabler_inputst::clear_input() { + timeline.clear(); + pressed_keys.clear(); + modState = 0; + last_serial = 0; +} + +set<InterfaceKey> enabler_inputst::get_input(Time now) { + // We walk the timeline, returning all events corresponding to a + // single physical keypress, and inserting repeats relative to the + // current time, not when the events we're now returning were + // *supposed* to happen. + + set<InterfaceKey> input; + set<Event>::iterator ev = timeline.begin(); + if (ev == timeline.end() || ev->time > now) { + return input; // No input (yet). + } + + const Time first_time = ev->time; + const int first_serial = ev->serial; + int simtick = enabler.simticks.read(); + bool event_from_macro = false; + while (ev != timeline.end() && ev->time == first_time && ev->serial == first_serial) { + // Avoid recording macro-sources events as macro events. + if (ev->macro) event_from_macro = true; + // To make sure the user had a chance to cancel (by lifting the key), we require there + // to be at least three simulation ticks before the first repeat. + if (ev->repeats == 1 && ev->tick > simtick - 3) { + } else { + input.insert(ev->k); + } + // Schedule a repeat + Event next = *ev; + next.repeats++; + switch (next.r) { + case REPEAT_NOT: + break; + case REPEAT_SLOW: + if (ev->repeats == 0) { + next.time = now + init.input.hold_time; + timeline.insert(next); + break; + } + case REPEAT_FAST: + double accel = 1; + if (ev->repeats >= init.input.repeat_accel_start) { + // Compute acceleration + accel = MIN(init.input.repeat_accel_limit, + sqrt(double(next.repeats - init.input.repeat_accel_start) + 16) - 3); + } + next.time = now + double(init.input.repeat_time) / accel; + timeline.insert(next); + break; + } + // Delete the event from the timeline and iterate + timeline.erase(ev++); + } +#ifdef DEBUG + if (input.size() && !init.display.flag.has_flag(INIT_DISPLAY_FLAG_TEXT)) { + cout << "Returning input:\n"; + set<InterfaceKey>::iterator it; + for (it = input.begin(); it != input.end(); ++it) + cout << " " << GetKeyDisplay(*it) << ": " << GetBindingDisplay(*it) << endl; + } +#endif + // It could be argued that the "record event" step of recording + // belongs in add_input, not here. I don't hold with this + // argument. The whole point is to record events as the user seems + // them happen. + if (macro_recording && !event_from_macro) { + set<InterfaceKey> macro_input = input; + macro_input.erase(INTERFACEKEY_RECORD_MACRO); + macro_input.erase(INTERFACEKEY_PLAY_MACRO); + macro_input.erase(INTERFACEKEY_SAVE_MACRO); + macro_input.erase(INTERFACEKEY_LOAD_MACRO); + if (macro_input.size()) + active_macro.push_back(macro_input); + } + return input; +} + +set<InterfaceKey> enabler_inputst::key_translation(EventMatch &match) { + set<InterfaceKey> bindings; + pair<multimap<EventMatch,InterfaceKey>::iterator,multimap<EventMatch,InterfaceKey>::iterator> its; + + for (its = keymap.equal_range(match); its.first != its.second; ++its.first) + bindings.insert((its.first)->second); + + return bindings; +} + +string enabler_inputst::GetKeyDisplay(int binding) { + map<InterfaceKey,set<string,less_sz> >::iterator it = keydisplay.find(binding); + if (it != keydisplay.end() && it->second.size()) + return *it->second.begin(); + else { + cout << "Missing binding displayed: " + bindingNames.left[binding] << endl; + return "?"; + } +} + +string enabler_inputst::GetBindingDisplay(int binding) { + map<InterfaceKey,string>::iterator it = bindingNames.left.find(binding); + if (it != bindingNames.left.end()) + return it->second; + else + return "NO BINDING"; +} + +string enabler_inputst::GetBindingTextDisplay(int binding) { + map<InterfaceKey,string>::iterator it = displayNames.left.find(binding); + if (it !=displayNames.left.end()) + return it->second; + else + return "NO BINDING"; +} + +Repeat enabler_inputst::key_repeat(InterfaceKey binding) { + map<InterfaceKey,Repeat>::iterator it = repeatmap.find(binding); + if (it != repeatmap.end()) + return it->second; + else + return REPEAT_NOT; +} + +void enabler_inputst::key_repeat(InterfaceKey binding, Repeat repeat) { + repeatmap[binding] = repeat; +} + +void enabler_inputst::record_input() { + active_macro.clear(); + macro_recording = true; +} + +void enabler_inputst::record_stop() { + macro_recording = false; +} + +bool enabler_inputst::is_recording() { + return macro_recording; +} + +void enabler_inputst::play_macro() { + Time now = SDL_GetTicks(); + for_each(timeline.begin(), timeline.end(), [&](Event e){ + now = MAX(now, e.time); + }); + for (macro::iterator sim = active_macro.begin(); sim != active_macro.end(); ++sim) { + Event e; e.r = REPEAT_NOT; e.repeats = 0; e.serial = next_serial(); e.time = now; + e.macro = true; // Avoid exponential macro blowup. + for (set<InterfaceKey>::iterator k = sim->begin(); k != sim->end(); ++k) { + e.k = *k; + timeline.insert(e); + now += init.input.macro_time; + } + } + macro_end = MAX(macro_end, now); +} + +bool enabler_inputst::is_macro_playing() { + return SDL_GetTicks() <= macro_end; +} + +// Replaces any illegal letters. +static string filter_filename(string name, char replacement) { + for (int i = 0; i < name.length(); i++) { + switch (name[i]) { + case '<': name[i] = replacement; break; + case '>': name[i] = replacement; break; + case ':': name[i] = replacement; break; + case '"': name[i] = replacement; break; + case '/': name[i] = replacement; break; + case '\\': name[i] = replacement; break; + case '|': name[i] = replacement; break; + case '?': name[i] = replacement; break; + case '*': name[i] = replacement; break; + } + if (name[i] <= 31) name[i] = replacement; + } + return name; +} + +void enabler_inputst::load_macro_from_file(const string &file) { + ifstream s(file.c_str()); + char buf[100]; + s.getline(buf, 100); + string name(buf); + if (macros.find(name) != macros.end()) return; // Already got it. + + macro macro; + set<InterfaceKey> group; + for(;;) { + s.getline(buf, 100); + if (!s.good()) { + MessageBox(NULL, "I/O error while loading macro", 0, 0); + s.close(); + return; + } + string line(buf); + if (line == "End of macro") { + if (group.size()) macro.push_back(group); + break; + } else if (line == "\tEnd of group") { + if (group.size()) macro.push_back(group); + group.clear(); + } else if (line.substr(0,2) != "\t\t" ) { + if( line.substr(1).find("\t") != string::npos) { + // expecting /t##/tCMD for a repeated command + istringstream ss(line.substr(1)); + int count; + string remainingline; + + if(ss >> count) { + ss >> remainingline; + if(remainingline.size()) { + for(int i=0; i < count; i++) { + map<string,InterfaceKey>::iterator it = bindingNames.right.find(remainingline); + if (it == bindingNames.right.end()) { + cout << "Binding name unknown while loading macro: " << line.substr(1) << endl; + } else { + group.insert(it->second); + if (group.size()) macro.push_back(group); + group.clear(); + } + } + } + else { + cout << "Binding missing while loading macro: " << line.substr(1) << endl; + } + } else { + cout << "Quantity not numeric or Unexpected tab(s) while loading macro: " << line.substr(1) << endl; + } + } + else + { + // expecting /tCMD for a non-grouped command + map<string,InterfaceKey>::iterator it = bindingNames.right.find(line.substr(1)); + if (it == bindingNames.right.end()) { + cout << "Binding name unknown while loading macro: " << line.substr(1) << endl; + } else { + group.insert(it->second); + if (group.size()) macro.push_back(group); + group.clear(); + } + } + } else { + map<string,InterfaceKey>::iterator it = bindingNames.right.find(line.substr(2)); + if (it == bindingNames.right.end()) + cout << "Binding name unknown while loading macro: " << line.substr(2) << endl; + else + group.insert(it->second); + } + } + if (s.good()) + macros[name] = macro; + else + MessageBox(NULL, "I/O error while loading macro", 0, 0); + s.close(); +} + +void enabler_inputst::save_macro_to_file(const string &file, const string &name, const macro ¯o) { + ofstream s(file.c_str()); + s << name << endl; + for (macro::const_iterator group = macro.begin(); group != macro.end(); ++group) { + for (set<InterfaceKey>::const_iterator key = group->begin(); key != group->end(); ++key) + s << "\t\t" << bindingNames.left[*key] << endl; + s << "\tEnd of group" << endl; + } + s << "End of macro" << endl; + s.close(); +} + +list<string> enabler_inputst::list_macros() { + // First, check for unloaded macros + svector<char*> files; + find_files_by_pattern("data/init/macros/*.mak", files); + for (int i = 0; i < files.size(); i++) { + string file(files[i]); + delete files[i]; + file = "data/init/macros/" + file; + load_macro_from_file(file); + } + // Then return all in-memory macros + list<string> ret; + for (map<string,macro>::iterator it = macros.begin(); it != macros.end(); ++it) + ret.push_back(it->first); + return ret; +} + +void enabler_inputst::load_macro(string name) { + if (macros.find(name) != macros.end()) + active_macro = macros[name]; + else + macros.clear(); +} + +void enabler_inputst::save_macro(string name) { + macros[name] = active_macro; + save_macro_to_file("data/init/macros/" + filter_filename(name, '_') + ".mak", name, active_macro); +} + +void enabler_inputst::delete_macro(string name) { + map<string,macro>::iterator it = macros.find(name); + if (it != macros.end()) macros.erase(it); + // TODO: Store the filename it was loaded from instead + string filename = "data/init/macros/" + filter_filename(name, '_') + ".mak"; + remove(filename.c_str()); +} + + +// Sets the next key-press to be stored instead of executed. +void enabler_inputst::register_key() { + key_registering = true; + stored_keys.clear(); +} + +// Returns a description of stored keys. Max one of each type. +list<RegisteredKey> enabler_inputst::getRegisteredKey() { + key_registering = false; + list<RegisteredKey> ret; + for (list<EventMatch>::iterator it = stored_keys.begin(); it != stored_keys.end(); ++it) { + struct RegisteredKey r = {it->type, display(*it)}; + ret.push_back(r); + } + return ret; +} + +// Binds one of the stored keys to key +void enabler_inputst::bindRegisteredKey(MatchType type, InterfaceKey key) { + for (list<EventMatch>::iterator it = stored_keys.begin(); it != stored_keys.end(); ++it) { + if (it->type == type) { + keymap.insert(pair<EventMatch,InterfaceKey>(*it, key)); + update_keydisplay(key, display(*it)); + } + } +} + +bool enabler_inputst::is_registering() { + return key_registering; +} + + +list<EventMatch> enabler_inputst::list_keys(InterfaceKey key) { + list<EventMatch> ret; + // Oh, now this is inefficient. + for (multimap<EventMatch,InterfaceKey>::iterator it = keymap.begin(); it != keymap.end(); ++it) + if (it->second == key) ret.push_back(it->first); + return ret; +} + +void enabler_inputst::remove_key(InterfaceKey key, EventMatch ev) { + for (multimap<EventMatch,InterfaceKey>::iterator it = keymap.find(ev); + it != keymap.end() && it->first == ev; + ++it) { + if (it->second == key) keymap.erase(it++); + } + // Also remove the key from key displaying, assuming we can find it + map<InterfaceKey,set<string,less_sz> >::iterator it = keydisplay.find(key); + if (it != keydisplay.end()) + it->second.erase(display(ev)); +} + +bool enabler_inputst::prefix_building() { + return in_prefix_command; +} + +void enabler_inputst::prefix_toggle() { + in_prefix_command = !in_prefix_command; + prefix_command.clear(); +} + +void enabler_inputst::prefix_add_digit(char digit) { + prefix_command.push_back(digit); +#ifdef DEBUG + cout << "Built prefix to " << prefix_command << endl; +#endif + if (atoi(prefix_command.c_str()) > 99) + prefix_command = "99"; // Let's not go overboard here. +} + +int enabler_inputst::prefix_end() { + if (prefix_command.size()) { + int repeats = atoi(prefix_command.c_str()); + prefix_toggle(); + return repeats; + } else { + return 1; + } +} + +string enabler_inputst::prefix() { + return prefix_command; +} |