OSDN Git Service

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