OSDN Git Service

Merge branch 'master' of https://github.com/hengband/hengband
[hengbandforosx/hengbandosx.git] / src / io / input-key-acceptor.cpp
1 #include "io/input-key-acceptor.h"
2 #include "cmd-io/macro-util.h"
3 #include "core/stuff-handler.h"
4 #include "core/window-redrawer.h"
5 #include "game-option/input-options.h"
6 #include "game-option/map-screen-options.h"
7 #include "io/signal-handlers.h"
8 #include "system/player-type-definition.h"
9 #include "term/gameterm.h"
10 #include "util/string-processor.h"
11 #include "world/world.h"
12
13 bool inkey_base; /* See the "inkey()" function */
14 bool inkey_xtra; /* See the "inkey()" function */
15 bool inkey_scan; /* See the "inkey()" function */
16 bool inkey_flag; /* See the "inkey()" function */
17
18 int num_more = 0;
19
20 /*
21  * This special pointer allows a sequence of keys to be "inserted" into
22  * the stream of keys returned by "inkey()".  This key sequence will not
23  * trigger any macros, and cannot be bypassed by the Borg.  It is used
24  * in Angband to handle "keymaps".
25  */
26 concptr inkey_next = nullptr;
27
28 /* Save macro trigger string for use in inkey_special() */
29 static char inkey_macro_trigger_string[1024];
30
31 /*
32  * Local variable -- we are inside a "macro action"
33  *
34  * Do not match any macros until "ascii 30" is found.
35  */
36 static bool parse_macro = false;
37
38 /*
39  * Local variable -- we are inside a "macro trigger"
40  *
41  * Strip all keypresses until a low ascii value is found.
42  */
43 static bool parse_under = false;
44
45 /*!
46  * @brief 全てのウィンドウの再描画を行う
47  * @details
48  * カーソル位置がずれるので戻す。
49  */
50 static void all_term_fresh()
51 {
52     TERM_LEN x, y;
53     term_activate(angband_terms[0]);
54     term_locate(&x, &y);
55
56     p_ptr->window_flags |= PW_ALL;
57     handle_stuff(p_ptr);
58
59     term_activate(angband_terms[0]);
60     term_gotoxy(x, y);
61     term_fresh();
62 }
63
64 /*
65  * Cancel macro action on the queue
66  */
67 static void forget_macro_action(void)
68 {
69     if (!parse_macro) {
70         return;
71     }
72
73     while (true) {
74         char ch;
75         if (term_inkey(&ch, false, true)) {
76             break;
77         }
78         if (ch == 0) {
79             break;
80         }
81         if (ch == 30) {
82             break;
83         }
84     }
85
86     parse_macro = false;
87 }
88
89 /*
90  * Helper function called only from "inkey()"
91  *
92  * This function does almost all of the "macro" processing.
93  *
94  * We use the "term_key_push()" function to handle "failed" macros, as well
95  * as "extra" keys read in while choosing the proper macro, and also to hold
96  * the action for the macro, plus a special "ascii 30" character indicating
97  * that any macro action in progress is complete.  Embedded macros are thus
98  * illegal, unless a macro action includes an explicit "ascii 30" character,
99  * which would probably be a massive hack, and might break things.
100  *
101  * Only 500 (0+1+2+...+29+30) milliseconds may elapse between each key in
102  * the macro trigger sequence.  If a key sequence forms the "prefix" of a
103  * macro trigger, 500 milliseconds must pass before the key sequence is
104  * known not to be that macro trigger.
105  */
106 static char inkey_aux(void)
107 {
108     int k = 0, n, p = 0, w = 0;
109     char ch;
110     char *buf = inkey_macro_trigger_string;
111
112     num_more = 0;
113
114     if (parse_macro) {
115         if (term_inkey(&ch, false, true)) {
116             parse_macro = false;
117         }
118     } else {
119         (void)(term_inkey(&ch, true, true));
120     }
121
122     if (ch == 30) {
123         parse_macro = false;
124     }
125
126     if (ch == 30) {
127         return ch;
128     }
129     if (parse_macro) {
130         return ch;
131     }
132     if (parse_under) {
133         return ch;
134     }
135
136     buf[p++] = ch;
137     buf[p] = '\0';
138     k = macro_find_check(buf);
139     if (k < 0) {
140         return ch;
141     }
142
143     while (true) {
144         k = macro_find_maybe(buf);
145
146         if (k < 0) {
147             break;
148         }
149
150         if (0 == term_inkey(&ch, false, true)) {
151             buf[p++] = ch;
152             buf[p] = '\0';
153             w = 0;
154         } else {
155             w += 1;
156             if (w >= 10) {
157                 break;
158             }
159
160             term_xtra(TERM_XTRA_DELAY, w);
161         }
162     }
163
164     k = macro_find_ready(buf);
165     if (k < 0) {
166         while (p > 0) {
167             if (term_key_push(buf[--p])) {
168                 return 0;
169             }
170         }
171
172         (void)term_inkey(&ch, true, true);
173         return ch;
174     }
175
176     concptr pat = macro__pat[k].data();
177     n = strlen(pat);
178     while (p > n) {
179         if (term_key_push(buf[--p])) {
180             return 0;
181         }
182     }
183
184     parse_macro = true;
185     if (term_key_push(30)) {
186         return 0;
187     }
188
189     concptr act = macro__act[k].data();
190
191     n = strlen(act);
192     while (n > 0) {
193         if (term_key_push(act[--n])) {
194             return 0;
195         }
196     }
197
198     return 0;
199 }
200
201 /*
202  * @brief キー入力を受け付けるメインルーチン / Get a keypress from the user.
203  * @param do_all_term_refresh trueであれば強制的にhandle_stuffと再描画を行う。デフォルト false
204  * return キーを表すコード
205  */
206 char inkey(bool do_all_term_refresh)
207 {
208     char ch = 0;
209     bool done = false;
210     term_type *old = game_term;
211
212     if (inkey_next && *inkey_next && !inkey_xtra) {
213         ch = *inkey_next++;
214         inkey_base = inkey_xtra = inkey_flag = inkey_scan = false;
215         return ch;
216     }
217
218     inkey_next = nullptr;
219     if (inkey_xtra) {
220         parse_macro = false;
221         parse_under = false;
222         term_flush();
223     }
224
225     int v;
226     (void)term_get_cursor(&v);
227
228     /* Show the cursor if waiting, except sometimes in "command" mode */
229     if (!inkey_scan && (!inkey_flag || hilite_player || w_ptr->character_icky_depth > 0)) {
230         (void)term_set_cursor(1);
231     }
232
233     term_activate(angband_terms[0]);
234     char kk;
235     while (!ch) {
236         if (!inkey_base && inkey_scan && (0 != term_inkey(&kk, false, false))) {
237             break;
238         }
239
240         if (!done && (0 != term_inkey(&kk, false, false))) {
241             start_term_fresh();
242             if (do_all_term_refresh) {
243                 all_term_fresh();
244             } else {
245                 term_fresh();
246             }
247             w_ptr->character_saved = false;
248
249             signal_count = 0;
250             done = true;
251         }
252
253         if (inkey_base) {
254             int w = 0;
255             if (!inkey_scan) {
256                 if (0 == term_inkey(&ch, true, true)) {
257                     break;
258                 }
259
260                 break;
261             }
262
263             while (true) {
264                 if (0 == term_inkey(&ch, false, true)) {
265                     break;
266                 } else {
267                     w += 10;
268                     if (w >= 100) {
269                         break;
270                     }
271
272                     term_xtra(TERM_XTRA_DELAY, w);
273                 }
274             }
275
276             break;
277         }
278
279         ch = inkey_aux();
280         if (ch == 29) {
281             ch = 0;
282             continue;
283         }
284
285         if (parse_under && (ch <= 32)) {
286             ch = 0;
287             parse_under = false;
288         }
289
290         if (ch == 30) {
291             ch = 0;
292         } else if (ch == 31) {
293             ch = 0;
294             parse_under = true;
295         } else if (parse_under) {
296             ch = 0;
297         }
298     }
299
300     term_activate(old);
301     term_set_cursor(v);
302     inkey_base = inkey_xtra = inkey_flag = inkey_scan = false;
303     return ch;
304 }
305
306 /*
307  * Get a keypress from the user.
308  * And interpret special keys as internal code.
309  *
310  * This function is a Mega-Hack and depend on pref-xxx.prf's.
311  * Currently works on Linux(UNIX), Windows, and Macintosh only.
312  */
313 int inkey_special(bool numpad_cursor)
314 {
315     static const struct {
316         concptr keyname;
317         int keyflag;
318     } modifier_key_list[] = {
319         { "shift-", SKEY_MOD_SHIFT },
320         { "control-", SKEY_MOD_CONTROL },
321         { nullptr, 0 },
322     };
323
324     static const struct {
325         bool numpad;
326         concptr keyname;
327         int keycode;
328     } special_key_list[] = {
329         { false, "Down]", SKEY_DOWN },
330         { false, "Left]", SKEY_LEFT },
331         { false, "Right]", SKEY_RIGHT },
332         { false, "Up]", SKEY_UP },
333         { false, "Page_Up]", SKEY_PGUP },
334         { false, "Page_Down]", SKEY_PGDOWN },
335         { false, "Home]", SKEY_TOP },
336         { false, "End]", SKEY_BOTTOM },
337         { true, "KP_Down]", SKEY_DOWN },
338         { true, "KP_Left]", SKEY_LEFT },
339         { true, "KP_Right]", SKEY_RIGHT },
340         { true, "KP_Up]", SKEY_UP },
341         { true, "KP_Page_Up]", SKEY_PGUP },
342         { true, "KP_Page_Down]", SKEY_PGDOWN },
343         { true, "KP_Home]", SKEY_TOP },
344         { true, "KP_End]", SKEY_BOTTOM },
345         { true, "KP_2]", SKEY_DOWN },
346         { true, "KP_4]", SKEY_LEFT },
347         { true, "KP_6]", SKEY_RIGHT },
348         { true, "KP_8]", SKEY_UP },
349         { true, "KP_9]", SKEY_PGUP },
350         { true, "KP_3]", SKEY_PGDOWN },
351         { true, "KP_7]", SKEY_TOP },
352         { true, "KP_1]", SKEY_BOTTOM },
353         { false, nullptr, 0 },
354     };
355
356     static const struct {
357         concptr keyname;
358         int keycode;
359     } gcu_special_key_list[] = {
360         { "A", SKEY_UP },
361         { "B", SKEY_DOWN },
362         { "C", SKEY_RIGHT },
363         { "D", SKEY_LEFT },
364         { "1~", SKEY_TOP },
365         { "4~", SKEY_BOTTOM },
366         { "5~", SKEY_PGUP },
367         { "6~", SKEY_PGDOWN },
368         { nullptr, 0 },
369     };
370
371     char buf[1024];
372     concptr str = buf;
373     char key;
374     int skey = 0;
375     int modifier = 0;
376     int i;
377     size_t trig_len;
378
379     /*
380      * Forget macro trigger ----
381      * It's important if we are already expanding macro action
382      */
383     inkey_macro_trigger_string[0] = '\0';
384
385     key = inkey();
386     trig_len = strlen(inkey_macro_trigger_string);
387     if (!trig_len) {
388         return (int)((unsigned char)key);
389     }
390     if (trig_len == 1 && parse_macro) {
391         char c = inkey_macro_trigger_string[0];
392         forget_macro_action();
393         return (int)((unsigned char)c);
394     }
395
396     ascii_to_text(buf, inkey_macro_trigger_string, sizeof(buf));
397     if (prefix(str, "\\[")) {
398         str += 2;
399         while (true) {
400             for (i = 0; modifier_key_list[i].keyname; i++) {
401                 if (prefix(str, modifier_key_list[i].keyname)) {
402                     str += strlen(modifier_key_list[i].keyname);
403                     modifier |= modifier_key_list[i].keyflag;
404                 }
405             }
406
407             if (!modifier_key_list[i].keyname) {
408                 break;
409             }
410         }
411
412         if (!numpad_as_cursorkey) {
413             numpad_cursor = false;
414         }
415
416         for (i = 0; special_key_list[i].keyname; i++) {
417             if ((!special_key_list[i].numpad || numpad_cursor) && streq(str, special_key_list[i].keyname)) {
418                 skey = special_key_list[i].keycode;
419                 break;
420             }
421         }
422
423         if (skey) {
424             forget_macro_action();
425             return (skey | modifier);
426         }
427     }
428
429     if (prefix(str, "\\e[")) {
430         str += 3;
431
432         for (i = 0; gcu_special_key_list[i].keyname; i++) {
433             if (streq(str, gcu_special_key_list[i].keyname)) {
434                 return gcu_special_key_list[i].keycode;
435             }
436         }
437     }
438
439     inkey_macro_trigger_string[0] = '\0';
440     return (int)((unsigned char)key);
441 }
442
443 /*!
444  * @brief 全てのウィンドウの描画を止める
445  */
446 void stop_term_fresh(void)
447 {
448     for (auto &angband_term : angband_terms) {
449         if (angband_term != nullptr) {
450             angband_term->never_fresh = true;
451         }
452     }
453 }
454
455 /*!
456  * @brief 全てのウィンドウの描画を再開する
457  */
458 void start_term_fresh(void)
459 {
460     for (auto &angband_term : angband_terms) {
461         if (angband_term != nullptr) {
462             angband_term->never_fresh = false;
463         }
464     }
465 }
466
467 /*!
468  * @brief マクロ実行中かの判定関数
469  * @return 実行中であればtrue
470  */
471 bool macro_running(void)
472 {
473     /* マクロ展開中のみ詳細に判定する */
474     if (parse_macro) {
475         int diff = angband_terms[0]->key_head - angband_terms[0]->key_tail;
476
477         /* 最終入力を展開した直後はdiff==1となる */
478         if (diff != 1) {
479             return true;
480         }
481
482         /* 最終入力の処理中はまだtrueを返す */
483         if (inkey_next && *inkey_next && !inkey_xtra) {
484             return true;
485         }
486
487         return false;
488     }
489
490     return false;
491 }