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