Apply the new ground_level method.
[crawl.git] / crawl-ref / source / cio.cc
blob783eaa90651441c12a7d4728fb7fa83f3d6d39b0
1 /*
2 * File: cio.cc
3 * Summary: Platform-independent console IO functions.
4 * Created by: dshaligram on Wed Jun 20 19:00:52 2007 UTC
5 */
7 #include "AppHdr.h"
9 #include "cio.h"
10 #include "externs.h"
11 #include "options.h"
12 #include "libutil.h"
13 #include "macro.h"
14 #include "message.h"
15 #include "state.h"
16 #include "viewgeom.h"
18 #include <queue>
20 #ifdef UNIX
21 extern int unixcurses_get_vi_key(int keyin);
23 static keycode_type _numpad2vi(keycode_type key)
25 #ifndef USE_TILE
26 key = unixcurses_get_vi_key(key);
27 #endif
28 switch (key)
30 case CK_UP: key = 'k'; break;
31 case CK_DOWN: key = 'j'; break;
32 case CK_LEFT: key = 'h'; break;
33 case CK_RIGHT: key = 'l'; break;
34 case 1001: key = 'b'; break;
35 case 1002: key = 'j'; break;
36 case 1003: key = 'n'; break;
37 case 1004: key = 'h'; break;
38 case 1005: key = '.'; break;
39 case 1006: key = 'l'; break;
40 case 1007: key = 'y'; break;
41 case 1008: key = 'k'; break;
42 case 1009: key = 'u'; break;
44 if (key >= '1' && key <= '9')
46 const char *vikeys = "bjnh.lyku";
47 return keycode_type(vikeys[key - '1']);
49 return (key);
51 #endif
53 int unmangle_direction_keys(int keyin, KeymapContext keymap,
54 bool fake_ctrl, bool fake_shift)
56 #ifdef UNIX
57 // Kludging running and opening as two character sequences
58 // for Unix systems. This is an easy way out... all the
59 // player has to do is find a termcap and numlock setting
60 // that will get curses the numbers from the keypad. This
61 // will hopefully be easy.
63 /* can we say yuck? -- haranp */
64 if (fake_ctrl && keyin == '*')
66 keyin = getchm(keymap);
67 // return control-key
68 keyin = CONTROL(toupper(_numpad2vi(keyin)));
70 else if (fake_shift && keyin == '/')
72 keyin = getchm(keymap);
73 // return shift-key
74 keyin = toupper(_numpad2vi(keyin));
76 #else
77 // Old DOS keypad support
78 if (keyin == 0)
80 /* FIXME haranp - hackiness */
81 const char DOSidiocy[10] = { "OPQKSMGHI" };
82 const char DOSunidiocy[10] = { "bjnh.lyku" };
83 const int DOScontrolidiocy[9] =
85 117, 145, 118, 115, 76, 116, 119, 141, 132
87 keyin = getchm(keymap);
88 for (int j = 0; j < 9; ++j)
90 if (keyin == DOSidiocy[j])
92 keyin = DOSunidiocy[j];
93 break;
95 if (keyin == DOScontrolidiocy[j])
97 keyin = CONTROL(toupper(DOSunidiocy[j]));
98 break;
102 #endif
104 // [dshaligram] More lovely keypad mangling.
105 switch (keyin)
107 #ifdef UNIX
108 case '1': return 'b';
109 case '2': return 'j';
110 case '3': return 'n';
111 case '4': return 'h';
112 case '6': return 'l';
113 case '7': return 'y';
114 case '8': return 'k';
115 case '9': return 'u';
117 #ifndef USE_TILE
118 default: return unixcurses_get_vi_key(keyin);
119 #endif
121 #else
122 case '1': return 'B';
123 case '2': return 'J';
124 case '3': return 'N';
125 case '4': return 'H';
126 case '6': return 'L';
127 case '7': return 'Y';
128 case '8': return 'K';
129 case '9': return 'U';
130 #endif
133 return (keyin);
136 // Wrapper around cgotoxy that can draw a fake cursor for Unix terms where
137 // cursoring over darkgrey or black causes problems.
138 void cursorxy(int x, int y)
140 #if defined(USE_TILE)
141 coord_def ep(x, y);
142 coord_def gc = crawl_view.screen2grid(ep);
143 tiles.place_cursor(CURSOR_MOUSE, gc);
144 #elif defined(UNIX)
145 if (Options.use_fake_cursor)
146 fakecursorxy(x, y);
147 else
148 cgotoxy(x, y, GOTO_CRT);
149 #else
150 cgotoxy(x, y, GOTO_CRT);
151 #endif
154 // cprintf that stops outputting when wrapped
155 // Conceptually very similar to wrapcprintf()
156 int nowrapcprintf(int wrapcol, const char *s, ...)
158 char buf[1000]; // Hard max
160 va_list args;
161 va_start(args, s);
162 // XXX: If snprintf isn't available, vsnprintf probably isn't, either.
163 const int len = vsnprintf(buf, sizeof buf, s, args);
164 va_end(args);
166 // Sanity checking to prevent buffer overflows
167 const int maxlen = std::min(std::max(wrapcol + 1 - wherex(), 0), len);
169 // Force the string to terminate at maxlen
170 buf[maxlen] = 0;
172 cprintf("%s", buf);
173 return std::min(len, maxlen);
176 // convenience wrapper (hah) for nowrapcprintf
177 // FIXME: should pass off to nowrapcprintf() instead of doing it manually
178 int nowrap_eol_cprintf(const char *s, ...)
180 const int wrapcol = get_number_of_cols() - 1;
181 char buf[1000]; // Hard max
183 va_list args;
184 va_start(args, s);
185 // XXX: If snprintf isn't available, vsnprintf probably isn't, either.
186 const int len = vsnprintf(buf, sizeof buf, s, args);
187 va_end(args);
189 // Sanity checking to prevent buffer overflows
190 const int maxlen = std::min(std::max(wrapcol + 1 - wherex(), 0), len);
192 // Force the string to terminate at maxlen
193 buf[maxlen] = 0;
195 cprintf("%s", buf);
196 return std::min(len, maxlen);
199 // cprintf that knows how to wrap down lines (primitive, but what the heck)
200 int wrapcprintf(int wrapcol, const char *s, ...)
202 char buf[1000]; // Hard max
203 va_list args;
204 va_start(args, s);
206 // XXX: If snprintf isn't available, vsnprintf probably isn't, either.
207 int len = vsnprintf(buf, sizeof buf, s, args);
208 int olen = len;
209 va_end(args);
211 char *run = buf;
212 while (len > 0)
214 int x = wherex(), y = wherey();
216 if (x > wrapcol) // Somebody messed up!
217 return 0;
219 int avail = wrapcol - x + 1;
220 int c = 0;
221 if (len > avail)
223 c = run[avail];
224 run[avail] = 0;
226 cprintf("%s", run);
228 if (len > avail)
229 run[avail] = c;
231 if ((len -= avail) > 0)
232 cgotoxy(1, y + 1);
233 run += avail;
235 return (olen);
238 int cancelable_get_line(char *buf, int len, input_history *mh,
239 int (*keyproc)(int &ch))
241 flush_prev_message();
243 mouse_control mc(MOUSE_MODE_MORE);
244 line_reader reader(buf, len, get_number_of_cols());
245 reader.set_input_history(mh);
246 reader.set_keyproc(keyproc);
248 return reader.read_line();
252 /////////////////////////////////////////////////////////////
253 // input_history
256 input_history::input_history(size_t size)
257 : history(), pos(), maxsize(size)
259 if (maxsize < 2)
260 maxsize = 2;
262 pos = history.end();
265 void input_history::new_input(const std::string &s)
267 history.remove(s);
269 if (history.size() == maxsize)
270 history.pop_front();
272 history.push_back(s);
274 // Force the iterator to the end (also revalidates it)
275 go_end();
278 const std::string *input_history::prev()
280 if (history.empty())
281 return NULL;
283 if (pos == history.begin())
284 pos = history.end();
286 return &*--pos;
289 const std::string *input_history::next()
291 if (history.empty())
292 return NULL;
294 if (pos == history.end() || ++pos == history.end())
295 pos = history.begin();
297 return &*pos;
300 void input_history::go_end()
302 pos = history.end();
305 void input_history::clear()
307 history.clear();
308 go_end();
311 /////////////////////////////////////////////////////////////////////////
312 // line_reader
314 line_reader::line_reader(char *buf, size_t sz, int wrap)
315 : buffer(buf), bufsz(sz), history(NULL), region(GOTO_CRT),
316 start(coord_def(0,0)), keyfn(NULL), wrapcol(wrap),
317 cur(NULL), length(0), pos(-1)
321 line_reader::~line_reader()
325 std::string line_reader::get_text() const
327 return (buffer);
330 void line_reader::set_input_history(input_history *i)
332 history = i;
335 void line_reader::set_keyproc(keyproc fn)
337 keyfn = fn;
340 void line_reader::cursorto(int ncx)
342 int x = (start.x + ncx - 1) % wrapcol + 1;
343 int y = start.y + (start.x + ncx - 1) / wrapcol;
344 int diff = y - cgetsize(region).y;
345 if (diff > 0)
347 // There's no space left in the region, so we scroll it.
348 // XXX: cscroll only implemented for GOTO_MSG.
349 // XXX: wrapcprintf works in GOTO_SCREEN; in particular
350 // it wraps to the screen's first column, so this
351 // won't work for regions that don't start at the
352 // left edge.
353 cscroll(diff, region);
354 start.y -= diff;
355 y -= diff;
356 cgotoxy(start.x, start.y, region);
357 wrapcprintf(wrapcol, "%s", buffer);
359 cgotoxy(x, y, region);
362 int line_reader::read_line(bool clear_previous)
364 if (bufsz <= 0)
365 return (false);
367 cursor_control con(true);
369 if (clear_previous)
370 *buffer = 0;
372 region = get_cursor_region();
373 start = cgetpos(region);
375 length = strlen(buffer);
377 // Remember the previous cursor position, if valid.
378 if (pos < 0 || pos > length)
379 pos = length;
381 cur = buffer + pos;
383 if (length)
384 wrapcprintf(wrapcol, "%s", buffer);
386 if (pos != length)
387 cursorto(pos);
389 if (history)
390 history->go_end();
392 while (true)
394 int ch = getchm(getch_ck);
396 #if defined(USE_UNIX_SIGNALS) && defined(SIGHUP_SAVE) && defined(USE_CURSES)
397 // Don't return a partial string if a HUP signal interrupted things
398 if (crawl_state.seen_hups)
400 buffer[0] = '\0';
401 return (0);
403 #endif
405 if (keyfn)
407 int whattodo = (*keyfn)(ch);
408 if (whattodo == 0)
410 buffer[length] = 0;
411 if (history && length)
412 history->new_input(buffer);
413 return (0);
415 else if (whattodo == -1)
417 buffer[length] = 0;
418 return (ch);
422 int ret = process_key(ch);
423 if (ret != -1)
424 return (ret);
428 void line_reader::backspace()
430 if (pos)
432 buffer[length] = 0;
433 --cur;
434 char *c = cur;
435 while (*c)
437 *c = *(c+1);
438 c++;
440 --pos;
441 --length;
443 cursorto(pos);
444 buffer[length] = 0;
445 wrapcprintf(wrapcol, "%s ", cur);
446 cursorto(pos);
450 bool line_reader::is_wordchar(int c)
452 return isalnum(c) || c == '_' || c == '-';
455 void line_reader::kill_to_begin()
457 if (!pos || cur == buffer)
458 return;
460 buffer[length] = 0;
461 cursorto(0);
462 wrapcprintf(wrapcol, "%s%*s", cur, cur - buffer, "");
463 memmove(buffer, cur, length - pos);
464 length -= pos;
465 pos = 0;
466 cur = buffer;
467 cursorto(pos);
470 void line_reader::killword()
472 if (!pos || cur == buffer)
473 return;
475 bool foundwc = false;
476 while (pos)
478 if (is_wordchar(cur[-1]))
479 foundwc = true;
480 else if (foundwc)
481 break;
483 backspace();
487 int line_reader::process_key(int ch)
489 switch (ch)
491 CASE_ESCAPE
492 return (CK_ESCAPE);
493 case CK_UP:
494 case CK_DOWN:
496 if (!history)
497 break;
499 const std::string *text = (ch == CK_UP) ? history->prev()
500 : history->next();
502 if (text)
504 int olen = length;
505 length = text->length();
506 if (length >= static_cast<int>(bufsz))
507 length = bufsz - 1;
508 memcpy(buffer, text->c_str(), length);
509 buffer[length] = 0;
510 cursorto(0);
512 int clear = length < olen ? olen - length : 0;
513 wrapcprintf(wrapcol, "%s%*s", buffer, clear, "");
515 pos = length;
516 cur = buffer + pos;
517 cursorto(pos);
519 break;
521 case CK_ENTER:
522 buffer[length] = 0;
523 if (history && length)
524 history->new_input(buffer);
525 return (0);
527 case CONTROL('K'):
529 // Kill to end of line.
530 int erase = length - pos;
531 if (erase)
533 length = pos;
534 buffer[length] = 0;
535 wrapcprintf(wrapcol, "%*s", erase, "");
536 cursorto(pos);
538 break;
540 case CK_DELETE:
541 if (pos < length)
543 char *c = cur;
544 while (c - buffer < length)
546 *c = c[1];
547 c++;
549 --length;
551 cursorto(pos);
552 buffer[length] = 0;
553 wrapcprintf(wrapcol, "%s ", cur);
554 cursorto(pos);
556 break;
558 case CK_BKSP:
559 backspace();
560 break;
562 case CONTROL('W'):
563 killword();
564 break;
566 case CONTROL('U'):
567 kill_to_begin();
568 break;
570 case CK_LEFT:
571 if (pos)
573 --pos;
574 cur = buffer + pos;
575 cursorto(pos);
577 break;
578 case CK_RIGHT:
579 if (pos < length)
581 ++pos;
582 cur = buffer + pos;
583 cursorto(pos);
585 break;
586 case CK_HOME:
587 case CONTROL('A'):
588 pos = 0;
589 cur = buffer + pos;
590 cursorto(pos);
591 break;
592 case CK_END:
593 case CONTROL('E'):
594 pos = length;
595 cur = buffer + pos;
596 cursorto(pos);
597 break;
598 case CK_MOUSE_CLICK:
599 return (-1);
600 default:
601 if (isprint(ch) && length < static_cast<int>(bufsz) - 1)
603 if (pos < length)
605 char *c = buffer + length - 1;
606 while (c >= cur)
608 c[1] = *c;
609 c--;
612 *cur++ = static_cast<char>(ch);
613 ++length;
614 buffer[length] = 0;
615 ++pos;
616 putch(ch);
617 if (pos < length)
618 wrapcprintf(wrapcol, "%s", cur);
619 cursorto(pos);
621 break;
624 return (-1);
627 /////////////////////////////////////////////////////////////////////////////
628 // Of mice and other mice.
630 static std::queue<c_mouse_event> mouse_events;
632 coord_def get_mouse_pos()
634 // lib$(OS) has to maintain mousep. This function is just the messenger.
635 return (crawl_view.mousep);
638 c_mouse_event get_mouse_event()
640 if (mouse_events.empty())
641 return c_mouse_event();
643 c_mouse_event ce = mouse_events.front();
644 mouse_events.pop();
645 return (ce);
648 void new_mouse_event(const c_mouse_event &ce)
650 mouse_events.push(ce);
653 static void _flush_mouse_events()
655 while (!mouse_events.empty())
656 mouse_events.pop();
659 void c_input_reset(bool enable_mouse, bool flush)
661 crawl_state.mouse_enabled = (enable_mouse && Options.mouse_input);
662 set_mouse_enabled(crawl_state.mouse_enabled);
664 if (flush)
665 _flush_mouse_events();