1 module scone.input.os.posix.posix_input; 2 3 version (Posix) 4 { 5 import core.sys.posix.termios; 6 import core.sys.posix.unistd : STDOUT_FILENO; 7 import scone.input.keyboard_event : KeyboardEvent; 8 import scone.input.scone_control_key : SCK; 9 import scone.input.scone_key : SK; 10 import scone.input.os.standard_input : StandardInput; 11 import scone.input.os.posix.background_thread; 12 import scone.input.os.posix.locale.input_map : InputMap; 13 import scone.input.os.posix.locale.locale : Locale; 14 import std.concurrency : spawn, Tid, thisTid, send, receiveTimeout, ownerTid; 15 import std.conv : text; 16 import std.datetime : msecs; 17 18 extern (C) 19 { 20 void cfmakeraw(termios* termios_p); 21 } 22 23 class PosixInput : StandardInput 24 { 25 void initialize() 26 { 27 this.setInputMapping(); 28 this.enableRawInput(); 29 this.startPollingInput(); 30 } 31 32 void deinitialize() 33 { 34 this.resetTerminalState(); 35 } 36 37 uint[] retreiveInputSequence() 38 { 39 uint[] sequence; 40 41 while (receiveTimeout(5.msecs, (uint code) { sequence ~= code; })) 42 { 43 // continiously repeat until no code is recieved within 5 milliseconds 44 } 45 46 return sequence; 47 } 48 49 KeyboardEvent[] latestKeyboardEvents() 50 { 51 auto sequence = this.retreiveInputSequence(); 52 53 //todo: returns null here. should this logic be here or in `inputMap.keyboardEventsFromSequence(sequence)` instead? 54 if (sequence.length == 0) 55 { 56 return null; 57 } 58 59 // conversion to input events. refactor whole (winodws+posix) code to use keypresses instead of input events? 60 KeyboardEvent[] keyboardEvents = []; 61 foreach (KeyboardEvent keypress; inputMap.keyboardEventsFromSequence(sequence)) 62 { 63 keyboardEvents ~= KeyboardEvent(keypress.key, keypress.controlKey); 64 } 65 66 return keyboardEvents; 67 } 68 69 private void startPollingInput() 70 { 71 // begin polling 72 spawn(&pollKeyboardEvent); 73 } 74 75 // unsure when to use this. 76 // on mac this shows a small key icon, used when entering passwords 77 private void setInputEcho(in bool echo) 78 { 79 termios termInfo; 80 tcgetattr(STDOUT_FILENO, &termInfo); 81 82 if (echo) 83 { 84 termInfo.c_lflag |= ECHO; 85 } 86 else 87 { 88 termInfo.c_lflag &= ~ECHO; 89 } 90 91 tcsetattr(STDOUT_FILENO, TCSADRAIN, &termInfo); 92 } 93 94 private void setInputMapping() 95 { 96 Locale locale = new Locale(); 97 this.inputMap = new InputMap(locale.systemLocaleSequences); 98 } 99 100 private void enableRawInput() 101 { 102 // store the state of the terminal 103 tcgetattr(1, &originalTerminalState); 104 105 // enable raw input 106 termios newTerminalState = originalTerminalState; 107 cfmakeraw(&newTerminalState); 108 tcsetattr(STDOUT_FILENO, TCSADRAIN, &newTerminalState); 109 } 110 111 private void resetTerminalState() 112 { 113 tcsetattr(STDOUT_FILENO, TCSADRAIN, &originalTerminalState); 114 } 115 116 private InputMap inputMap; 117 private termios originalTerminalState; 118 } 119 }