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