1 /////////////////////////////////////////////////////////////////////////////// 2 //Telnet Win32 : an ANSI telnet client. 3 //Copyright (C) 1998-2000 Paul Brannan 4 //Copyright (C) 1998 I.Ioannou 5 //Copyright (C) 1997 Brad Johnson 6 // 7 //This program is free software; you can redistribute it and/or 8 //modify it under the terms of the GNU General Public License 9 //as published by the Free Software Foundation; either version 2 10 //of the License, or (at your option) any later version. 11 // 12 //This program is distributed in the hope that it will be useful, 13 //but WITHOUT ANY WARRANTY; without even the implied warranty of 14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 //GNU General Public License for more details. 16 // 17 //You should have received a copy of the GNU General Public License 18 //along with this program; if not, write to the Free Software 19 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 // 21 //I.Ioannou 22 //roryt@hol.gr 23 // 24 /////////////////////////////////////////////////////////////////////////// 25 26 /////////////////////////////////////////////////////////////////////////////// 27 // 28 // Module: tncon.cpp 29 // 30 // Contents: telnet console processing 31 // 32 // Product: telnet 33 // 34 // Revisions: August 30, 1998 Paul Brannan <pbranna@clemson.edu> 35 // July 29, 1998 Paul Brannan 36 // June 15, 1998 Paul Brannan 37 // May 16, 1998 Paul Brannan 38 // 5.April.1997 jbj@nounname.com 39 // 9.Dec.1996 jbj@nounname.com 40 // Version 2.0 41 // 42 // 02.Apr.1995 igor.milavec@uni-lj.si 43 // Original code 44 // 45 /////////////////////////////////////////////////////////////////////////////// 46 47 #include "precomp.h" 48 49 #define KEYEVENT InputRecord[i].Event.KeyEvent 50 51 // Paul Brannan 6/25/98 52 // #ifdef __MINGW32__ 53 // #define KEYEVENT_CHAR KEYEVENT.AsciiChar 54 // #else 55 #define KEYEVENT_CHAR KEYEVENT.uChar.AsciiChar 56 // #endif 57 58 #define KEYEVENT_PCHAR &KEYEVENT_CHAR 59 60 // This is for local echo (Paul Brannan 5/16/98) 61 inline void DoEcho(const char *p, int l, TConsole &Console, 62 TNetwork &Network, NetParams *pParams) { 63 // Pause the console (Paul Brannan 8/24/98) 64 if(Network.get_local_echo()) { 65 ResetEvent(pParams->hUnPause); 66 SetEvent(pParams->hPause); 67 while (!*pParams->bNetPaused); // Pause 68 69 Console.WriteCtrlString(p, l); 70 71 SetEvent(pParams->hUnPause); // Unpause 72 } 73 } 74 75 // This is for line mode (Paul Brannan 12/31/98) 76 static char buffer[1024]; 77 static unsigned int bufptr = 0; 78 79 // Line mode -- currently uses sga/echo to determine when to enter line mode 80 // (as in RFC 858), but correct behaviour is as described in RFC 1184. 81 // (Paul Brannan 12/31/98) 82 // FIX ME!! What to do with unflushed data when we change from line mode 83 // to character mode? 84 inline bool DoLineModeSpecial(char keychar, TConsole &Console, TNetwork &Network, 85 NetParams *pParams) { 86 if(keychar == VK_BACK) { 87 if(bufptr) bufptr--; 88 DoEcho("\b \b", 3, Console, Network, pParams); 89 return true; 90 } else if(keychar == VK_RETURN) { 91 Network.WriteString(buffer, bufptr); 92 Network.WriteString("\012", 1); 93 DoEcho("\r\n", 2, Console, Network, pParams); 94 bufptr = 0; 95 return true; 96 } 97 return false; 98 } 99 100 inline void DoLineMode(const char *p, int p_len, TConsole &Console, 101 TNetwork &Network) { 102 if(Network.get_line_mode()) { 103 if(bufptr < sizeof(buffer) + p_len - 1) { 104 memcpy(buffer + bufptr, p, p_len); 105 bufptr += p_len; 106 } else { 107 Console.Beep(); 108 } 109 } else { 110 Network.WriteString(p, p_len); 111 } 112 } 113 114 // Paul Brannan 5/27/98 115 // Fixed this code for use with appliation cursor keys 116 // This should probably be optimized; it's pretty ugly as it is 117 // Rewrite #1: now uses ClosestStateKey (Paul Brannan 12/9/98) 118 const char *ClosestStateKey(WORD keyCode, DWORD keyState, 119 KeyTranslator &KeyTrans) { 120 char const *p; 121 122 if((p = KeyTrans.TranslateKey(keyCode, keyState))) return p; 123 124 // Check numlock and scroll lock (Paul Brannan 9/23/98) 125 if((p = KeyTrans.TranslateKey(keyCode, keyState & ~NUMLOCK_ON))) return p; 126 if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY 127 & ~NUMLOCK_ON))) return p; 128 if((p = KeyTrans.TranslateKey(keyCode, keyState & ~SCROLLLOCK_ON))) return p; 129 if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY 130 & ~SCROLLLOCK_ON))) return p; 131 132 // John Ioannou (roryt@hol.gr) 133 // Athens 31/03/97 00:25am GMT+2 134 // fix for win95 CAPSLOCK bug 135 // first check if the user has keys with capslock and then we filter it 136 if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY))) return p; 137 if((p = KeyTrans.TranslateKey(keyCode, keyState & ~CAPSLOCK_ON))) return p; 138 if((p = KeyTrans.TranslateKey(keyCode, keyState & ~ENHANCED_KEY 139 & ~CAPSLOCK_ON))) return p; 140 141 return 0; // we couldn't find a suitable key translation 142 } 143 144 const char *FindClosestKey(WORD keyCode, DWORD keyState, 145 KeyTranslator &KeyTrans) { 146 char const *p; 147 148 // Paul Brannan 7/20/98 149 if(ini.get_alt_erase()) { 150 if(keyCode == VK_BACK) { 151 keyCode = VK_DELETE; 152 keyState |= ENHANCED_KEY; 153 } else if(keyCode == VK_DELETE && (keyState & ENHANCED_KEY)) { 154 keyCode = VK_BACK; 155 keyState &= ~ENHANCED_KEY; 156 } 157 } 158 159 DWORD ext_mode = KeyTrans.get_ext_mode(); 160 if(ext_mode) { 161 // Not as fast as an unrolled loop, but certainly more 162 // compact (Paul Brannan 12/9/98) 163 for(DWORD j = ext_mode; j >= APP_KEY; j -= APP_KEY) { 164 if((j | ext_mode) == ext_mode) { 165 if((p = ClosestStateKey(keyCode, keyState | j, 166 KeyTrans))) return p; 167 } 168 } 169 } 170 return ClosestStateKey(keyCode, keyState, KeyTrans); 171 } 172 173 // Paul Brannan Feb. 22, 1999 174 int do_op(tn_ops op, TNetwork &Network, Tnclip &Clipboard) { 175 switch(op) { 176 case TN_ESCAPE: 177 return TNPROMPT; 178 case TN_SCROLLBACK: 179 return TNSCROLLBACK; 180 case TN_DIAL: 181 return TNSPAWN; 182 case TN_PASTE: 183 if(ini.get_keyboard_paste()) Clipboard.Paste(); 184 else return 0; 185 break; 186 case TN_NULL: 187 Network.WriteString("", 1); 188 return 0; 189 case TN_CR: 190 Network.WriteString("\r", 2); // CR must be followed by NUL 191 return 0; 192 case TN_CRLF: 193 Network.WriteString("\r\n", 2); 194 return 0; 195 } 196 return 0; 197 } 198 199 int telProcessConsole(NetParams *pParams, KeyTranslator &KeyTrans, 200 TConsole &Console, TNetwork &Network, TMouse &Mouse, 201 Tnclip &Clipboard, HANDLE hThread) 202 { 203 KeyDefType_const keydef; 204 const char *p; 205 int p_len; 206 unsigned int i; 207 int opval; 208 HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE); 209 210 SetConsoleMode(hConsole, ini.get_enable_mouse() ? ENABLE_MOUSE_INPUT : 0); 211 212 const DWORD nHandle = 2; 213 HANDLE hHandle[nHandle] = {hConsole, pParams->hExit}; 214 215 for (;;) { 216 DWORD dwInput; 217 switch (WaitForMultipleObjects(nHandle, hHandle, FALSE, INFINITE)) { 218 case WAIT_OBJECT_0: { 219 220 // Paul Brannan 7/29/98 221 if(ini.get_input_redir()) { 222 char InputBuffer[10]; 223 224 // Correction from Joe Manns <joe.manns@ardenenginneers.com> 225 // to fix race conditions (4/13/99) 226 int bResult; 227 bResult = ReadFile(hConsole, InputBuffer, 10, &dwInput, 0); 228 if(bResult && dwInput == 0) return TNNOCON; 229 230 // no key translation for redirected input 231 Network.WriteString(InputBuffer, dwInput); 232 break; 233 } 234 235 INPUT_RECORD InputRecord[11]; 236 if (!ReadConsoleInput(hConsole, &InputRecord[0], 10, &dwInput)) 237 return TNPROMPT; 238 239 for (i = 0; (unsigned)i < dwInput; i++){ 240 switch (InputRecord[i].EventType) { 241 case KEY_EVENT:{ 242 if (KEYEVENT.bKeyDown) { 243 244 WORD keyCode = KEYEVENT.wVirtualKeyCode; 245 DWORD keyState = KEYEVENT.dwControlKeyState; 246 247 // Paul Brannan 5/27/98 248 // Moved the code that was here to FindClosestKey() 249 keydef.szKeyDef = FindClosestKey(keyCode, 250 keyState, KeyTrans); 251 252 if(keydef.szKeyDef) { 253 if(!keydef.op->sendstr) 254 if((opval = do_op(keydef.op->the_op, Network, 255 Clipboard)) != 0) 256 return opval; 257 } 258 259 if(Network.get_line_mode()) { 260 if(DoLineModeSpecial(KEYEVENT_CHAR, Console, Network, pParams)) 261 continue; 262 } 263 264 p = keydef.szKeyDef; 265 if (p == NULL) { // if we don't have a translator 266 if(!KEYEVENT_CHAR) continue; 267 p_len = 1; 268 p = KEYEVENT_PCHAR; 269 } else { 270 p_len = strlen(p); 271 } 272 273 // Local echo (Paul Brannan 5/16/98) 274 DoEcho(p, p_len, Console, Network, pParams); 275 // Line mode (Paul Brannan 12/31/98) 276 DoLineMode(p, p_len, Console, Network); 277 } 278 } 279 break; 280 281 case MOUSE_EVENT: 282 if(!InputRecord[i].Event.MouseEvent.dwEventFlags) { 283 ResetEvent(pParams->hUnPause); 284 SetEvent(pParams->hPause); 285 while (!*pParams->bNetPaused); // thread paused 286 // SuspendThread(hThread); 287 288 // Put the mouse's X and Y coords back into the 289 // input buffer 290 DWORD Result; 291 WriteConsoleInput(hConsole, &InputRecord[i], 1, 292 &Result); 293 294 Mouse.doMouse(); 295 296 SetEvent(pParams->hUnPause); 297 // ResumeThread(hThread); 298 } 299 break; 300 301 case FOCUS_EVENT: 302 break; 303 case WINDOW_BUFFER_SIZE_EVENT: 304 // FIX ME!! This should take care of the window re-sizing bug 305 // Unfortunately, it doesn't. 306 Console.sync(); 307 Network.do_naws(Console.GetWidth(), Console.GetHeight()); 308 break; 309 } 310 311 } // keep going until no more input 312 break; 313 } 314 default: 315 return TNNOCON; 316 } 317 } 318 } 319 320 WORD scrollkeys() { 321 HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE); 322 INPUT_RECORD InputRecord; 323 BOOL done = FALSE; 324 325 while (!done) { 326 DWORD dwInput; 327 WaitForSingleObject( hConsole, INFINITE ); 328 if (!ReadConsoleInput(hConsole, &InputRecord, 1, &dwInput)){ 329 done = TRUE; 330 continue; 331 } 332 if (InputRecord.EventType == KEY_EVENT && 333 InputRecord.Event.KeyEvent.bKeyDown ) { 334 // Why not just return the key code? (Paul Brannan 12/5/98) 335 return InputRecord.Event.KeyEvent.wVirtualKeyCode; 336 } else if(InputRecord.EventType == MOUSE_EVENT) { 337 if(!InputRecord.Event.MouseEvent.dwEventFlags) { 338 // Put the mouse's X and Y coords back into the input buffer 339 WriteConsoleInput(hConsole, &InputRecord, 1, &dwInput); 340 return SC_MOUSE; 341 } 342 } 343 } 344 return SC_ESC; 345 } 346 347 // FIX ME!! This is more evidence that tncon.cpp ought to have class structure 348 // (Paul Brannan 12/10/98) 349 350 // Bryan Montgomery 10/14/98 351 static TNetwork net; 352 void setTNetwork(TNetwork tnet) { 353 net = tnet; 354 } 355 356 // Thomas Briggs 8/17/98 357 BOOL WINAPI ControlEventHandler(DWORD event) { 358 switch(event) { 359 case CTRL_BREAK_EVENT: 360 // Bryan Montgomery 10/14/98 361 if(ini.get_control_break_as_c()) net.WriteString("\x3",1); 362 return TRUE; 363 default: 364 return FALSE; 365 } 366 } 367