1 /**
2  * @file
3  * @brief System independent console IO functions
4 **/
5 
6 #pragma once
7 
8 #include <cctype>
9 #include <string>
10 #include <vector>
11 
12 #include "enum.h"
13 #include "KeymapContext.h"
14 
15 #ifdef USE_TILE_LOCAL
16  #include "tilebuf.h"
17  #include <SDL_keycode.h>
18 #endif
19 
20 enum keyfun_action
21 {
22     KEYFUN_PROCESS,
23     KEYFUN_IGNORE,
24     KEYFUN_CLEAR,
25     KEYFUN_BREAK,
26 };
27 
28 class input_history
29 {
30 public:
31     input_history(size_t size);
32 
33     void new_input(const string &s);
34     void clear();
35 
36     const string *prev();
37     const string *next();
38 
39     void go_end();
40 private:
41     typedef list<string> string_list;
42 
43     string_list             history;
44     string_list::iterator   pos;
45     size_t maxsize;
46 };
47 
48 void cursorxy(int x, int y);
cursorxy(const coord_def & p)49 static inline void cursorxy(const coord_def& p) { cursorxy(p.x, p.y); }
50 
51 // Converts a key to a direction key, converting keypad and other sequences
52 // to vi key sequences (shifted/control key directions are also handled). Non
53 // direction keys (hopefully) pass through unmangled.
54 int unmangle_direction_keys(int keyin, KeymapContext keymap = KMC_DEFAULT,
55                             bool allow_fake_modifiers = true);
56 
57 void nowrap_eol_cprintf(PRINTF(0, ));
58 void wrapcprintf(int wrapcol, const char *s, ...);
59 void wrapcprintf(const char *s, ...);
60 
61 // Returns zero if user entered text and pressed Enter, otherwise returns the
62 // key pressed that caused the exit, usually Escape.
63 //
64 // If keyproc is provided, it must return 1 for normal processing, 0 to exit
65 // normally (pretend the user pressed Enter), or -1 to exit as if the user
66 // pressed Escape
67 int cancellable_get_line(char *buf,
68                          int len,
69                          input_history *mh = nullptr,
70                          keyfun_action (*keyproc)(int &c) = nullptr,
71                          const string &fill = "",
72                          const string &tag = "");
73 
74 // Do not use this templated function directly. Use the macro below instead.
cancellable_get_line_autohist_temp(char * buf,int len)75 template<int> static int cancellable_get_line_autohist_temp(char *buf, int len)
76 {
77     static input_history hist(10);
78     return cancellable_get_line(buf, len, &hist);
79 }
80 
81 // This version of cancellable_get_line will automatically retain its own
82 // input history, independent of other calls to cancellable_get_line.
83 #define cancellable_get_line_autohist(buf, len) \
84     cancellable_get_line_autohist_temp<__LINE__>(buf, len)
85 
86 struct c_mouse_event
87 {
88     coord_def pos;
89     int       bstate;
90 
91     enum button_state_type
92     {
93         BUTTON1        = 0x1,
94         BUTTON1_DBL    = 0x2,
95         BUTTON2        = 0x4,
96         BUTTON2_DBL    = 0x8,
97         BUTTON3        = 0x10,
98         BUTTON3_DBL    = 0x20,
99         BUTTON4        = 0x40,
100         BUTTON4_DBL    = 0x80,
101         BUTTON_SCRL_UP = 0x100,
102         BUTTON_SCRL_DN = 0x200,
103     };
104 
c_mouse_eventc_mouse_event105     c_mouse_event() : pos(-1, -1), bstate(0)
106     {
107     }
108 
posc_mouse_event109     c_mouse_event(const coord_def &c, int state = 0) : pos(c), bstate(state)
110     {
111     }
112 
113     // Returns true for valid events.
114     operator bool () const
115     {
116         return bstate;
117     }
118 
left_clickedc_mouse_event119     bool left_clicked() const
120     {
121         return bstate & BUTTON1;
122     }
123 
right_clickedc_mouse_event124     bool right_clicked() const
125     {
126         return bstate & BUTTON3;
127     }
128 
scroll_upc_mouse_event129     bool scroll_up() const
130     {
131         return bstate & (BUTTON4 | BUTTON4_DBL | BUTTON_SCRL_UP);
132     }
133 
scroll_downc_mouse_event134     bool scroll_down() const
135     {
136         return bstate & (BUTTON2 | BUTTON2_DBL | BUTTON_SCRL_DN);
137     }
138 };
139 
140 c_mouse_event get_mouse_event();
141 void          new_mouse_event(const c_mouse_event &ce);
142 void          c_input_reset(bool enable_mouse, bool flush = false);
143 
144 // Keys that getch() must return for keys Crawl is interested in.
145 enum KEYS
146 {
147     CK_ENTER  = '\r',
148     CK_BKSP   = 8,
149     CK_ESCAPE = ESCAPE,
150 
151     CK_DELETE = -255,
152 
153     // This sequence of enums should not be rearranged.
154     CK_UP,
155     CK_DOWN,
156     CK_LEFT,
157     CK_RIGHT,
158 
159     CK_INSERT,
160 
161     CK_HOME,
162     CK_END,
163     CK_CLEAR,
164 
165     CK_PGUP,
166     CK_PGDN,
167     CK_TAB_TILE, // unused
168 
169     CK_SHIFT_UP,
170     CK_SHIFT_DOWN,
171     CK_SHIFT_LEFT,
172     CK_SHIFT_RIGHT,
173 
174     CK_SHIFT_INSERT,
175 
176     CK_SHIFT_HOME,
177     CK_SHIFT_END,
178     CK_SHIFT_CLEAR,
179 
180     CK_SHIFT_PGUP,
181     CK_SHIFT_PGDN,
182     CK_SHIFT_TAB,
183 
184     CK_CTRL_UP,
185     CK_CTRL_DOWN,
186     CK_CTRL_LEFT,
187     CK_CTRL_RIGHT,
188 
189     CK_CTRL_INSERT,
190 
191     CK_CTRL_HOME,
192     CK_CTRL_END,
193     CK_CTRL_CLEAR,
194 
195     CK_CTRL_PGUP,
196     CK_CTRL_PGDN,
197     CK_CTRL_TAB,
198 
199 #ifdef TOUCH_UI
200     // extra numpad keys for zoom
201     CK_NUMPAD_PLUS,
202     CK_NUMPAD_MINUS,
203 #endif
204 
205 // ugly...
206 // TODO: should crawl just use one of these internally and convert?
207 #ifdef USE_TILE_LOCAL
208     CK_F12 = -SDLK_F12,
209 #elif defined(TARGET_OS_WINDOWS) // windows console
210     CK_F12 = -379, // -(VK_F12 | 256) // XX ...
211 #else // ncurses console
212     CK_F12 = -276, // -KEY_F12 from ncurses
213 #endif
214     CK_F11,
215     CK_F10,
216     CK_F9,
217     CK_F8,
218     CK_F7,
219     CK_F6,
220     CK_F5,
221     CK_F4,
222     CK_F3,
223     CK_F2,
224     CK_F1, // -265, aka -KEY_F1
225     CK_F0, // is this actually used?
226 
227     // Mouse codes.
228     CK_MOUSE_MOVE  = -9999,
229     CK_MOUSE_CMD,
230     CK_MOUSE_B1,
231     CK_MOUSE_B2,
232     CK_MOUSE_B3,
233     CK_MOUSE_B4,
234     CK_MOUSE_B5,
235     CK_MOUSE_CLICK,
236     CK_TOUCH_DUMMY, // so a non-event can be passed from handle_mouse to the controlling code
237     CK_REDRAW, // no-op to force redraws of things
238     CK_RESIZE,
239 
240     CK_NO_KEY // so that the handle_mouse loop can be broken from early (for
241               // popups), and otherwise for keys to ignore
242 };
243 
244 class cursor_control
245 {
246 public:
cursor_control(bool cursor_enabled)247     cursor_control(bool cursor_enabled)
248         : cstate(is_cursor_enabled())
249     {
250         set_cursor_enabled(cursor_enabled);
251     }
~cursor_control()252     ~cursor_control()
253     {
254         set_cursor_enabled(cstate);
255     }
256 private:
257     bool cstate;
258 };
259 
260 enum edit_mode
261 {
262     EDIT_MODE_INSERT,
263     EDIT_MODE_OVERWRITE,
264 };
265 
266 class draw_colour
267 {
268 public:
269     draw_colour(COLOURS fg, COLOURS bg);
270     ~draw_colour();
271     void set();
272     void reset();
273 private:
274     COLOURS foreground;
275     COLOURS background;
276 };
277 
278 // Reads lines of text; used internally by cancellable_get_line.
279 class line_reader
280 {
281 public:
282     line_reader(char *buffer, size_t bufsz,
283                 int wrap_col = get_number_of_cols());
284     virtual ~line_reader();
285 
286     typedef keyfun_action (*keyproc)(int &key);
287 
288     virtual int read_line(bool clear_previous = true, bool reset_cursor = false);
289     int read_line(const string &prefill);
290 
291     string get_text() const;
292     void set_text(string s);
293 
294     void set_input_history(input_history *ih);
295     void set_keyproc(keyproc fn);
296 
297     void set_edit_mode(edit_mode m);
298     edit_mode get_edit_mode();
299 
300     void set_colour(COLOURS fg, COLOURS bg);
301     void set_location(coord_def loc);
302 
303     string get_prompt();
304     void set_prompt(string p);
305 
306     void insert_char_at_cursor(int ch);
307     void overwrite_char_at_cursor(int ch);
308 #ifdef USE_TILE_WEB
309     void set_tag(const string &tag);
310 #endif
311 
312 protected:
313     int read_line_core(bool reset_cursor);
314     int process_key_core(int ch);
315     virtual void print_segment(int start_point=0, int overprint=0);
316     virtual void cursorto(int newcpos);
317     virtual int getkey();
318 
319     virtual int process_key(int ch);
320     void backspace();
321     void killword();
322     void kill_to_begin();
323     void calc_pos();
324 
325     bool is_wordchar(char32_t c);
326 
327 protected:
328     char            *buffer;
329     size_t          bufsz;
330     input_history   *history;
331     GotoRegion      region;
332     coord_def       start;
333     keyproc         keyfn;
334     int             wrapcol;
335     edit_mode       mode;
336     COLOURS         fg_colour;
337     COLOURS         bg_colour;
338     string          prompt; // currently only used for webtiles input dialogs
339 
340 #ifdef USE_TILE_WEB
341     string          tag; // For identification on the Webtiles client side
342 #endif
343 
344     // These are subject to change during editing.
345     char            *cur;
346     int             length;
347     int             pos;
348 };
349 
350 #ifdef USE_TILE_LOCAL
351 class fontbuf_line_reader : public line_reader
352 {
353 public:
354     fontbuf_line_reader(char *buffer, size_t bufsz,
355             FontBuffer &font_buf,
356             int wrap_col = get_number_of_cols());
357     int read_line(bool clear_previous = true, bool reset_cursor = false);
358 
359 protected:
360     void print_segment(int start_point=0, int overprint=0);
361     void cursorto(int newcpos);
362     int getkey();
363 
364     FontBuffer& m_font_buf;
365 };
366 #endif
367 
368 class resumable_line_reader : public line_reader
369 {
370 public:
resumable_line_reader(char * buf,size_t sz)371     resumable_line_reader(char *buf, size_t sz) : line_reader(buf, sz, sz) { read_line(); };
putkey(int ch)372     int putkey(int ch) { return process_key_core(ch); };
373 protected:
getkey()374     virtual int getkey() override { return CK_NO_KEY; };
process_key(int ch)375     virtual int process_key(int ch) override
376     {
377         return ch == CK_NO_KEY ? CK_NO_KEY : line_reader::process_key(ch);
378     }
print_segment(int,int)379     virtual void print_segment(int, int) override {};
cursorto(int)380     void cursorto(int) override {};
381     int key;
382 };
383 
384 typedef int keycode_type;
385 
386 keyfun_action keyfun_num_and_char(int &ch);
387