Merge remote-tracking branch 'origin/master' into windows
[scilab.git] / scilab / modules / console / src / c / windows / TermConsole.c
1 /*
2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2008 - DIGITEO - Allan CORNET
4 *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13 *
14 */
15 /*--------------------------------------------------------------------------*/
16 #include <Windows.h>
17 #include <stdio.h>
18 #include <ctype.h>
19 #include "TermConsole.h"
20 #include "sci_malloc.h"
21 #include "TermCommand.h"
22 #include "FocusOnConsole.h"
23 #include "os_string.h"
24 #include "TermCompletion.h"
25 #include "TermLine.h"
26 #include "scilines.h"
27 #include "HistoryManager.h"
28 #include "localization.h"
29 #include "storeCommand.h"
30 #include "configvariable_interface.h"
31 /*--------------------------------------------------------------------------*/
32 #ifdef CR_1
33 #undef CR_1
34 #endif
35 #define CR_1 '\n'
36
37 #ifdef CR_2
38 #undef CR_2
39 #endif
40 #define CR_2 '\r'
41 /*--------------------------------------------------------------------------*/
42 static HANDLE Win32OutputStream = NULL, Win32InputStream = NULL;
43 static DWORD OldWin32Mode;
44 /*--------------------------------------------------------------------------*/
45 static BOOL InitTerm = TRUE;
46 /*--------------------------------------------------------------------------*/
47 static unsigned char TerminalGetchar(void);
48 static BOOL isCTRLPressed(DWORD StateKey);
49 static BOOL isCTRL_VKEY(int VKEY);
50 static BOOL isALTPressed(DWORD StateKey);
51 static BOOL isALT_VKEY(int VKEY);
52 static BOOL isExtendedPressed(DWORD StateKey);
53 static void simulateCarriageReturn(void);
54 static char actionControlKey(void);
55 /*--------------------------------------------------------------------------*/
56 static BOOL CtrlHandler( DWORD fdwCtrlType )
57 {
58     switch ( fdwCtrlType )
59     {
60         case CTRL_C_EVENT:
61         {
62             ControlC_Command();
63             newLine();
64             simulateCarriageReturn();
65         }
66         return TRUE;
67     }
68     return FALSE;
69 }
70 /*--------------------------------------------------------------------------*/
71 static void simulateCarriageReturn(void)
72 {
73     INPUT_RECORD rec;
74     DWORD written;
75
76     memset (&rec, 0, sizeof(rec));
77     rec.EventType = KEY_EVENT;
78     rec.Event.KeyEvent.bKeyDown = TRUE;
79     rec.Event.KeyEvent.wRepeatCount = 13;
80     rec.Event.KeyEvent.uChar.AsciiChar = 13;
81
82     if (!Win32InputStream)
83     {
84         Win32InputStream = GetStdHandle(STD_INPUT_HANDLE);
85     }
86     WriteConsoleInput(Win32InputStream, &rec, 1, &written);
87 }
88 /*--------------------------------------------------------------------------*/
89 void InitializeTerminal(void)
90 {
91     if (!Win32InputStream)
92     {
93         Win32InputStream = GetStdHandle(STD_INPUT_HANDLE);
94         GetConsoleMode(Win32InputStream, &OldWin32Mode);
95         SetConsoleMode(Win32InputStream, ENABLE_PROCESSED_INPUT);
96     }
97
98     if (!Win32OutputStream)
99     {
100         Win32OutputStream = GetStdHandle(STD_OUTPUT_HANDLE);
101     }
102
103     setFocusOnConsole();
104
105     SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE );
106
107 }
108 /*--------------------------------------------------------------------------*/
109 void TerminalBeep(void)
110 {
111     MessageBeep(MB_OK);
112 }
113 /*--------------------------------------------------------------------------*/
114 int TerminalPrintf(const char *buffer)
115 {
116     if (buffer)
117     {
118         if (buffer[0] != 0)
119         {
120             int len = (int)strlen (buffer);
121             /* flush all stream */
122             /* problem with fortran output */
123             fflush(NULL);
124
125             len = fputs (buffer, stdout);
126
127             /* flush all stream */
128             /* problem with fortran output */
129             fflush(NULL);
130
131             return len;
132         }
133         return 0;
134     }
135     return -1;
136 }
137 /*--------------------------------------------------------------------------*/
138 int TerminalPutc(char ch)
139 {
140     return putc(ch, stdout);
141 }
142 /*--------------------------------------------------------------------------*/
143 static unsigned char TerminalGetchar(void)
144 {
145     INPUT_RECORD irBuffer;
146     DWORD n = 0;
147     unsigned char ch = 0;
148     do
149     {
150         /* http://bugzilla.scilab.org/show_bug.cgi?id=1052 */
151         if ( ismenu() == 1 )
152         {
153             return 0;
154         }
155
156         WaitForSingleObject(Win32InputStream, INFINITE);
157         PeekConsoleInput (Win32InputStream, &irBuffer, 1, &n);
158
159         switch (irBuffer.EventType)
160         {
161             case KEY_EVENT:
162             {
163                 if (irBuffer.Event.KeyEvent.bKeyDown)
164                 {
165                     if (irBuffer.Event.KeyEvent.dwControlKeyState)
166                     {
167                         if (isCTRLPressed(irBuffer.Event.KeyEvent.dwControlKeyState))
168                         {
169                             char c = actionControlKey();
170                             if (c)
171                             {
172                                 ReadConsoleInputW (Win32InputStream, &irBuffer, 1, &n);
173                                 return c;
174                             }
175                             else
176                             {
177                                 if (irBuffer.Event.KeyEvent.uChar.AsciiChar != '\0')
178                                 {
179                                     ReadConsoleInputW (Win32InputStream, &irBuffer, 1, &n);
180                                     c = irBuffer.Event.KeyEvent.uChar.AsciiChar;
181                                     if ( (c > 0) && !iscntrl(c) )
182                                     {
183                                         return c;
184                                     }
185                                 }
186                                 else
187                                 {
188                                     ReadConsoleInput (Win32InputStream, &irBuffer, 1, &n);
189                                 }
190                             }
191                             break;
192                         }
193
194                         if (isALTPressed(irBuffer.Event.KeyEvent.dwControlKeyState))
195                         {
196                             if (irBuffer.Event.KeyEvent.uChar.AsciiChar != '\0')
197                             {
198                                 ReadConsole (Win32InputStream, &ch, 1, &n, NULL);
199                                 return ch;
200                             }
201                             else
202                             {
203                                 DWORD stateKey = 0;
204                                 WORD vk = 0;
205
206                                 ReadConsoleInput (Win32InputStream, &irBuffer, 1, &n);
207
208                                 stateKey = irBuffer.Event.KeyEvent.dwControlKeyState;
209                                 vk = irBuffer.Event.KeyEvent.wVirtualKeyCode;
210
211                                 switch (vk)
212                                 {
213                                     case VK_F4:
214                                         ALTF4_Command();
215                                         break;
216
217                                     default:
218                                         break;
219                                 }
220                             }
221                             break;
222                         }
223                     }
224
225                     if (irBuffer.Event.KeyEvent.uChar.AsciiChar != '\0')
226                     {
227                         ReadConsole (Win32InputStream, &ch, 1, &n, NULL);
228
229                         switch (ch)
230                         {
231                             case VK_TAB:
232                                 TermCompletion();
233                                 break;
234                             case VK_BACK:
235                                 deletePreviousChar();
236                                 break;
237                             default:
238                             {
239                                 if ( !iscntrl(ch) || (ch == CR_1) || (ch == CR_2) )
240                                 {
241                                     return ch;
242                                 }
243                             }
244                             break;
245                         }
246                     }
247                     else
248                     {
249                         WORD vk = 0;
250                         ReadConsoleInput (Win32InputStream, &irBuffer, 1, &n);
251                         vk = irBuffer.Event.KeyEvent.wVirtualKeyCode;
252
253                         switch (vk)
254                         {
255                             case VK_F1:
256                             case VK_HELP:
257                                 F1_Command();
258                                 break;
259                             case VK_F2:
260                                 F2_Command();
261                                 break;
262                             case VK_LEFT:
263                                 moveBackSingleChar();
264                                 break;
265                             case VK_RIGHT:
266                                 moveForwardSingleChar();
267                                 break;
268                             case VK_UP:
269                                 moveBackHistory();
270                                 break;
271                             case VK_DOWN:
272                                 moveForwardHistory();
273                                 break;
274                             case VK_DELETE:
275                                 deleteCurrentChar();
276                                 break;
277                             case VK_HOME:
278                                 moveBeginningLine();
279                                 break;
280                             case VK_END:
281                                 moveEndLine();
282                                 break;
283                             default:
284                                 break;
285                         }
286                     }
287                 }
288                 else
289                 {
290                     ReadConsoleInput (Win32InputStream, &irBuffer, 1, &n);
291                 }
292             }
293             break;
294             case MOUSE_EVENT:
295             {
296                 /* Read mouse Input but not used */
297                 ReadConsoleInput (Win32InputStream, &irBuffer, 1, &n);
298             }
299             break;
300             case WINDOW_BUFFER_SIZE_EVENT:
301             {
302                 /* Read resize event Input */
303                 setConsoleWidth(irBuffer.Event.WindowBufferSizeEvent.dwSize.X);
304                 setConsoleLines(irBuffer.Event.WindowBufferSizeEvent.dwSize.Y);
305
306                 ReadConsoleInput (Win32InputStream, &irBuffer, 1, &n);
307             }
308             break;
309             case MENU_EVENT:
310             {
311                 ReadConsoleInput (Win32InputStream, &irBuffer, 1, &n);
312             }
313             break;
314             case FOCUS_EVENT:
315             {
316                 ReadConsoleInput (Win32InputStream, &irBuffer, 1, &n);
317             }
318             break;
319             default:
320             {
321                 /* Read Input but not used */
322                 ReadConsoleInput (Win32InputStream, &irBuffer, 1, &n);
323             }
324             break;
325         }
326     }
327     while (TRUE);
328 }
329 /*--------------------------------------------------------------------------*/
330 static char actionControlKey(void)
331 {
332     if ( isCTRL_VKEY('X') || isCTRL_VKEY('C') )
333     {
334         ControlC_Command();
335         return '\n';
336     }
337     else if (isCTRL_VKEY('A')) /* moves to the beginning of the line */
338     {
339         moveBeginningLine();
340     }
341     else if (isCTRL_VKEY('B')) /* moves back a single character */
342     {
343         moveBackSingleChar();
344     }
345     else if (isCTRL_VKEY('D')) /* deletes the current character */
346     {
347         deleteCurrentChar();
348     }
349     else if (isCTRL_VKEY('E')) /* moves to the end of the line */
350     {
351         moveEndLine();
352     }
353     else if (isCTRL_VKEY('F')) /* moves forward a single character */
354     {
355         moveForwardSingleChar();
356     }
357     else if (isCTRL_VKEY('H')) /* delete the previous character */
358     {
359         deletePreviousChar();
360     }
361     else if (isCTRL_VKEY('K')) /* kills from current position to the end of line */
362     {
363         killCurrentPositionToEndLine();
364     }
365     else if (isCTRL_VKEY('N')) /* moves forward through history */
366     {
367         moveForwardHistory();
368     }
369     else if (isCTRL_VKEY('P')) /* moves back through history */
370     {
371         moveBackHistory();
372     }
373     else if ( isCTRL_VKEY('R') || isCTRL_VKEY('L') ) /* redraw line in case it gets trashed */
374     {
375         redrawLine();
376     }
377     else if (isCTRL_VKEY('U')) /* kills the entire line */
378     {
379         clearCurrentLine();
380     }
381     else if (isCTRL_VKEY('V'))
382     {
383         pasteClipBoard();
384     }
385     else if (isCTRL_VKEY('W')) /* kills last word */
386     {
387         killLastWord();
388     }
389     else if (isCTRL_VKEY(VK_TAB) || isCTRL_VKEY(VK_SPACE)) /* Completion */
390     {
391         TermCompletion();
392     }
393     else if (isCTRL_VKEY(VK_LEFT)) /* */
394     {
395         moveBackSingleWord();
396     }
397     else if (isCTRL_VKEY(VK_RIGHT)) /* */
398     {
399         moveForwardSingleWord();
400     }
401     return 0;
402 }
403 /*--------------------------------------------------------------------------*/
404 char *TerminalGetString(const char *prompt)
405 {
406     if (InitTerm)
407     {
408         InitializeTerminal();
409         InitTerm = FALSE;
410     }
411
412     newLine();
413
414     setCurrentPrompt(prompt);
415
416     /* print the prompt */
417     displayPrompt();
418
419     /* initialize history search */
420     setSearchedTokenInScilabHistory(NULL);
421
422     for (;;)
423     {
424         unsigned char cur_char = TerminalGetchar();
425
426         if (cur_char <= 0)
427         {
428             return NULL;
429         }
430
431         /* http://bugzilla.scilab.org/show_bug.cgi?id=1052 */
432         if (ismenu () == 1)
433         {
434             /* Abort current line */
435             return NULL;
436         }
437
438         if ( (cur_char == CR_1) || (cur_char == CR_2) )
439         {
440             if ( isHistorySearch() )
441             {
442                 putLineSearchedHistory();
443             }
444             else
445             {
446                 char *line = getCurrentLine();
447                 TerminalPutc('\n');
448                 appendLineToScilabHistory(line);
449                 return line;
450             }
451         }
452         else
453         {
454             TerminalPutc(cur_char);
455             addCharacterCurrentLine(cur_char);
456         }
457     }
458     return NULL;
459 }
460 /*--------------------------------------------------------------------------*/
461 static BOOL isCTRLPressed(DWORD StateKey)
462 {
463     return ((StateKey & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) != 0);
464 }
465 /*--------------------------------------------------------------------------*/
466 static BOOL isALTPressed(DWORD StateKey)
467 {
468     return ((StateKey & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) != 0);
469 }
470 /*--------------------------------------------------------------------------*/
471 static BOOL isExtendedPressed(DWORD StateKey)
472 {
473     return ((StateKey & (ENHANCED_KEY)) != 0);
474 }
475 /*--------------------------------------------------------------------------*/
476 static BOOL isCTRL_VKEY(int VKEY)
477 {
478     return ( GetKeyState(VKEY) & 0x80 );
479 }
480 /*--------------------------------------------------------------------------*/