1 /***************************************************************************
2                          terminfo.cpp  -  get information about the terminal
3                              -------------------
4     begin                : July 22 2002
5     copyright            : (C) 2002 by Marc Schellens
6     email                : m_schellens@users.sf.net
7 ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 /*
18   #ifdef HAVE_CONFIG_H
19   #include <config.h>
20   #endif
21 */
22 
23 #include "includefirst.hpp"
24 #include "stdio.h"
25 #include <iostream>
26 
27 #include "terminfo.hpp"
28 
29 #ifndef _WIN32
30 #include <termios.h>
31 #include <unistd.h>
32 #endif
33 
34 #if defined(HAVE_LIBREADLINE)
35 #include <readline/readline.h>
36 #endif
37 
38 /* AC 2020 mai : it is useful ?!
39 // used to defined GDL_TMPDIR: may have trouble on MSwin, help welcome
40 #ifndef _WIN32
41 #include <paths.h>
42 #endif
43 */
44 
45 #if defined(_WIN32) && !defined(__CYGWIN__)
TermWidth()46 int TermWidth()
47 {
48   HANDLE consh = GetStdHandle(STD_OUTPUT_HANDLE);
49 
50   CONSOLE_SCREEN_BUFFER_INFO bufinfo;
51   GetConsoleScreenBufferInfo(consh, &bufinfo);
52   return bufinfo.srWindow.Right + 1;
53 }
TermHeight()54 int TermHeight()
55 {
56   HANDLE consh = GetStdHandle(STD_OUTPUT_HANDLE);
57 
58   CONSOLE_SCREEN_BUFFER_INFO bufinfo;
59   GetConsoleScreenBufferInfo(consh, &bufinfo);
60   return bufinfo.srWindow.Bottom + 1;
61 }
62 
63 #elif defined(HAVE_LIBREADLINE) && defined(RL_GET_SCREEN_SIZE)
64 
TermWidth()65 int TermWidth()
66 {
67   int cols;
68   int rows;
69   rl_get_screen_size(&rows, &cols);
70   return cols;
71 }
72 
TermHeight()73 int TermHeight()
74 {
75   int cols;
76   int rows;
77   rl_get_screen_size(&rows, &cols);
78   return rows;
79 }
80 
81 #elif defined(HAVE_LIBNCURSES) || defined(HAVE_LIBCURSES)
82 
83 #ifdef HAVE_LIBNCURSES
84 #include <ncurses.h>
85 #elif defined(HAVE_LIBCURSES)
86 #include <curses.h>
87 #endif
88 
TermWidth()89 int TermWidth()
90 {
91   int cols = 0;
92   SCREEN *screen;
93 
94   if( cols != 0) return cols;
95 
96   screen = newterm((char *) NULL, stdout, stdin);
97   if((void *)screen == NULL)
98     cols = 80;
99   else
100     cols = COLS;
101 
102   endwin();
103 
104   return cols;
105 }
106 
TermHeight()107 int TermHeight()
108 {
109   int lines = 0;
110   SCREEN *screen;
111 
112   if( lines != 0) return lines;
113 
114   // original line follows:
115   // initscr();
116 
117   screen = newterm((char *) NULL, stdout, stdin);
118   if((void *)screen == NULL)
119     lines = 24;
120   else
121     lines = LINES;
122 
123   endwin();
124 
125   return lines;
126 }
127 
128 #else
129 
130 // default
131 
TermWidth()132 int TermWidth()
133 {
134   return 80;
135 }
136 
TermHeight()137 int TermHeight()
138 {
139   return 24;
140 }
141 
142 #endif
143 
144 // AC 2020-05-05 : <<found on the Internet>> Unclear for me :((
145 #if defined(HAVE_LIBREADLINE) && defined(RL_GET_SCREEN_SIZE)
146 
SetTermSize(int rows,int cols)147 void SetTermSize(int rows, int cols)
148 {
149   rl_set_screen_size (rows, cols);
150 #if defined(RL_ISSTATE) && defined(RL_INITIALIZED)
151   if (RL_ISSTATE(RL_INITIALIZED)) {
152     rl_resize_terminal();
153   } else {
154     std::cout << "Please report" << std::endl;
155   }
156 #else
157   std::cout << "Not ready due to RL_ISSTATE/RL_INITIALIZED (please report)" << std::endl;
158 #endif
159 }
160 
161 #endif
162 
163 namespace lib {
164   using namespace std;
165 
166 #ifdef _WIN32
get_kbrd(EnvT * e)167   BaseGDL* get_kbrd( EnvT* e)
168   {
169 
170     SizeT nParam=e->NParam();
171 
172     bool doWait = true;
173     if( nParam > 0) {
174       doWait = false;
175       DLong waitArg = 0;
176       e->AssureLongScalarPar( 0, waitArg);
177       if( waitArg != 0)
178 	doWait = true;
179     }
180 
181     char c='\0';
182 
183     if (doWait) {
184       cin.get(c);
185     }
186     else {
187       c=std::fgetc(stdin);
188       if(c==EOF) c='\0';
189     }
190 
191     DStringGDL* res = new DStringGDL( DString( i2s( c)));
192     return res;
193   }
194 
195 #else
196   // get_kbrd patch
197   // http://sourceforge.net/forum/forum.php?thread_id=3292183&forum_id=338691
get_kbrd(EnvT * e)198   BaseGDL* get_kbrd( EnvT* e)
199   {
200 #if defined(HAVE_LIBREADLINE)
201     rl_prep_terminal (0);
202 #endif
203 
204     SizeT nParam=e->NParam();
205 
206     bool doWait = true;
207     if (nParam > 0)
208       {
209 	doWait = false;
210 	DLong waitArg = 0;
211 	e->AssureLongScalarPar( 0, waitArg);
212 	if (waitArg != 0)
213 	  {
214 	    doWait = true;
215 	  }
216       }
217 
218     // https://sourceforge.net/forum/forum.php?thread_id=3292183&forum_id=338691
219     // DONE: Implement proper SCALAR parameter handling (doWait variable)
220     // which is/was not blocking in the original program.
221     // note: multi-byte input is not supported here.
222 
223     char c='\0'; //initialize is never a bad idea...
224 
225     int fd=fileno(stdin);
226 
227     struct termios orig, get;
228 
229     // Get terminal setup to revert to it at end.
230 
231     (void)tcgetattr(fd, &orig);
232     // New terminal setup, non-canonical.
233     get.c_lflag = ISIG;
234 
235     if (doWait)
236       {
237 	// will wait for a character
238 	get.c_cc[VTIME]=0;
239 	get.c_cc[VMIN]=1;
240 	(void)tcsetattr(fd, TCSANOW, &get);
241 
242 	cin.get(c);
243       }
244     else
245       {
246 	// will not wait, but return EOF or next character in terminal buffer if present
247 
248 	get.c_cc[VTIME]=0;
249 	get.c_cc[VMIN]=0;
250 	(void)tcsetattr(fd, TCSANOW, &get);
251 
252 	//the trick is *not to use C++ functions here. cin.get would wait.*
253 	c=std::fgetc(stdin);
254 	//and to convert EOF to null (otherwise GDL may exit if not compiled with
255 	//[lib][n]curses)
256 	if(c==EOF) c='\0';
257       }
258 
259     // Restore original terminal settings.
260 
261     (void)tcsetattr(fd, TCSANOW, &orig);
262 
263 #if defined(HAVE_LIBREADLINE)
264     rl_deprep_terminal ();
265 #endif
266 
267     DStringGDL* res = new DStringGDL( DString( i2s( c)));
268     return res;
269   }
270 #endif
271 
terminal_size_fun(EnvT * e)272   BaseGDL* terminal_size_fun( EnvT* e ) {
273 
274     SizeT nParam = e->NParam();
275     //    cout << nParam << endl;
276 
277     // Just returning the size of the Terminal
278     if (nParam == 0) {
279       DLongGDL* ret = new DLongGDL(dimension(2));
280       (*ret)[0] = TermWidth();
281       (*ret)[1] = TermHeight();
282       return ret;
283     }
284 
285     DLong nb_lines = -1, nb_cols = -1;
286 
287     if (nParam == 1) {
288       e->AssureLongScalarPar( 0, nb_cols);
289     }
290     if (nParam == 2) {
291       e->AssureLongScalarPar( 0, nb_cols);
292       e->AssureLongScalarPar( 1, nb_lines);
293     }
294     if (nb_lines <= 0) nb_lines = TermHeight();
295     if (nb_cols <= 0) nb_cols = TermWidth();
296 
297     //    cout << nb_lines << " "<< nb_cols << endl;
298 
299 #if defined(HAVE_LIBREADLINE) && defined(RL_GET_SCREEN_SIZE)
300     SetTermSize(nb_lines, nb_cols);
301 #else
302     Message("Setting Terminal Size not ready (OK only with recent Readline (5.2+))");
303 #endif
304 
305     // reading again the new size
306     DLongGDL* ret = new DLongGDL( dimension(2) );
307     (*ret)[0] = TermWidth();
308     (*ret)[1] = TermHeight();
309     return ret;
310   }
311 
312 } // namespace
313 
314