1 package com
.googlecode
.lanterna
.input
;
3 import static com
.googlecode
.lanterna
.input
.KeyDecodingProfile
.ESC_CODE
;
5 import java
.util
.HashMap
;
10 * This implementation of CharacterPattern matches two similar patterns
11 * of Escape sequences, that many terminals produce for special keys.<p>
13 * These sequences all start with Escape, followed by either an open bracket
14 * or a capital letter O (these two are treated as equivalent).<p>
16 * Then follows a list of zero or up to two decimals separated by a
17 * semicolon, and a non-digit last character.<p>
19 * If the last character is a tilde (~) then the first number defines
20 * the key (through stdMap), otherwise the last character itself defines
21 * the key (through finMap).<p>
23 * The second number, if provided by the terminal, specifies the modifier
24 * state (shift,alt,ctrl). The value is 1 + sum(modifiers), where shift is 1,
25 * alt is 2 and ctrl is 4.<p>
27 * The two maps stdMap and finMap can be customized in subclasses to add,
28 * remove or replace keys - to support non-standard Terminals.<p>
30 * Examples: (on a gnome terminal)<br>
31 * ArrowUp is "Esc [ A"; Alt-ArrowUp is "Esc [ 1 ; 3 A"<br>
32 * both are handled by finMap mapping 'A' to ArrowUp <br><br>
33 * F6 is "Esc [ 1 7 ~"; Ctrl-Shift-F6 is "Esc [ 1 7 ; 6 R"<br>
34 * both are handled by stdMap mapping 17 to F6 <br><br>
39 public class EscapeSequenceCharacterPattern
implements CharacterPattern
{
40 // state machine used to match key sequence:
42 START
, INTRO
, NUM1
, NUM2
, DONE
44 // bit-values for modifier keys: only used internally
45 public static final int SHIFT
= 1, ALT
= 2, CTRL
= 4;
48 * Map of recognized "standard pattern" sequences:<br>
49 * e.g.: 24 -> F12 : "Esc [ <b>24</b> ~"
51 protected final Map
<Integer
, KeyType
> stdMap
= new HashMap
<Integer
, KeyType
>();
53 * Map of recognized "finish pattern" sequences:<br>
54 * e.g.: 'A' -> ArrowUp : "Esc [ <b>A</b>"
56 protected final Map
<Character
, KeyType
> finMap
= new HashMap
<Character
, KeyType
>();
58 * A flag to control, whether an Esc-prefix for an Esc-sequence is to be treated
59 * as Alt-pressed. Some Terminals (e.g. putty) report the Alt-modifier like that.<p>
60 * If the application is e.g. more interested in seeing separate Escape and plain
61 * Arrow keys, then it should replace this class by a subclass that sets this flag
62 * to false. (It might then also want to remove the CtrlAltAndCharacterPattern.)
64 protected boolean useEscEsc
= true;
67 * Create an instance with a standard set of mappings.
69 public EscapeSequenceCharacterPattern() {
70 finMap
.put('A', KeyType
.ArrowUp
);
71 finMap
.put('B', KeyType
.ArrowDown
);
72 finMap
.put('C', KeyType
.ArrowRight
);
73 finMap
.put('D', KeyType
.ArrowLeft
);
74 finMap
.put('E', KeyType
.Unknown
); // gnome-terminal center key on numpad
75 finMap
.put('G', KeyType
.Unknown
); // putty center key on numpad
76 finMap
.put('H', KeyType
.Home
);
77 finMap
.put('F', KeyType
.End
);
78 finMap
.put('P', KeyType
.F1
);
79 finMap
.put('Q', KeyType
.F2
);
80 finMap
.put('R', KeyType
.F3
);
81 finMap
.put('S', KeyType
.F4
);
82 finMap
.put('Z', KeyType
.ReverseTab
);
84 stdMap
.put(1, KeyType
.Home
);
85 stdMap
.put(2, KeyType
.Insert
);
86 stdMap
.put(3, KeyType
.Delete
);
87 stdMap
.put(4, KeyType
.End
);
88 stdMap
.put(5, KeyType
.PageUp
);
89 stdMap
.put(6, KeyType
.PageDown
);
90 stdMap
.put(11, KeyType
.F1
);
91 stdMap
.put(12, KeyType
.F2
);
92 stdMap
.put(13, KeyType
.F3
);
93 stdMap
.put(14, KeyType
.F4
);
94 stdMap
.put(15, KeyType
.F5
);
95 stdMap
.put(16, KeyType
.F5
);
96 stdMap
.put(17, KeyType
.F6
);
97 stdMap
.put(18, KeyType
.F7
);
98 stdMap
.put(19, KeyType
.F8
);
99 stdMap
.put(20, KeyType
.F9
);
100 stdMap
.put(21, KeyType
.F10
);
101 stdMap
.put(23, KeyType
.F11
);
102 stdMap
.put(24, KeyType
.F12
);
103 stdMap
.put(25, KeyType
.F13
);
104 stdMap
.put(26, KeyType
.F14
);
105 stdMap
.put(28, KeyType
.F15
);
106 stdMap
.put(29, KeyType
.F16
);
107 stdMap
.put(31, KeyType
.F17
);
108 stdMap
.put(32, KeyType
.F18
);
109 stdMap
.put(33, KeyType
.F19
);
113 * combines a KeyType and modifiers into a KeyStroke.
114 * Subclasses can override this for customization purposes.
116 * @param key the KeyType as determined by parsing the sequence.
117 * It will be null, if the pattern looked like a key sequence but wasn't
119 * @param mods the bitmask of the modifer keys pressed along with the key.
120 * @return either null (to report mis-match), or a valid KeyStroke.
122 protected KeyStroke
getKeyStroke(KeyType key
, int mods
) {
123 boolean bShift
= false, bCtrl
= false, bAlt
= false;
124 if (key
== null) { return null; } // alternative: key = KeyType.Unknown;
125 if (mods
>= 0) { // only use when non-negative!
126 bShift
= (mods
& SHIFT
) != 0;
127 bAlt
= (mods
& ALT
) != 0;
128 bCtrl
= (mods
& CTRL
) != 0;
130 return new KeyStroke( key
, bCtrl
, bAlt
, bShift
);
134 * combines the raw parts of the sequence into a KeyStroke.
135 * This method does not check the first char, but overrides may do so.
137 * @param first the char following after Esc in the sequence (either [ or O)
138 * @param num1 the first decimal, or 0 if not in the sequence
139 * @param num2 the second decimal, or 0 if not in the sequence
140 * @param last the terminating char.
141 * @param bEsc whether an extra Escape-prefix was found.
142 * @return either null (to report mis-match), or a valid KeyStroke.
144 protected KeyStroke
getKeyStrokeRaw(char first
,int num1
,int num2
,char last
,boolean bEsc
) {
145 KeyType kt
= null; boolean bPuttyCtrl
= false;
146 if (last
== '~' && stdMap
.containsKey(num1
)) {
147 kt
= stdMap
.get(num1
);
148 } else if (finMap
.containsKey(last
)) {
149 kt
= finMap
.get(last
);
150 // Putty sends ^[OA for ctrl arrow-up, ^[[A for plain arrow-up:
151 // but only for A-D -- other ^[O... sequences are just plain keys
152 if (first
== 'O' && last
>= 'A' && last
<= 'D') { bPuttyCtrl
= true; }
153 // if we ever stumble into "keypad-mode", then it will end up inverted.
155 kt
= null; // unknown key.
159 if (mods
>= 0) { mods
|= ALT
; }
163 if (mods
>= 0) { mods
|= CTRL
; }
164 else { mods
= CTRL
; }
166 return getKeyStroke( kt
, mods
);
170 public Matching
match(List
<Character
> cur
) {
171 State state
= State
.START
;
172 int num1
= 0, num2
= 0;
173 char first
= '\0', last
= '\0';
174 boolean bEsc
= false;
176 for (char ch
: cur
) {
179 if (ch
!= ESC_CODE
) {
185 // Recognize a second Escape to mean "Alt is pressed".
186 // (at least putty sends it that way)
187 if (useEscEsc
&& ch
== ESC_CODE
&& ! bEsc
) {
188 bEsc
= true; continue;
191 // Key sequences supported by this class must
192 // start either with Esc-[ or Esc-O
193 if (ch
!= '[' && ch
!= 'O') {
196 first
= ch
; state
= State
.NUM1
;
201 } else if (Character
.isDigit(ch
)) {
202 num1
= num1
* 10 + Character
.digit(ch
, 10);
204 last
= ch
; state
= State
.DONE
;
208 if (Character
.isDigit(ch
)) {
209 num2
= num2
* 10 + Character
.digit(ch
, 10);
211 last
= ch
; state
= State
.DONE
;
214 case DONE
: // once done, extra characters spoil it
218 if (state
== State
.DONE
) {
219 KeyStroke ks
= getKeyStrokeRaw(first
,num1
,num2
,last
,bEsc
);
220 return ks
!= null ?
new Matching( ks
) : null; // depends
222 return Matching
.NOT_YET
; // maybe later