001package squidpony.squidgrid.gui.gdx; 002 003import com.badlogic.gdx.*; 004import com.badlogic.gdx.files.FileHandle; 005import com.badlogic.gdx.utils.IntIntMap; 006import com.badlogic.gdx.utils.TimeUtils; 007import squidpony.squidmath.IntIntOrderedMap; 008import squidpony.squidmath.IntVLA; 009 010/** 011 * This input processing class can handle mouse and keyboard input, using a squidpony.squidgrid.gui.gdx.SquidMouse for 012 * Mouse input and a user implementation of the SquidInput.KeyHandler interface to react to keys represented as chars 013 * and the modifiers those keys were pressed with, any of alt, ctrl, and/or shift. Not all keys are representable by 014 * default in unicode, so symbolic representations are stored in constants in this class, and are passed to 015 * {@link KeyHandler#handle(char, boolean, boolean, boolean)} as chars like DOWN_ARROW or its value, '\u2193'. Shift 016 * modifies the input as it would on a QWERTY keyboard, and the exact mapping is documented in 017 * {@link #fromCode(int, boolean)} as well. This class handles mouse input immediately, but stores keypresses in a 018 * queue, storing all key events and allowing them to be processed one at a time using {@link #next()} or all at once 019 * using {@link #drain()}. To have an effect, it needs to be registered by calling 020 * {@link Input#setInputProcessor(InputProcessor)}. Note that calling {@link #hasNext()} does more than just check if 021 * there are events that can be processed; because hasNext() is expected to be called frequently, it is also the point 022 * where this class checks if a key is being held and so the next event should occur. Holding a key only causes the 023 * keyDown() method of InputListener to be called once, so this uses hasNext() to see if there should be a next event 024 * coming from a held key. 025 * <br> 026 * This also allows some key remapping, including remapping so a key pressed with modifiers like Ctrl and Shift could 027 * act like '?' (which could be used by expert players to avoid accidentally opening a help menu they don't need), and 028 * that would free up '?' for some other use that could also be remapped. The remap() methods do much of this, often 029 * with help from {@link #combineModifiers(char, boolean, boolean, boolean)}, while the unmap() methods allow removal of 030 * any no-longer-wanted remappings. 031 * <br> 032 * It does not perform the blocking functionality of earlier SquidKey implementations, because this is meant to run 033 * in an event-driven libGDX game and should not step on the toes of libGDX's input handling. To block game logic 034 * until an event has been received, check hasNext() in the game's render() method and effectively "block" by not 035 * running game logic if hasNext() returns false. You can process an event if hasNext() returns true by calling 036 * {@link #next()}. Mouse inputs do not affect hasNext(), and next() will process only key pressed events. Also, see 037 * above about the extra behavior of hasNext regarding held keys. 038 * 039 * @author Eben Howard - http://squidpony.com - howard@squidpony.com 040 * @author Nathan Sweet 041 * @author Tommy Ettinger 042 * */ 043public class SquidInput extends InputAdapter { 044 /** 045 * A single-method interface used to process "typed" characters, special characters produced by unusual keys, and 046 * modifiers that can affect them. SquidInput has numerous static char values that are expected to be passed 047 * to handle() in place of the special keys (such as arrow keys) that do not have a standard char value. 048 */ 049 public interface KeyHandler{ 050 /** 051 * The only method you need to implement yourself in KeyHandler, this should react to keys such as 052 * 'a' (produced by pressing the A key while not holding Shift), 'E' (produced by pressing the E key while 053 * holding Shift), and '\u2190' (left arrow in unicode, also available as a constant in SquidInput, produced by 054 * pressing the left arrow key even though that key does not have a default unicode representation). Capital 055 * letters will be capitalized when they are passed to this, but they may or may not have the shift argument as 056 * true depending on how this method was called. Symbols that may be produced by holding Shift and pressing a 057 * number or a symbol key can vary between keyboards (some may require Shift to be held down, others may not). 058 * <br> 059 * This can react to the input in whatever way you find appropriate for your game. 060 * @param key a char of the "typed" representation of the key, such as 'a' or 'E', or if there is no Unicode 061 * character for the key, an appropriate alternate character as documented in SquidInput.fromKey() 062 * @param alt true if the Alt modifier was being held while this key was entered, false otherwise. 063 * @param ctrl true if the Ctrl modifier was being held while this key was entered, false otherwise. 064 * @param shift true if the Shift modifier was being held while this key was entered, false otherwise. 065 */ 066 void handle(char key, boolean alt, boolean ctrl, boolean shift); 067 } 068 069 protected KeyHandler keyAction; 070 protected boolean numpadDirections = true, ignoreInput; 071 protected SquidMouse mouse; 072 protected final IntVLA queue = new IntVLA(); 073 protected long lastKeyTime = -1000000L; 074 protected IntIntOrderedMap heldCodes = new IntIntOrderedMap(64, 0.25f); 075 protected long repeatGapMillis = 220L; 076 public final IntIntMap mapping = new IntIntMap(128, 0.25f); 077 /** 078 * Constructs a new SquidInput that does not respond to keyboard or mouse input. These can be set later by calling 079 * setKeyHandler() to allow keyboard handling or setMouse() to allow mouse handling on a grid. 080 * <br> 081 * All SquidInput constructors must be called after the {@link ApplicationListener#create()} method has started, and 082 * cannot be called in an ApplicationListener's constructor or in initialization at the class level. This is because 083 * all constructors attempt to load a file, if it exists, with {@code Gdx.files.local("keymap.preferences")}, and 084 * {@code Gdx.files} is only available starting in create(). This file, keymap.preferences, is meant to contain any 085 * key remappings specified by the user, and its contents should be produced by {@link #keyMappingToString()} when 086 * any key mappings change and are saved. You can use your own filename, but keymap.preferences is the standard one 087 * and these can be exchanged between games (so common remappings for users of DVORAK or AZERTY keyboards can be 088 * sent between users even if they play different games). If you really want a custom filename, or to load the 089 * keymap from a larger user profile, you can give the contents to {@link #keyMappingFromString(String)} on a 090 * SquidInput after construction. 091 */ 092 public SquidInput() { 093 this(null, null, false); 094 } 095 096 /** 097 * Constructs a new SquidInput that does not respond to keyboard input, but does take mouse input and passes mouse 098 * events along to the given SquidMouse. The SquidMouse, even though it is an InputProcessor on its own, should not 099 * be registered by calling Input.setInputProcessor(SquidMouse), and instead this object should be registered by 100 * calling Input.setInputProcessor(SquidInput). The keyboard and mouse handling can be changed later by calling 101 * setKeyHandler() to allow keyboard handling or setMouse() to change mouse handling. 102 * <br> 103 * All SquidInput constructors must be called after the {@link ApplicationListener#create()} method has started, and 104 * cannot be called in an ApplicationListener's constructor or in initialization at the class level. This is because 105 * all constructors attempt to load a file, if it exists, with {@code Gdx.files.local("keymap.preferences")}, and 106 * {@code Gdx.files} is only available starting in create(). This file, keymap.preferences, is meant to contain any 107 * key remappings specified by the user, and its contents should be produced by {@link #keyMappingToString()} when 108 * any key mappings change and are saved. You can use your own filename, but keymap.preferences is the standard one 109 * and these can be exchanged between games (so common remappings for users of DVORAK or AZERTY keyboards can be 110 * sent between users even if they play different games). If you really want a custom filename, or to load the 111 * keymap from a larger user profile, you can give the contents to {@link #keyMappingFromString(String)} on a 112 * SquidInput after construction. 113 * @param mouse a SquidMouse instance that will be used for handling mouse input. 114 */ 115 public SquidInput(SquidMouse mouse) { 116 this(null, mouse, false); 117 } 118 119 /** 120 * Constructs a new SquidInput that does not respond to mouse input, but does take keyboard input and sends keyboard 121 * events through some processing before calling keyHandler.handle() on keypresses that can sensibly be processed. 122 * Modifier keys do not go through the same processing but are checked for their current state when the key is 123 * pressed, and the states of alt, ctrl, and shift are passed to keyHandler.handle() as well. 124 * You can use setMouse() to allow mouse handling or change the KeyHandler with setKeyHandler(). 125 * <br> 126 * All SquidInput constructors must be called after the {@link ApplicationListener#create()} method has started, and 127 * cannot be called in an ApplicationListener's constructor or in initialization at the class level. This is because 128 * all constructors attempt to load a file, if it exists, with {@code Gdx.files.local("keymap.preferences")}, and 129 * {@code Gdx.files} is only available starting in create(). This file, keymap.preferences, is meant to contain any 130 * key remappings specified by the user, and its contents should be produced by {@link #keyMappingToString()} when 131 * any key mappings change and are saved. You can use your own filename, but keymap.preferences is the standard one 132 * and these can be exchanged between games (so common remappings for users of DVORAK or AZERTY keyboards can be 133 * sent between users even if they play different games). If you really want a custom filename, or to load the 134 * keymap from a larger user profile, you can give the contents to {@link #keyMappingFromString(String)} on a 135 * SquidInput after construction. 136 * @param keyHandler must implement the SquidInput.KeyHandler interface so it can handle() key input. 137 */ 138 public SquidInput(KeyHandler keyHandler) { 139 this(keyHandler, null, false); 140 } 141 /** 142 * Constructs a new SquidInput that does not respond to mouse input, but does take keyboard input and sends keyboard 143 * events through some processing before calling keyHandler.handle() on keypresses that can sensibly be processed. 144 * Modifier keys do not go through the same processing but are checked for their current state when the key is 145 * pressed, and the states of alt, ctrl, and shift are passed to keyHandler.handle() as well. 146 * You can use setMouse() to allow mouse handling or change the KeyHandler with setKeyHandler(). 147 * <br> 148 * All SquidInput constructors must be called after the {@link ApplicationListener#create()} method has started, and 149 * cannot be called in an ApplicationListener's constructor or in initialization at the class level. This is because 150 * all constructors attempt to load a file, if it exists, with {@code Gdx.files.local("keymap.preferences")}, and 151 * {@code Gdx.files} is only available starting in create(). This file, keymap.preferences, is meant to contain any 152 * key remappings specified by the user, and its contents should be produced by {@link #keyMappingToString()} when 153 * any key mappings change and are saved. You can use your own filename, but keymap.preferences is the standard one 154 * and these can be exchanged between games (so common remappings for users of DVORAK or AZERTY keyboards can be 155 * sent between users even if they play different games). If you really want a custom filename, or to load the 156 * keymap from a larger user profile, you can give the contents to {@link #keyMappingFromString(String)} on a 157 * SquidInput after construction. 158 * @param keyHandler must implement the SquidInput.KeyHandler interface so it can handle() key input. 159 * @param ignoreInput true if this should ignore input initially, false if it should process input normally. 160 */ 161 public SquidInput(KeyHandler keyHandler, boolean ignoreInput) { 162 this(keyHandler, null, ignoreInput); 163 } 164 /** 165 * Constructs a new SquidInput that responds to mouse and keyboard input when given a SquidMouse and a 166 * SquidInput.KeyHandler implementation. It sends keyboard events through some processing before calling 167 * keyHandler.handle() on keypresses that can sensibly be processed. Modifier keys do not go through the same 168 * processing but are checked for their current state when the key is pressed, and the states of alt, ctrl, and 169 * shift are passed to keyHandler.handle() as well. The SquidMouse, even though it is an 170 * InputProcessor on its own, should not be registered by calling Input.setInputProcessor(SquidMouse), and instead 171 * this object should be registered by calling Input.setInputProcessor(SquidInput). You can use setKeyHandler() or 172 * setMouse() to change keyboard or mouse handling. 173 * <br> 174 * All SquidInput constructors must be called after the {@link ApplicationListener#create()} method has started, and 175 * cannot be called in an ApplicationListener's constructor or in initialization at the class level. This is because 176 * all constructors attempt to load a file, if it exists, with {@code Gdx.files.local("keymap.preferences")}, and 177 * {@code Gdx.files} is only available starting in create(). This file, keymap.preferences, is meant to contain any 178 * key remappings specified by the user, and its contents should be produced by {@link #keyMappingToString()} when 179 * any key mappings change and are saved. You can use your own filename, but keymap.preferences is the standard one 180 * and these can be exchanged between games (so common remappings for users of DVORAK or AZERTY keyboards can be 181 * sent between users even if they play different games). If you really want a custom filename, or to load the 182 * keymap from a larger user profile, you can give the contents to {@link #keyMappingFromString(String)} on a 183 * SquidInput after construction. 184 * @param keyHandler must implement the SquidInput.KeyHandler interface so it can handle() key input. 185 * @param mouse a SquidMouse instance that will be used for handling mouse input. 186 */ 187 public SquidInput(KeyHandler keyHandler, SquidMouse mouse) { 188 this(keyHandler, mouse, false); 189 } 190 191 /** 192 * Constructs a new SquidInput that responds to mouse and keyboard input when given a SquidMouse and a 193 * SquidInput.KeyHandler implementation, and can be put in an initial state where it ignores input until told 194 * otherwise via setIgnoreInput(boolean). It sends keyboard events through some processing before calling 195 * keyHandler.handle() on keypresses that can sensibly be processed. Modifier keys do not go through the same 196 * processing but are checked for their current state when the key is pressed, and the states of alt, ctrl, and 197 * shift are passed to keyHandler.handle() as well. The SquidMouse, even though it is an 198 * InputProcessor on its own, should not be registered by calling Input.setInputProcessor(SquidMouse), and instead 199 * this object should be registered by calling Input.setInputProcessor(SquidInput). You can use setKeyHandler() or 200 * setMouse() to change keyboard or mouse handling. 201 * <br> 202 * All SquidInput constructors must be called after the {@link ApplicationListener#create()} method has started, and 203 * cannot be called in an ApplicationListener's constructor or in initialization at the class level. This is because 204 * all constructors attempt to load a file, if it exists, with {@code Gdx.files.local("keymap.preferences")}, and 205 * {@code Gdx.files} is only available starting in create(). This file, keymap.preferences, is meant to contain any 206 * key remappings specified by the user, and its contents should be produced by {@link #keyMappingToString()} when 207 * any key mappings change and are saved. You can use your own filename, but keymap.preferences is the standard one 208 * and these can be exchanged between games (so common remappings for users of DVORAK or AZERTY keyboards can be 209 * sent between users even if they play different games). If you really want a custom filename, or to load the 210 * keymap from a larger user profile, you can give the contents to {@link #keyMappingFromString(String)} on a 211 * SquidInput after construction. 212 * @param keyHandler must implement the SquidInput.KeyHandler interface so it can handle() key input. 213 * @param mouse a SquidMouse instance that will be used for handling mouse input. 214 * @param ignoreInput true if this should ignore input initially, false if it should process input normally. 215 */ 216 public SquidInput(KeyHandler keyHandler, SquidMouse mouse, boolean ignoreInput) { 217 keyAction = keyHandler; 218 this.mouse = mouse; 219 this.ignoreInput = ignoreInput; 220 FileHandle prefFile; 221 if(Gdx.files.isLocalStorageAvailable() && (prefFile = Gdx.files.local("keymap.preferences")).exists()) 222 { 223 keyMappingFromString(prefFile.readString("UTF8")); 224 } 225 } 226 227 public void setKeyHandler(KeyHandler keyHandler) 228 { 229 keyAction = keyHandler; 230 } 231 public void setMouse(SquidMouse mouse) 232 { 233 this.mouse = mouse; 234 } 235 236 public boolean isUsingNumpadDirections() { 237 return numpadDirections; 238 } 239 240 public void setUsingNumpadDirections(boolean using) { 241 numpadDirections = using; 242 } 243 244 public KeyHandler getKeyHandler() { 245 return keyAction; 246 } 247 248 public SquidMouse getMouse() { 249 return mouse; 250 } 251 252 /** 253 * Get the status for whether this should ignore input right now or not. True means this object will ignore and not 254 * queue input, false means it should process them normally. Useful to pause processing or delegate it to 255 * another object temporarily. 256 * @return true if this object currently ignores input, false otherwise. 257 */ 258 public boolean getIgnoreInput() { 259 return ignoreInput; 260 } 261 262 /** 263 * Set the status for whether this should ignore input right now or not. True means this object will ignore and not 264 * queue input, false means it should process them normally. Useful to pause processing or delegate it to 265 * another object temporarily. 266 * @param ignoreInput true if this should object should ignore and not queue input, false otherwise. 267 */ 268 public void setIgnoreInput(boolean ignoreInput) { 269 this.ignoreInput = ignoreInput; 270 } 271 272 /** 273 * Remaps a char that could be input and processed by {@link KeyHandler#handle(char, boolean, boolean, boolean)} 274 * (possibly with some pressed modifiers) to another char with possible modifiers. When the first key/modifier mix 275 * is received, it will be translated to the second group in this method. 276 * @param pressedChar a source char that might be used in handling, like 'q', 'A', '7', '(', or {@link #UP_ARROW}. 277 * @param pressedAlt true if alt is part of the source combined keypress, false otherwise 278 * @param pressedCtrl true if ctrl is part of the source combined keypress, false otherwise 279 * @param pressedShift true if shift is part of the source combined keypress, false otherwise 280 * @param targetChar a target char that might be used in handling, like 'q', 'A', '7', '(', or {@link #UP_ARROW}. 281 * @param targetAlt true if alt is part of the target combined keypress, false otherwise 282 * @param targetCtrl true if ctrl is part of the target combined keypress, false otherwise 283 * @param targetShift true if shift is part of the target combined keypress, false otherwise 284 * @return this for chaining 285 */ 286 public SquidInput remap(char pressedChar, boolean pressedAlt, boolean pressedCtrl, boolean pressedShift, 287 char targetChar, boolean targetAlt, boolean targetCtrl, boolean targetShift) 288 { 289 mapping.put(combineModifiers(pressedChar, pressedAlt, pressedCtrl, pressedShift), 290 combineModifiers(targetChar, targetAlt, targetCtrl, targetShift)); 291 return this; 292 } 293 294 /** 295 * Remaps a keypress combination, which is a char and several potential modifiers, to another keypress combination. 296 * When the {@code pressed} combination is received, it will be translated to {@code target} in this method. 297 * @see #combineModifiers(char, boolean, boolean, boolean) combineModifiers is usually used to make pressed and target 298 * @param pressed an int for the source keypress, probably produced by {@link #combineModifiers(char, boolean, boolean, boolean)} 299 * @param target an int for the target keypress, probably produced by {@link #combineModifiers(char, boolean, boolean, boolean)} 300 * @return this for chaining 301 */ 302 public SquidInput remap(int pressed, int target) 303 { 304 mapping.put(pressed, target); 305 return this; 306 } 307 /** 308 * Remaps many keypress combinations, each of which is a char and several potential modifiers, to other keypress 309 * combinations. When the first of a pair of combinations is received, it will be translated to the second 310 * combination of the pair. 311 * @see #combineModifiers(char, boolean, boolean, boolean) combineModifiers is usually used to make the contents of pairs 312 * @param pairs an int array alternating source and target keypresses, each probably produced by {@link #combineModifiers(char, boolean, boolean, boolean)} 313 * @return this for chaining 314 */ 315 public SquidInput remap(int[] pairs) 316 { 317 int len; 318 if(pairs == null || (len = pairs.length) <= 1) 319 return this; 320 for (int i = 0; i < len - 1; i++) { 321 mapping.put(pairs[i], pairs[++i]); 322 } 323 return this; 324 } 325 326 /** 327 * Removes a keypress combination from the mapping by specifying the char and any modifiers that are part of the 328 * keypress combination. This combination will no longer be remapped, but the original handling will be the same. 329 * @param pressedChar a char that might be used in handling, like 'q', 'A', '7', '(', or {@link #UP_ARROW}. 330 * @param pressedAlt true if alt is part of the combined keypress, false otherwise 331 * @param pressedCtrl true if ctrl is part of the combined keypress, false otherwise 332 * @param pressedShift true if shift is part of the combined keypress, false otherwise 333 * @return this for chaining 334 */ 335 public SquidInput unmap(char pressedChar, boolean pressedAlt, boolean pressedCtrl, boolean pressedShift){ 336 mapping.remove(combineModifiers(pressedChar, pressedAlt,pressedCtrl,pressedShift), 0); 337 return this; 338 } 339 /** 340 * Removes a keypress combination from the mapping by specifying the keypress combination as an int. This 341 * combination will no longer be remapped, but the original handling will be the same. 342 * @see #combineModifiers(char, boolean, boolean, boolean) combineModifiers is usually used to make pressed 343 * @param pressed an int for the source keypress, probably produced by {@link #combineModifiers(char, boolean, boolean, boolean)} 344 * @return this for chaining 345 */ 346 public SquidInput unmap(int pressed) 347 { 348 mapping.remove(pressed, 0); 349 return this; 350 } 351 352 /** 353 * Removes any remappings to key bindings that were in use in this SquidInput. 354 * @return this for chaining 355 */ 356 public SquidInput clearMapping() 357 { 358 mapping.clear(); 359 return this; 360 } 361 /** 362 * Combines the key (as it would be given to {@link KeyHandler#handle(char, boolean, boolean, boolean)}) with the 363 * three booleans for the alt, ctrl, and shift modifier keys, returning an int that can be used with the internal 364 * queue of ints or the public {@link #mapping} of received inputs to actual inputs the program can process. 365 * @param key a char that might be used in handling, like 'q', 'A', '7', '(', or {@link #UP_ARROW}. 366 * @param alt true if alt is part of this combined keypress, false otherwise 367 * @param ctrl true if ctrl is part of this combined keypress, false otherwise 368 * @param shift true if shift is part of this combined keypress, false otherwise 369 * @return an int that contains the information to represent the key with any modifiers as one value 370 */ 371 public static int combineModifiers(char key, boolean alt, boolean ctrl, boolean shift) { 372 int c = alt ? (key | 0x10000) : key; 373 c |= ctrl ? 0x20000 : 0; 374 c |= shift ? 0x40000 : 0; 375 return c; 376 } 377 378 /** 379 * Gets the current key remapping as a String, which can be saved in a file and read back with 380 * {@link #keyMappingFromString(String)}. Because that method is automatically called by SquidInput's constructor 381 * and tries to load a file named "keymap.preferences" in the local folder, it is recommended that you save 382 * user-remapped keys to that file, but only if the user has actively changed them (that file could exist from an 383 * earlier run and you don't want to affect it unless the user made some change). This will allow input with any of 384 * Shift, Alt, and Ctl modifiers in any script supported by the Unicode Basic Multilingual Plane (the first 65536 385 * chars of Unicode). 386 * @return a String that stores four chars per remapping. 387 */ 388 public String keyMappingToString() 389 { 390 char[] cs = new char[mapping.size << 2]; 391 int i = 0; 392 for(IntIntMap.Entry ent : mapping) 393 { 394 cs[i++] = (char)(ent.key); 395 cs[i++] = (char)((ent.key>>>16) + 64); 396 cs[i++] = (char)(ent.value); 397 cs[i++] = (char)((ent.value>>>16) + 64); 398 } 399 return String.valueOf(cs); 400 } 401 402 /** 403 * Reads in a String (almost certainly produced by {@link #keyMappingToString()}) to set the current key remapping. 404 * This is automatically called if a file named "keymap.preferences" is present in the local folder, receiving the 405 * contents of that file as a parameter, so games that want to save a user's preferences should try to save that 406 * file if possible (this also allows one file to be reused across multiple computers or installations by copying 407 * it). This will allow input with any of Shift, Alt, and Ctl modifiers in any script supported by the Unicode Basic 408 * Multilingual Plane (the first 65536 chars of Unicode). You may want to call {@link #clearMapping()} before 409 * calling this if you have already set the mapping and want the String's contents to be used as the only mapping. 410 * @param keymap a String that was probably produced by {@link #keyMappingToString()} 411 * @return this for chaining 412 */ 413 public SquidInput keyMappingFromString(String keymap) 414 { 415 int len, k, v; 416 if(keymap == null || (len = keymap.length()) < 4) 417 return this; 418 for (int i = 0; i < len - 3; i++) { 419 k = keymap.charAt(i); 420 k |= (keymap.charAt(++i) - 64) << 16; 421 v = keymap.charAt(++i); 422 v |= (keymap.charAt(++i) - 64) << 16; 423 mapping.put(k, v); 424 } 425 return this; 426 } 427 428 /** 429 * Processes all events queued up, passing them through this object's key processing and then to keyHandler. Mouse 430 * events are not queued and are processed when they come in. 431 */ 432 public void drain () { 433 IntVLA qu = queue; 434 435 if (keyAction == null || qu.size <= 0) { 436 qu.clear(); 437 return; 438 } 439 440 for (int i = 0, n = qu.size, t; i < n; ) { 441 t = qu.get(i++); 442 t = mapping.get(t, t); 443 keyAction.handle((char)t, (t & 0x10000) != 0, (t & 0x20000) != 0, (t & 0x40000) != 0); 444 } 445 446 qu.clear(); 447 } 448 449 /** 450 * Gets the amount of milliseconds of holding a key this requires to count as a key repeat. The default is 220. 451 * @return how long a key needs to be held before this will count it as a key repeat, as a long in milliseconds 452 */ 453 public long getRepeatGap() { 454 return repeatGapMillis; 455 } 456 457 /** 458 * Sets the amount of milliseconds of holding a key this requires to count as a key repeat. The default is 220. 459 * @param time how long a key needs to be held before this will count it as a key repeat, as a positive long in milliseconds 460 */ 461 public void setRepeatGap(long time) { 462 repeatGapMillis = time; 463 } 464 465 /** 466 * Returns true if at least one event is queued, but also will call {@link #keyDown(int)} if a key is being held but 467 * there is a failure to process the repeated event. The conditions this checks: 468 * <ul> 469 * <li>Is a key is currently being held?</li> 470 * <li>Are no modifier keys being held (shift, control, alt)? (without this check, the modifier key counts as a 471 * repeat of the key it modifies, which is probably never the intended behavior)</li> 472 * <li>Has {@link #keyDown(int)} already been called at least once?</li> 473 * <li>Have there been at least {@link #getRepeatGap()} milliseconds between the last key being received and this call?</li> 474 * </ul> 475 * If all of these conditions are true, keyDown() is called again with the last key it had received, and if this has 476 * an effect (the key can be handled), then generally an event should be queued, so this will have a next event and 477 * should return true. You can change the amount of time required to hold a key to cause key repeats with 478 * {@link #setRepeatGap(long)}, but the default of 220 ms is usually suitable. Too low of a value can cause normal 479 * key presses to be counted twice or more, and too high of a value may delay an expected key repeat. 480 * @return true if there is an event queued, false otherwise 481 */ 482 public boolean hasNext() 483 { 484 if( 485 Gdx.input.isKeyPressed(Input.Keys.ANY_KEY) 486 && !Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) 487 && !Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT) 488 && !Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) 489 && !Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT) 490 && !Gdx.input.isKeyPressed(Input.Keys.ALT_LEFT) 491 && !Gdx.input.isKeyPressed(Input.Keys.ALT_RIGHT) 492 && !heldCodes.isEmpty() 493 && TimeUtils.timeSinceMillis(lastKeyTime) > repeatGapMillis // defaults to 220 ms 494 ) 495 { 496 keyDown(heldCodes.lastKey()); 497 } 498 return queue.size > 0; 499 } 500 501 /** 502 * Processes the first key event queued up, passing it to this object's InputProcessor. Mouse events are not 503 * queued and are processed when they come in. 504 */ 505 public void next() { 506 IntVLA qu = queue; 507 if (keyAction == null) { 508 qu.clear(); // Accidentally captured a keypress when keys aren't meant to be handled 509 return; 510 } 511 if (qu.isEmpty()) { 512 return; 513 } 514// 515// for (int i = 0; i < qu.size; i++) { 516// System.out.print((char)qu.get(i)); 517// } 518// System.out.println(); 519// 520 int t = qu.removeIndex(0); 521 t = mapping.get(t, t); 522 keyAction.handle((char)t, (t & 0x10000) != 0, (t & 0x20000) != 0, (t & 0x40000) != 0); 523 524 } 525 526 /** 527 * Empties the backing queue of data. 528 */ 529 public void flush() 530 { 531 queue.clear(); 532 } 533 534 @Override 535 public boolean keyDown (int keycode) { 536 if (ignoreInput || keyAction == null) { 537 return false; 538 } 539 heldCodes.put(keycode, (int)(lastKeyTime = TimeUtils.millis())); 540 boolean shift = Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT); 541 int c = fromCode(keycode, shift); 542 if(c != '\0') { 543 c |= (Gdx.input.isKeyPressed(Input.Keys.ALT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.ALT_RIGHT)) 544 ? 0x10000 : 0; 545 c |= (Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT)) 546 ? 0x20000 : 0; 547 c |= (shift) 548 ? 0x40000 : 0; 549 //if(queue.isEmpty()) 550 queue.add(c); 551 //else 552 // queue.set(0, c); 553 } 554 return false; 555 } 556 @Override 557 public boolean keyUp (int keycode) { 558 heldCodes.remove(keycode); 559 if(heldCodes.isEmpty()) 560 queue.clear(); 561 return false; 562 } 563 564 @Override 565 public boolean keyTyped (char character) { 566 return false; 567 } 568 569 @Override 570 public boolean touchDown (int screenX, int screenY, int pointer, int button) { 571 if(ignoreInput || mouse == null) return false; 572 return mouse.touchDown(screenX, screenY, pointer, button); 573 } 574 575 @Override 576 public boolean touchUp (int screenX, int screenY, int pointer, int button) { 577 if(ignoreInput || mouse == null) return false; 578 return mouse.touchUp(screenX, screenY, pointer, button); 579 } 580 581 @Override 582 public boolean touchDragged (int screenX, int screenY, int pointer) { 583 if(ignoreInput || mouse == null) return false; 584 return mouse.touchDragged(screenX, screenY, pointer); 585 } 586 587 @Override 588 public boolean mouseMoved (int screenX, int screenY) { 589 if(ignoreInput || mouse == null) return false; 590 return mouse.mouseMoved(screenX, screenY); 591 } 592 593 @Override 594 public boolean scrolled (int amount) { 595 if(ignoreInput || mouse == null) return false; 596 return mouse.scrolled(amount); 597 } 598 599 /** 600 * Maps keycodes to unicode chars, sometimes depending on whether the Shift key is held. 601 * 602 * It is strongly recommended that you refer to key combinations regarding non-alphabet keys by using, for example, 603 * Ctrl-Shift-; instead of Ctrl-:, that is to use the unshifted key with Shift instead of assuming that all 604 * keyboards will use the QWERTY layout. Pressing shift while pressing just about any representable symbol will map 605 * to the shifted version as if on a QWERTY keyboard, and if you don't have a QWERTY keyboard, the mappings are 606 * documented in full below. 607 * 608 * Keys 'a' to 'z' report 'A' to 'Z' when shift is held. Non-ASCII-Latin characters do not have this behavior, since 609 * most keyboards would be unable to send keys for a particular language and A-Z are very common. You can still 610 * allow a response to, e.g. 'ä' and 'Ä' separately by checking for whether Shift was pressed in conjunction with 611 * 'ä' on a keyboard with that key, which can be useful when users can configure their own keyboard layouts. 612 * 613 * Top row numbers map as follows: 614 * 615 * {@literal '1' to '!', '2' to '@', '3' to '#', '4' to '$', '5' to '%',} 616 * {@literal '6' to '^', '7' to '&', '8' to '*', '9' to '(', '0' to ')'} 617 * 618 * Numpad numbers will report a SquidInput constant such as UP_LEFT_ARROW for Numpad 7, but only if numpadDirections 619 * is true; otherwise they send the number (here, 7). Numpad 0 sends VERTICAL_ARROW or 0. 620 * 621 * Most symbol keys are mapped to a single unicode char as a constant in SquidInput and disregard Shift. The 622 * constant is usually the same as the name of the char; possible exceptions are Backspace (on PC) or Delete (on 623 * Mac) mapping to BACKSPACE, Delete (on PC) mapping to FORWARD_DELETE, Esc mapping to ESCAPE, and Enter (on PC) or 624 * Return (on Mac) mapping to ENTER. 625 * 626 * {@literal ':', '*', '#', '@'}, and space keys, if present, always map to themselves, regardless of Shift. 627 * 628 * Other characters map as follows when Shift is held, as they would on a QWERTY keyboard: 629 * <ul> 630 * <li>{@code ','} to {@code '<'}</li> 631 * <li>{@code '.'} to {@code '>'}</li> 632 * <li>{@code '/'} to {@code '?'}</li> 633 * <li>{@code ';'} to {@code ':'}</li> 634 * <li>{@code '\''} to {@code '"'}</li> 635 * <li>{@code '['} to <code>'{'</code></li> 636 * <li>{@code ']'} to <code>'}'</code></li> 637 * <li>{@code '|'} to {@code '\\'}</li> 638 * <li>{@code '-'} to {@code '_'}</li> 639 * <li>{@code '+'} to {@code '='}</li> 640 * <li>{@code '`'} to {@code '~'} (note, this key produces no event on the GWT backend)</li> 641 * </ul> 642 * @param keycode a keycode as passed by LibGDX 643 * @param shift true if Shift key is being held. 644 * @return a char appropriate to the given keycode; often uses shift to capitalize or change a char, but not for keys like the arrow keys that normally don't produce chars 645 */ 646 public char fromCode(int keycode, boolean shift) 647 { 648 switch (keycode) { 649 case Input.Keys.HOME: 650 return HOME; 651 case Input.Keys.FORWARD_DEL: 652 return FORWARD_DELETE; 653 case Input.Keys.ESCAPE: 654 return ESCAPE; 655 case Input.Keys.END: 656 return END; 657 658 case Input.Keys.UP: 659 return UP_ARROW; 660 case Input.Keys.DOWN: 661 return DOWN_ARROW; 662 case Input.Keys.LEFT: 663 return LEFT_ARROW; 664 case Input.Keys.RIGHT: 665 return RIGHT_ARROW; 666 case Input.Keys.CENTER: 667 return CENTER_ARROW; 668 669 case Input.Keys.NUM_0: 670 return (shift) ? ')' : '0'; 671 case Input.Keys.NUM_1: 672 return (shift) ? '!' : '1'; 673 case Input.Keys.NUM_2: 674 return (shift) ? '@' : '2'; 675 case Input.Keys.NUM_3: 676 return (shift) ? '#' : '3'; 677 case Input.Keys.NUM_4: 678 return (shift) ? '$' : '4'; 679 case Input.Keys.NUM_5: 680 return (shift) ? '%' : '5'; 681 case Input.Keys.NUM_6: 682 return (shift) ? '^' : '6'; 683 case Input.Keys.NUM_7: 684 return (shift) ? '&' : '7'; 685 case Input.Keys.NUM_8: 686 return (shift) ? '*' : '8'; 687 case Input.Keys.NUM_9: 688 return (shift) ? '(' : '9'; 689 case Input.Keys.NUMPAD_0: 690 return (numpadDirections) ? VERTICAL_ARROW : '0'; 691 case Input.Keys.NUMPAD_1: 692 return (numpadDirections) ? DOWN_LEFT_ARROW : '1'; 693 case Input.Keys.NUMPAD_2: 694 return (numpadDirections) ? DOWN_ARROW : '2'; 695 case Input.Keys.NUMPAD_3: 696 return (numpadDirections) ? DOWN_RIGHT_ARROW : '3'; 697 case Input.Keys.NUMPAD_4: 698 return (numpadDirections) ? LEFT_ARROW : '4'; 699 case Input.Keys.NUMPAD_5: 700 return (numpadDirections) ? CENTER_ARROW : '5'; 701 case Input.Keys.NUMPAD_6: 702 return (numpadDirections) ? RIGHT_ARROW : '6'; 703 case Input.Keys.NUMPAD_7: 704 return (numpadDirections) ? UP_LEFT_ARROW : '7'; 705 case Input.Keys.NUMPAD_8: 706 return (numpadDirections) ? UP_ARROW : '8'; 707 case Input.Keys.NUMPAD_9: 708 return (numpadDirections) ? UP_RIGHT_ARROW : '9'; 709 case Input.Keys.COLON: 710 return ':'; 711 case Input.Keys.STAR: 712 return '*'; 713 case Input.Keys.POUND: 714 return '#'; 715 case Input.Keys.A: 716 return (shift) ? 'A' : 'a'; 717 case Input.Keys.B: 718 return (shift) ? 'B' : 'b'; 719 case Input.Keys.C: 720 return (shift) ? 'C' : 'c'; 721 case Input.Keys.D: 722 return (shift) ? 'D' : 'd'; 723 case Input.Keys.E: 724 return (shift) ? 'E' : 'e'; 725 case Input.Keys.F: 726 return (shift) ? 'F' : 'f'; 727 case Input.Keys.G: 728 return (shift) ? 'G' : 'g'; 729 case Input.Keys.H: 730 return (shift) ? 'H' : 'h'; 731 case Input.Keys.I: 732 return (shift) ? 'I' : 'i'; 733 case Input.Keys.J: 734 return (shift) ? 'J' : 'j'; 735 case Input.Keys.K: 736 return (shift) ? 'K' : 'k'; 737 case Input.Keys.L: 738 return (shift) ? 'L' : 'l'; 739 case Input.Keys.M: 740 return (shift) ? 'M' : 'm'; 741 case Input.Keys.N: 742 return (shift) ? 'N' : 'n'; 743 case Input.Keys.O: 744 return (shift) ? 'O' : 'o'; 745 case Input.Keys.P: 746 return (shift) ? 'P' : 'p'; 747 case Input.Keys.Q: 748 return (shift) ? 'Q' : 'q'; 749 case Input.Keys.R: 750 return (shift) ? 'R' : 'r'; 751 case Input.Keys.S: 752 return (shift) ? 'S' : 's'; 753 case Input.Keys.T: 754 return (shift) ? 'T' : 't'; 755 case Input.Keys.U: 756 return (shift) ? 'U' : 'u'; 757 case Input.Keys.V: 758 return (shift) ? 'V' : 'v'; 759 case Input.Keys.W: 760 return (shift) ? 'W' : 'w'; 761 case Input.Keys.X: 762 return (shift) ? 'X' : 'x'; 763 case Input.Keys.Y: 764 return (shift) ? 'Y' : 'y'; 765 case Input.Keys.Z: 766 return (shift) ? 'Z' : 'z'; 767 case Input.Keys.COMMA: 768 return (shift) ? '<' : ','; 769 case Input.Keys.PERIOD: 770 return (shift) ? '>' :'.'; 771 case Input.Keys.TAB: 772 return TAB; 773 case Input.Keys.SPACE: 774 return ' '; 775 case Input.Keys.ENTER: 776 return ENTER; 777 case Input.Keys.BACKSPACE: 778 return BACKSPACE; // also DEL 779 case Input.Keys.GRAVE: 780 return (shift) ? '~' : '`'; 781 case Input.Keys.MINUS: 782 return (shift) ? '_' : '-'; 783 case Input.Keys.EQUALS: 784 return (shift) ? '+' :'='; 785 case Input.Keys.LEFT_BRACKET: 786 return (shift) ? '{' :'['; 787 case Input.Keys.RIGHT_BRACKET: 788 return (shift) ? '}' :']'; 789 case Input.Keys.BACKSLASH: 790 return (shift) ? '|' :'\\'; 791 case Input.Keys.SEMICOLON: 792 return (shift) ? ':' :';'; 793 case Input.Keys.APOSTROPHE: 794 return (shift) ? '"' :'\''; 795 case Input.Keys.SLASH: 796 return (shift) ? '?' :'/'; 797 case Input.Keys.AT: 798 return '@'; 799 case Input.Keys.PAGE_UP: 800 return PAGE_UP; 801 case Input.Keys.PAGE_DOWN: 802 return PAGE_DOWN; 803 case Input.Keys.BUTTON_A: 804 return GAMEPAD_A; 805 case Input.Keys.BUTTON_B: 806 return GAMEPAD_B; 807 case Input.Keys.BUTTON_C: 808 return GAMEPAD_C; 809 case Input.Keys.BUTTON_X: 810 return GAMEPAD_X; 811 case Input.Keys.BUTTON_Y: 812 return GAMEPAD_Y; 813 case Input.Keys.BUTTON_Z: 814 return GAMEPAD_Z; 815 case Input.Keys.BUTTON_L1: 816 return GAMEPAD_L1; 817 case Input.Keys.BUTTON_R1: 818 return GAMEPAD_R1; 819 case Input.Keys.BUTTON_L2: 820 return GAMEPAD_L2; 821 case Input.Keys.BUTTON_R2: 822 return GAMEPAD_R2; 823 case Input.Keys.BUTTON_THUMBL: 824 return GAMEPAD_LEFT_THUMB; 825 case Input.Keys.BUTTON_THUMBR: 826 return GAMEPAD_RIGHT_THUMB; 827 case Input.Keys.BUTTON_START: 828 return GAMEPAD_START; 829 case Input.Keys.BUTTON_SELECT: 830 return GAMEPAD_SELECT; 831 case Input.Keys.INSERT: 832 return INSERT; 833 834 case Input.Keys.F1: 835 return F1; 836 case Input.Keys.F2: 837 return F2; 838 case Input.Keys.F3: 839 return F3; 840 case Input.Keys.F4: 841 return F4; 842 case Input.Keys.F5: 843 return F5; 844 case Input.Keys.F6: 845 return F6; 846 case Input.Keys.F7: 847 return F7; 848 case Input.Keys.F8: 849 return F8; 850 case Input.Keys.F9: 851 return F9; 852 case Input.Keys.F10: 853 return F10; 854 case Input.Keys.F11: 855 return F11; 856 case Input.Keys.F12: 857 return F12; 858 default: 859 return '\0'; 860 } 861 862 } 863 864 /** 865 * Left arrow key. If numpadDirections is enabled, this will also be sent by Numpad 4. 866 */ 867 public static final char LEFT_ARROW = '\u2190'; 868 /** 869 * Up arrow key. If numpadDirections is enabled, this will also be sent by Numpad 8. 870 */ 871 public static final char UP_ARROW = '\u2191'; 872 /** 873 * Down arrow key. If numpadDirections is enabled, this will also be sent by Numpad 6. 874 */ 875 public static final char RIGHT_ARROW = '\u2192'; 876 /** 877 * Down arrow key. If numpadDirections is enabled, this will also be sent by Numpad 2. 878 */ 879 public static final char DOWN_ARROW = '\u2193'; 880 /** 881 * Not typically a dedicated key, but if numpadDirections is enabled, this will be sent by Numpad 1. 882 */ 883 public static final char DOWN_LEFT_ARROW = '\u2199'; 884 /** 885 * Not typically a dedicated key, but if numpadDirections is enabled, this will be sent by Numpad 3. 886 */ 887 public static final char DOWN_RIGHT_ARROW = '\u2198'; 888 /** 889 * Not typically a dedicated key, but if numpadDirections is enabled, this will be sent by Numpad 9. 890 */ 891 public static final char UP_RIGHT_ARROW = '\u2197'; 892 /** 893 * Not typically a dedicated key, but if numpadDirections is enabled, this will be sent by Numpad 7. 894 */ 895 public static final char UP_LEFT_ARROW = '\u2196'; 896 /** 897 * Not typically a dedicated key, but if numpadDirections is enabled, this will be sent by Numpad 5. 898 */ 899 public static final char CENTER_ARROW = '\u21BA'; 900 /** 901 * Not typically a dedicated key, but if numpadDirections is enabled, this will be sent by Numpad 0. 902 * 903 * Intended for games that might need up a jump or crouch button on the numpad that supplants movement. 904 */ 905 public static final char VERTICAL_ARROW = '\u2195'; 906 /** 907 * Enter key, also called Return key. Used to start a new line of text or confirm entries in forms. 908 */ 909 public static final char ENTER = '\u21B5'; 910 /** 911 * Tab key. Used for entering horizontal spacing, such as indentation, but also often to cycle between menu items. 912 */ 913 public static final char TAB = '\u21B9'; 914 /** 915 * Backspace key on most PC keyboards; Delete key on Mac keyboards. Used to delete the previous character. 916 */ 917 public static final char BACKSPACE = '\u2280'; 918 /** 919 * Delete key on most PC keyboards; no equivalent on some (all?) Mac keyboards. Used to delete the next character. 920 * 921 * Not present on some laptop keyboards and some (all?) Mac keyboards. 922 */ 923 public static final char FORWARD_DELETE = '\u2281'; 924 /** 925 * Insert key. Not recommended for common use because it could affect other application behavior. 926 * 927 * Not present on some laptop keyboards. 928 */ 929 public static final char INSERT = '\u2208'; 930 /** 931 * Page Down key. 932 * 933 * Not present on some laptop keyboards. 934 */ 935 public static final char PAGE_DOWN = '\u22A4'; 936 /** 937 * Page Up key. 938 * 939 * Not present on some laptop keyboards. 940 */ 941 public static final char PAGE_UP = '\u22A5'; 942 /** 943 * Home key (commonly used for moving a cursor to start of line). 944 * 945 * Not present on some laptop keyboards. 946 */ 947 public static final char HOME = '\u2302'; 948 /** 949 * End key (commonly used for moving a cursor to end of line). 950 * 951 * Not present on some laptop keyboards. 952 */ 953 public static final char END = '\u2623'; 954 /** 955 * Esc or Escape key 956 */ 957 public static final char ESCAPE = '\u2620'; 958 959 /** 960 * Function key F1 961 */ 962 public static final char F1 = '\u2460'; 963 /** 964 * Function key F2 965 */ 966 public static final char F2 = '\u2461'; 967 /** 968 * Function key F3 969 */ 970 public static final char F3 = '\u2462'; 971 /** 972 * Function key F4 973 */ 974 public static final char F4 = '\u2463'; 975 /** 976 * Function key F5 977 */ 978 public static final char F5 = '\u2464'; 979 /** 980 * Function key F6 981 */ 982 public static final char F6 = '\u2465'; 983 /** 984 * Function key F7 985 */ 986 public static final char F7 = '\u2466'; 987 /** 988 * Function key F8 989 */ 990 public static final char F8 = '\u2467'; 991 /** 992 * Function key F9 993 */ 994 public static final char F9 = '\u2468'; 995 /** 996 * Function key F10 997 */ 998 public static final char F10 = '\u2469'; 999 /** 1000 * Function key F11 1001 */ 1002 public static final char F11 = '\u246A'; 1003 /** 1004 * Function key F12 1005 */ 1006 public static final char F12 = '\u246B'; 1007 1008 /** 1009 * Gamepad A button. 1010 */ 1011 public static final char GAMEPAD_A = '\u24b6'; 1012 /** 1013 * Gamepad B button. 1014 */ 1015 public static final char GAMEPAD_B = '\u24b7'; 1016 /** 1017 * Gamepad C button. 1018 */ 1019 public static final char GAMEPAD_C = '\u24b8'; 1020 /** 1021 * Gamepad X button. 1022 */ 1023 public static final char GAMEPAD_X = '\u24cd'; 1024 /** 1025 * Gamepad Y button. 1026 */ 1027 public static final char GAMEPAD_Y = '\u24ce'; 1028 /** 1029 * Gamepad Z button. 1030 */ 1031 public static final char GAMEPAD_Z = '\u24cf'; 1032 1033 /** 1034 * Gamepad L1 button. 1035 */ 1036 public static final char GAMEPAD_L1 = '\u24c1'; 1037 /** 1038 * Gamepad L2 button. 1039 */ 1040 public static final char GAMEPAD_L2 = '\u24db'; 1041 /** 1042 * Gamepad R1 button. 1043 */ 1044 public static final char GAMEPAD_R1 = '\u24c7'; 1045 /** 1046 * Gamepad R2 button. 1047 */ 1048 public static final char GAMEPAD_R2 = '\u24e1'; 1049 /** 1050 * Gamepad Left Thumb button. 1051 */ 1052 public static final char GAMEPAD_LEFT_THUMB = '\u24a7'; 1053 /** 1054 * Gamepad Right Thumb button. 1055 */ 1056 public static final char GAMEPAD_RIGHT_THUMB = '\u24ad'; 1057 /** 1058 * Gamepad Start button. 1059 */ 1060 public static final char GAMEPAD_START = '\u2713'; 1061 /** 1062 * Gamepad Select button. 1063 */ 1064 public static final char GAMEPAD_SELECT = '\u261C'; 1065 1066 1067}