Reactos
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)
61inline 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)
76static char buffer[1024];
77static 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?
84inline 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
100inline 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)
118const 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
144const 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
174int 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
199int 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
320WORD 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
351static TNetwork net;
352void setTNetwork(TNetwork tnet) {
353 net = tnet;
354}
355
356// Thomas Briggs 8/17/98
357BOOL 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}