1 /*
2  * Tlf - contest logging program for amateur radio operators
3  * Copyright (C) 2015 Thomas Beierlein <tb@forth-ev.de>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 /* User Interface helpers for ncurses based user interface */
21 
22 
23 #include <pthread.h>
24 #include <unistd.h>
25 #include <glib.h>
26 
27 #include "clear_display.h"
28 #include "clusterinfo.h"
29 #include "keystroke_names.h"
30 #include "stoptx.h"
31 #include "tlf_panel.h"
32 #include "startmsg.h"
33 #include "splitscreen.h"
34 
35 
36 extern int use_rxvt;
37 extern int ymax, xmax;
38 
39 int key_kNXT3 = 0;
40 int key_kPRV3 = 0;
41 int key_kNXT5 = 0;
42 int key_kPRV5 = 0;
43 
44 pthread_mutex_t panel_mutex = PTHREAD_MUTEX_INITIALIZER;
45 
46 static int getkey(int wait);
47 static int onechar(void);
48 void resize_layout(void);
49 
50 /** fake refresh code to use update logic for panels */
refreshp()51 void refreshp() {
52     pthread_mutex_lock(&panel_mutex);
53     update_panels();
54     doupdate();
55     pthread_mutex_unlock(&panel_mutex);
56 }
57 
58 
59 /** add A_BOLD to attributes if 'use_rxvt' is not set */
modify_attr(int attr)60 int modify_attr(int attr) {
61 
62     if (use_rxvt == 0)
63 	attr |= A_BOLD;
64 
65     return attr;
66 }
67 
68 
69 /** lookup key code by capability name
70  *
71  * ncurses automatically maps extended capabilities (such as kNXT3 or similar)
72  * to keycodes. But there is no fixed ordering for that.
73  * So we look up the key code by its name on strtup and use that afterwards.
74  * \param capability  - capability name
75  * \return              keycode or 0 if no associated key found
76  */
lookup_key(char * capability)77 int lookup_key(char *capability) {
78     char *esc_sequence = NULL;
79     int keycode = 0;
80 
81     if (*capability == '\0') {
82 	return 0;
83     }
84 
85     esc_sequence = tigetstr(capability);
86 
87     if (esc_sequence == NULL || esc_sequence == (char *) - 1) {
88 	return 0;
89     }
90 
91     keycode = key_defined(esc_sequence);
92 
93     return keycode;
94 }
95 
96 /** lookup all needed special keys not defined by ncurses */
lookup_keys()97 void lookup_keys() {
98     int not_ok = 0;
99 
100     key_kNXT3 = lookup_key("kNXT3");
101     key_kPRV3 = lookup_key("kPRV3");
102     key_kNXT5 = lookup_key("kNXT5");
103     key_kPRV5 = lookup_key("kPRV5");
104 
105     if ((key_kNXT3 || key_kPRV3) == 0) {
106 	showmsg("Terminal does not support Alt-PgUp/PgDn keys");
107 	not_ok = 1;
108     }
109 
110     if ((key_kNXT5 || key_kPRV5) == 0) {
111 	showmsg("Terminal does not support Ctrl-PgUp/PgDn keys");
112 	not_ok = 1;
113     }
114 
115     if (not_ok == 1) {
116 	showmsg("See ':CQD' in man page for setting Auto_CQ delay");
117 	showmsg("");
118 	beep();
119 	sleep(2);
120     }
121 }
122 
123 /** key_get  wait for next key from terminal
124  *
125  */
key_get()126 int key_get() {
127     return getkey(1);
128 }
129 
130 /** key_poll return next key from terminal if there is one
131  *
132  */
key_poll()133 int key_poll() {
134     return getkey(0);
135 }
136 
137 
138 /* helper function to set 'nodelay' mode according to 'wait'
139  * parameter and then ask for the next character
140  * leaves 'nodelay' afterwards always as FALSE (meaning: wait for
141  * character
142  */
getkey(int wait)143 static int getkey(int wait) {
144     int x = 0;
145 
146     nodelay(stdscr, wait ? FALSE : TRUE);
147 
148     x = onechar();
149 
150     if (x == KEY_RESIZE) {
151 	resize_layout();
152     }
153 
154     nodelay(stdscr, FALSE);
155 
156     return x;
157 }
158 
159 
160 /* New onechar() that takes advantage of Ncurses keypad mode and processes
161  * certain escaped keys and assigns them to Ncurses values known by
162  * keyname().  Also catches Escape and processes it immediately as well
163  * as calling stoptx() for minimal delay.
164  */
onechar(void)165 static int onechar(void) {
166     int x = 0;
167     int trash = 0;
168 
169     x = getch();
170 
171     /* Replace Ctl-H (Backspace) and Delete with KEY_BACKSPACE */
172     if (x == CTRL_H || x == DELETE)
173 	x = KEY_BACKSPACE;
174 
175     if (x == ESCAPE) {
176 	nodelay(stdscr, TRUE);
177 
178 	x = getch();
179 
180 	/* Escape pressed, not an escaped key. */
181 	if (x == ERR) {
182 	    stoptx();
183 
184 	    return x = ESCAPE;
185 
186 	} else if (x != 91) {
187 
188 	    switch (x) {
189 
190 		case 32 ... 57:   // Alt-Space to Alt-9,   160 - 185
191 		case 97 ... 122:  // Alt-a to alt-z,       225 - 250
192 		    x += 128;
193 		    break;
194 
195 		/* Not all terminals support Ctl-Shift-ch so
196 		 * treat them as Alt-ch
197 		 */
198 		case 65 ... 78:   //   alt-A to alt-N,     225 - 238
199 		case 80 ... 90:   //   alt-P to alt-Z,     240 - 250
200 		    x += 160;
201 		    break;
202 
203 		case 79: {
204 		    x = getch();
205 
206 		    /* Catch Alt-O */
207 		    if (x == ERR) {
208 			x = 239;
209 			break;
210 		    }
211 
212 		    /* Key codes for Shift-F1 to Shift-F4 in Xfce terminal. */
213 		    if (x == 49) {
214 			x = getch();
215 
216 			if (x == 59) {
217 			    x = getch();
218 			    if (x == 50) {
219 				x = getch();
220 
221 				switch (x) {
222 
223 				    case 80: {
224 					x = KEY_F(13);
225 					break;
226 				    }
227 
228 				    case 81: {
229 					x = KEY_F(14);
230 					break;
231 				    }
232 
233 				    case 82: {
234 					x = KEY_F(15);
235 					break;
236 				    }
237 
238 				    case 83: {
239 					x = KEY_F(16);
240 					break;
241 				    }
242 				}
243 			    }
244 			}
245 		    }
246 		}
247 	    }
248 
249 	    nodelay(stdscr, FALSE);
250 
251 	} else {
252 	    nodelay(stdscr, FALSE);
253 
254 	    x = getch();        /* Get next code after 91 */
255 
256 	    switch (x) {
257 
258 		/* Key codes for this section:
259 		 * 27 91 49 126 Home
260 		 * 27 91 52 126 End
261 		 */
262 		case 49: {
263 		    x = getch();
264 
265 		    if (x == 126) {
266 			x = KEY_HOME;
267 			break;
268 		    }
269 		}
270 
271 		case 52: {
272 		    x = KEY_END;
273 		    trash = getch();
274 		    break;
275 		}
276 	    }
277 	}
278     }
279 
280     /* It seems Xterm treats Alt-Space through Alt-9 with a prefix
281      * character of 194 followed by 160 through 185.
282      */
283     if (x == 194) {
284 	nodelay(stdscr, TRUE);
285 
286 	trash = getch();
287 
288 	if (trash == ERR)
289 	    return x;
290 
291 	x = trash;
292 
293 	// Alt-Space to Alt-9
294 	if (x >= 160 && x <= 185) {
295 	    nodelay(stdscr, FALSE);
296 
297 	    return x;
298 	}
299     }
300 
301     /* It seems Xterm treats Alt-a to Alt-z with a prefix
302      * character of 195 followed by 161-186 (a-z) or
303      * 129-154 (A-Z).
304      */
305     if (x == 195) {
306 	nodelay(stdscr, TRUE);
307 
308 	trash = getch();
309 
310 	if (trash == ERR)
311 	    return x;
312 
313 	x = trash;
314 
315 	switch (x) {
316 
317 	    case 161 ... 186:  // Alt-a to Alt-z  225 - 250
318 		x += 64;
319 		break;
320 
321 	    case 129 ... 154:  // Alt-A to Alt-Z  225 - 250
322 		x += 96;
323 		break;
324 	}
325 
326 	nodelay(stdscr, FALSE);
327     }
328 
329     return x;
330 }
331 
332 
333 /* Handles resizing of windows
334  * As ncurses handles most of it itself, it just redraws the main
335  * display and resizes only the packet panel windows
336  */
resize_layout(void)337 void resize_layout(void) {
338     getmaxyx(stdscr, ymax, xmax);
339     clear_display();
340     clusterinfo();
341     refresh_splitlayout();
342 }
343 
344 #define MAX_SPACES  1000
345 
346 static char *spaces_buffer = NULL;
347 
spaces(int n)348 const char *spaces(int n) {
349 
350     if (spaces_buffer == NULL) {
351 	spaces_buffer = g_strnfill(MAX_SPACES, ' ');
352     }
353 
354     if (n < 0) {
355 	n = 0;
356     } else if (n > MAX_SPACES) {
357 	n = MAX_SPACES;
358     }
359 
360     return spaces_buffer + (MAX_SPACES - n);
361 }
362