OSDN Git Service

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