Refactor keygroup into library/
[lsnes.git] / src / platform / sdl / commandline.cpp
blobaea101cd2494e62a4e6f4de8a1f871daa19ec97b
1 #include "platform/sdl/platform.hpp"
3 #include <sstream>
4 #define MAXHISTORY 1000
6 namespace
8 volatile uint32_t autorepeat_first = 10;
9 volatile uint32_t autorepeat_subsequent = 4;
11 bool is_whitespace(uint32_t cp)
13 switch(cp) {
14 case 12:
15 case 32:
16 case 9:
17 case 0x1680:
18 case 0x180E:
19 case 0x2000:
20 case 0x2001:
21 case 0x2002:
22 case 0x2003:
23 case 0x2004:
24 case 0x2005:
25 case 0x2006:
26 case 0x2007:
27 case 0x2008:
28 case 0x2009:
29 case 0x200A:
30 case 0x2028:
31 case 0x205F:
32 case 0x3000:
33 return true;
34 default:
35 return false;
40 commandline_model::commandline_model() throw()
42 enabled_flag = false;
43 autorepeating_key = SPECIAL_NOOP;
44 autorepeat_phase = 0;
45 cursor_pos = 0;
46 overwrite_mode = false;
49 std::string commandline_model::key(uint32_t k) throw(std::bad_alloc)
51 std::string ret;
52 switch(k) {
53 case SPECIAL_NOOP:
54 return "";
55 case SPECIAL_ACK:
56 history_itr = history.end();
57 history.push_back(codepoints);
58 if(history.size() > MAXHISTORY)
59 history.pop_front();
60 ret = read_command();
61 enabled_flag = false;
62 return ret;
63 case SPECIAL_NAK:
64 enabled_flag = false;
65 return "";
66 default:
67 //Fall through.
71 //Set up autorepeat if needed.
72 if(k & PRESSED_MASK) {
73 autorepeating_key = k;
74 autorepeat_counter = 0;
75 autorepeat_phase = 1;
76 } else {
77 //These all are active on positive edge.
78 autorepeating_key = SPECIAL_NOOP;
79 autorepeat_counter = 0;
80 autorepeat_phase = 0;
81 return "";
84 switch(k & ~PRESSED_MASK) {
85 case SPECIAL_INSERT:
86 overwrite_mode = !overwrite_mode;
87 case SPECIAL_HOME:
88 cursor_pos = 0;
89 return "";
90 case SPECIAL_END:
91 cursor_pos = codepoints.size();
92 return "";
93 case SPECIAL_PGUP:
94 case SPECIAL_UP:
95 scroll_history_up();
96 return "";
97 case SPECIAL_PGDN:
98 case SPECIAL_DOWN:
99 scroll_history_down();
100 return "";
101 case SPECIAL_LEFT:
102 if(cursor_pos > 0)
103 cursor_pos--;
104 return "";
105 case SPECIAL_RIGHT:
106 if(cursor_pos < codepoints.size())
107 cursor_pos++;
108 return "";
109 case SPECIAL_BACKSPACE:
110 if(cursor_pos == 0)
111 //Nothing to delete.
112 return "";
113 delete_codepoint(cursor_pos - 1);
114 cursor_pos--;
115 return "";
116 case SPECIAL_DELETE:
117 if(cursor_pos == codepoints.size())
118 //Nothing to delete.
119 return "";
120 delete_codepoint(cursor_pos);
121 return "";
122 case SPECIAL_LEFT_WORD:
123 while(cursor_pos > 0 && (cursor_pos == codepoints.size() || isspace(codepoints[cursor_pos])))
124 cursor_pos--;
125 while(cursor_pos > 0 && (cursor_pos == codepoints.size() || !isspace(codepoints[cursor_pos])))
126 cursor_pos--;
127 //If the previous position is whitespace, back off to it.
128 while(cursor_pos > 0 && isspace(codepoints[cursor_pos - 1]))
129 cursor_pos--;
130 return "";
131 case SPECIAL_RIGHT_WORD:
132 while(cursor_pos < codepoints.size() && isspace(codepoints[cursor_pos]))
133 cursor_pos++;
134 while(cursor_pos < codepoints.size() && !isspace(codepoints[cursor_pos]))
135 cursor_pos++;
136 return "";
137 case SPECIAL_DELETE_WORD:
138 while(cursor_pos > 0 && !isspace(codepoints[cursor_pos - 1])) {
139 delete_codepoint(cursor_pos - 1);
140 cursor_pos--;
142 while(cursor_pos > 0 && isspace(codepoints[cursor_pos - 1])) {
143 delete_codepoint(cursor_pos - 1);
144 cursor_pos--;
146 return "";
147 case 0:
148 case PRESSED_MASK:
149 //Eh?
150 return "";
151 default:
152 //This can't be NOOP, ACK nor NAK because those were checked above, nor is it special. Therefore
153 //it is a character.
154 handle_cow();
155 if(!overwrite_mode || cursor_pos == codepoints.size()) {
156 codepoints.resize(codepoints.size() + 1);
157 for(size_t i = codepoints.size() - 1; i > cursor_pos; i--)
158 codepoints[i] = codepoints[i - 1];
160 codepoints[cursor_pos++] = k & ~PRESSED_MASK;
161 return "";
165 void commandline_model::tick() throw(std::bad_alloc)
167 if(autorepeat_phase == 0 || !enabled_flag)
168 return;
169 if(autorepeat_phase == 1) {
170 autorepeat_counter++;
171 if(autorepeat_counter >= autorepeat_first) {
172 key(autorepeating_key);
173 autorepeat_phase = 2;
174 autorepeat_counter = 0;
177 if(autorepeat_phase == 2) {
178 autorepeat_counter++;
179 if(autorepeat_counter >= autorepeat_subsequent) {
180 key(autorepeating_key);
181 autorepeat_counter = 0;
186 size_t commandline_model::cursor() throw()
188 return enabled_flag ? cursor_pos : 0;
191 bool commandline_model::enabled() throw()
193 return enabled_flag;
196 bool commandline_model::overwriting() throw()
198 return overwrite_mode;
201 void commandline_model::enable(const std::string& cmd) throw()
203 enable();
204 unsigned left = 0;
205 unsigned tmp = 0;
206 for(size_t itr = 0; itr < cmd.length(); itr++) {
207 unsigned char ch = cmd[itr];
208 if(ch < 128)
209 codepoints.push_back(ch);
210 else if(left) {
211 left--;
212 tmp = tmp * 64 + (ch & 0x3F);
213 if(!left)
214 codepoints.push_back(tmp);
215 } else if(ch < 192) {
216 } else if(ch < 224) {
217 left = 1;
218 tmp = ch & 0x1F;
219 } else if(ch < 240) {
220 left = 2;
221 tmp = ch & 0x0F;
222 } else if(ch < 248) {
223 left = 3;
224 tmp = ch & 0x07;
227 cursor_pos = codepoints.size();
230 void commandline_model::enable() throw()
232 if(enabled_flag)
233 return;
234 enabled_flag = true;
235 autorepeat_phase = 0;
236 autorepeat_counter = 0;
237 codepoints.clear();
238 cursor_pos = 0;
239 overwrite_mode = false;
240 history_itr = history.end();
243 std::string commandline_model::read_command() throw(std::bad_alloc)
245 std::ostringstream o;
246 for(auto i : codepoints) {
247 char buf[5] = {0, 0, 0, 0, 0};
248 if(i < 0x80)
249 buf[0] = i;
250 else if(i < 0x800) {
251 buf[0] = 0xC0 | (i >> 6);
252 buf[1] = 0x80 | (i & 63);
253 } else if(i < 0x10000) {
254 buf[0] = 0xE0 | (i >> 12);
255 buf[1] = 0x80 | ((i >> 6) & 63);
256 buf[2] = 0x80 | (i & 63);
257 } else if(i < 0x10FFFF) {
258 buf[0] = 0xF0 | (i >> 18);
259 buf[1] = 0x80 | ((i >> 12) & 63);
260 buf[2] = 0x80 | ((i >> 6) & 63);
261 buf[3] = 0x80 | (i & 63);
263 o << buf;
265 return o.str();
268 void commandline_model::handle_cow()
270 if(history_itr == history.end())
271 //Latched to end of history.
272 return;
273 codepoints = *history_itr;
274 history_itr = history.end();
277 void commandline_model::scroll_history_up()
279 if(history_itr == history.end())
280 //Save the current commandline.
281 saved_codepoints = codepoints;
282 if(history_itr == history.begin())
283 //At the begnning.
284 return;
285 history_itr--;
286 codepoints = *history_itr;
287 if(cursor_pos > codepoints.size())
288 cursor_pos = codepoints.size();
291 void commandline_model::scroll_history_down()
293 if(history_itr == history.end())
294 //At the end.
295 return;
296 history_itr++;
297 if(history_itr == history.end())
298 codepoints = saved_codepoints;
299 else
300 codepoints = *history_itr;
301 if(cursor_pos > codepoints.size())
302 cursor_pos = codepoints.size();
305 void commandline_model::delete_codepoint(size_t idx)
307 handle_cow();
308 for(size_t i = idx; i < codepoints.size() - 1; i++)
309 codepoints[i] = codepoints[i + 1];
310 codepoints.resize(codepoints.size() - 1);
313 void commandline_model::paint(SDL_Surface* surf, uint32_t x, uint32_t y, uint32_t maxwidth, uint32_t color,
314 bool box, uint32_t boxcolor) throw()
316 if(!maxwidth)
317 return;
318 try {
319 if(box)
320 draw_box(surf, x, y, maxwidth, 16, boxcolor);
321 if(enabled_flag)
322 draw_string(surf, read_command(), x, y, maxwidth, color, overwrite_mode ? 2 : 1, cursor_pos);
323 else
324 draw_string(surf, "", x, y, maxwidth, color, 0, 0);
325 } catch(...) {
326 OOM_panic();