1 /* TN5250
2  * Copyright (C) 1997-2008 Michael Madore
3  *
4  * This file is part of TN5250.
5  *
6  * TN5250 is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1, or (at your option)
9  * any later version.
10  *
11  * TN5250 is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this software; see the file COPYING.  If not, write to
18  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19  * Boston, MA 02111-1307 USA
20  *
21  */
22 #define _TN5250_TERMINAL_PRIVATE_DEFINED
23 #include "tn5250-private.h"
24 #include "cursesterm.h"
25 
26 #ifdef USE_CURSES
27 
28 /* Some versions of ncurses don't have this defined. */
29 #ifndef A_VERTICAL
30 #define A_VERTICAL ((1UL) << ((22) + 8))
31 #endif				/* A_VERTICAL */
32 
33 /* Some older versions of ncurses don't define NCURSES_COLOR_T */
34 #ifndef NCURSES_COLOR_T
35 #define NCURSES_COLOR_T short
36 #endif
37 
38 /* Mapping of 5250 colors to curses colors */
39 struct _curses_color_map {
40    char *name;
41    NCURSES_COLOR_T ref;
42    attr_t bld;
43 };
44 typedef struct _curses_color_map curses_color_map;
45 
46 static curses_color_map colorlist[] =
47 {
48   { "black",     COLOR_BLACK         },
49   { "red",       COLOR_RED,   A_BOLD },
50   { "green",     COLOR_GREEN         },
51   { "yellow",    COLOR_YELLOW,A_BOLD },
52   { "blue",      COLOR_CYAN,  A_BOLD },
53   { "pink",      COLOR_MAGENTA       },
54   { "turquoise", COLOR_CYAN          },
55   { "white",     COLOR_WHITE, A_BOLD },
56   { NULL, -1 }
57 };
58 
59 #define A_5250_GREEN    ((attr_t)COLOR_PAIR(COLOR_GREEN)|colorlist[COLOR_GREEN].bld)
60 #define A_5250_WHITE    ((attr_t)COLOR_PAIR(COLOR_WHITE)|colorlist[COLOR_WHITE].bld)
61 #define A_5250_RED      ((attr_t)COLOR_PAIR(COLOR_RED)|colorlist[COLOR_RED].bld)
62 #define A_5250_TURQ     ((attr_t)COLOR_PAIR(COLOR_CYAN)|colorlist[COLOR_CYAN].bld)
63 #define A_5250_YELLOW   ((attr_t)COLOR_PAIR(COLOR_YELLOW)|colorlist[COLOR_YELLOW].bld)
64 #define A_5250_PINK     ((attr_t)COLOR_PAIR(COLOR_MAGENTA)|colorlist[COLOR_MAGENTA].bld)
65 #define A_5250_BLUE     ((attr_t)COLOR_PAIR(COLOR_BLUE)|colorlist[COLOR_BLUE].bld)
66 
67 /*@-globstate -nullpass@*/  /* lclint incorrectly assumes stdscr may be NULL */
68 
69 static attr_t attribute_map[33];
70 
71 static void curses_terminal_init(Tn5250Terminal * This) /*@modifies This@*/;
72 static void curses_terminal_term(Tn5250Terminal * This) /*@modifies This@*/;
73 static void curses_terminal_destroy(Tn5250Terminal /*@only@*/ * This);
74 static int curses_terminal_width(Tn5250Terminal * This);
75 static int curses_terminal_height(Tn5250Terminal * This);
76 static int curses_terminal_flags(Tn5250Terminal * This);
77 static void curses_terminal_update(Tn5250Terminal * This,
78 				   Tn5250Display * display) /*@modifies This@*/;
79 static void curses_terminal_update_indicators(Tn5250Terminal * This,
80 					      Tn5250Display * display) /*@modifies This@*/;
81 static int curses_terminal_waitevent(Tn5250Terminal * This) /*@modifies This@*/;
82 static int curses_terminal_getkey(Tn5250Terminal * This) /*@modifies This@*/;
83 static int curses_terminal_get_esc_key(Tn5250Terminal * This, int is_esc) /*@modifies This@*/;
84 static void curses_terminal_beep(Tn5250Terminal * This);
85 static int curses_terminal_enhanced (Tn5250Terminal * This);
86 static int curses_terminal_is_ruler(Tn5250Terminal *This, Tn5250Display *display, int x, int y);
87 int curses_rgb_to_color(int r, int g, int b, int *rclr, int *rbold);
88 int curses_terminal_config(Tn5250Terminal *This, Tn5250Config *config);
89 void curses_terminal_print_screen(Tn5250Terminal *This, Tn5250Display *display);
90 void curses_postscript_print(FILE *out, int x, int y, char *string, attr_t attr);
91 
92 #ifdef USE_OWN_KEY_PARSING
93 struct _Key {
94    int	       k_code;
95    char        k_str[10];
96 };
97 
98 typedef struct _Key Key;
99 #endif
100 
101 #define MAX_K_BUF_LEN 20
102 
103 struct _Tn5250TerminalPrivate {
104    int		  last_width, last_height;
105 #ifdef USE_OWN_KEY_PARSING
106    unsigned char  k_buf[MAX_K_BUF_LEN];
107    int		  k_buf_len;
108 
109    Key *	  k_map;
110    int		  k_map_len;
111 #endif
112    char *         font_80;
113    char *         font_132;
114    Tn5250Display  *display;
115    Tn5250Config   *config;
116    int		  quit_flag : 1;
117    int		  have_underscores : 1;
118    int		  underscores : 1;
119    int		  is_xterm : 1;
120    int		  display_ruler : 1;
121    int            local_print : 1;
122 };
123 
124 #ifdef USE_OWN_KEY_PARSING
125 /* This is an array mapping our key code to a termcap capability
126  * name. */
127 static Key curses_caps[] = {
128    { K_ENTER, "@8" },
129    { K_ENTER, "cr" },
130    { K_BACKTAB, "kB" },
131    { K_F1, "k1" },
132    { K_F2, "k2" },
133    { K_F3, "k3" },
134    { K_F4, "k4" },
135    { K_F5, "k5" },
136    { K_F6, "k6" },
137    { K_F7, "k7" },
138    { K_F8, "k8" },
139    { K_F9, "k9" },
140    { K_F10, "k;" },
141    { K_F11, "F1" },
142    { K_F12, "F2" },
143    { K_F13, "F3" },
144    { K_F14, "F4" },
145    { K_F15, "F5" },
146    { K_F16, "F6" },
147    { K_F17, "F7" },
148    { K_F18, "F8" },
149    { K_F19, "F9" },
150    { K_F20, "FA" },
151    { K_F21, "FB" },
152    { K_F22, "FC" },
153    { K_F23, "FD" },
154    { K_F24, "FE" },
155    { K_LEFT, "kl" },
156    { K_RIGHT, "kr" },
157    { K_UP, "ku" },
158    { K_DOWN, "kd" },
159    { K_ROLLDN, "kP" },
160    { K_ROLLUP, "kN" },
161    { K_BACKSPACE, "kb" },
162    { K_HOME, "kh" },
163    { K_END, "@7" },
164    { K_INSERT, "kI" },
165    { K_DELETE, "kD" },
166    { K_PRINT, "%9" },
167    { K_HELP, "%1" },
168    { K_CLEAR, "kC" },
169    { K_REFRESH, "&2" },
170    { K_FIELDEXIT, "@9" },
171 };
172 
173 /* This is an array mapping some of our vt100 sequences to our internal
174  * key names. */
175 static Key curses_vt100[] = {
176    /* CTRL strings */
177    { K_ATTENTION,	"\001" }, /* CTRL A */
178    { K_ROLLDN,		"\002" }, /* CTRL B */
179    { K_SYSREQ,		"\003" }, /* CTRL C */
180    { K_ROLLUP,		"\004" }, /* CTRL D */
181    { K_ERASE,           "\005" }, /* CTRL E */
182    { K_ROLLUP,		"\006" }, /* CTRL F */
183    { K_FIELDEXIT,       "\013" }, /* CTRL K */
184    { K_REFRESH,         "\014" }, /* CTRL L */
185    { K_HOME,            "\017" }, /* CTRL O */
186    { K_PRINT,           "\020" }, /* CTRL P */
187    { K_RESET,           "\022" }, /* CTRL R */
188    { K_MEMO,            "\023" }, /* CTRL S */
189    { K_TESTREQ,         "\024" }, /* CTRL T */
190    { K_ROLLDN,          "\025" }, /* CTRL U */
191    { K_EXEC,            "\027" }, /* CTRL W */
192    { K_FIELDPLUS,       "\030" }, /* CTRL X */
193 
194    /* ASCII DEL is not correctly reported as the DC key in some
195     * termcaps */
196    /* But it is backspace in some termcaps... */
197    /* { K_DELETE,		"\177" }, */ /* ASCII DEL */
198    { K_DELETE,		"\033\133\063\176" }, /* ASCII DEL Sequence: \E[3~ */
199 
200 
201    /* ESC strings */
202    { K_F1,		"\033\061" }, /* ESC 1 */
203    { K_F2,		"\033\062" }, /* ESC 2 */
204    { K_F3,		"\033\063" }, /* ESC 3 */
205    { K_F4,		"\033\064" }, /* ESC 4 */
206    { K_F5,		"\033\065" }, /* ESC 5 */
207    { K_F6,		"\033\066" }, /* ESC 6 */
208    { K_F7,		"\033\067" }, /* ESC 7 */
209    { K_F8,		"\033\070" }, /* ESC 8 */
210    { K_F9,		"\033\071" }, /* ESC 9 */
211    { K_F10,		"\033\060" }, /* ESC 0 */
212    { K_F11,		"\033\055" }, /* ESC - */
213    { K_F12,		"\033\075" }, /* ESC = */
214    { K_F13,		"\033\041" }, /* ESC ! */
215    { K_F14,		"\033\100" }, /* ESC @ */
216    { K_F15,		"\033\043" }, /* ESC # */
217    { K_F16,		"\033\044" }, /* ESC $ */
218    { K_F17,		"\033\045" }, /* ESC % */
219    { K_F18,		"\033\136" }, /* ESC ^ */
220    { K_F19,		"\033\046" }, /* ESC & */
221    { K_F20,		"\033\052" }, /* ESC * */
222    { K_F21,		"\033\050" }, /* ESC ( */
223    { K_F22,		"\033\051" }, /* ESC ) */
224    { K_F23,		"\033\137" }, /* ESC _ */
225    { K_F24,		"\033\053" }, /* ESC + */
226    { K_ATTENTION,	"\033\101" }, /* ESC A */
227    { K_CLEAR,		"\033\103" }, /* ESC C */
228    { K_DUPLICATE,	"\033\104" }, /* ESC D */
229    { K_HELP,		"\033\110" }, /* ESC H */
230    { K_INSERT,		"\033\111" }, /* ESC I */
231    { K_REFRESH,		"\033\114" }, /* ESC L */
232    { K_FIELDMINUS,	"\033\115" }, /* ESC M */
233    { K_NEWLINE,		"\033\116" }, /* ESC N */ /* Our extension */
234    { K_PRINT,		"\033\120" }, /* ESC P */
235    { K_RESET,		"\033\122" }, /* ESC R */
236    { K_SYSREQ,		"\033\123" }, /* ESC S */
237    { K_TOGGLE,		"\033\124" }, /* ESC T */
238    { K_FIELDEXIT,	"\033\130" }, /* ESC X */
239    { K_NEWLINE,         "\033\012" }, /* ESC ^J */
240    { K_NEWLINE,         "\033\015" }, /* ESC ^M */
241    { K_INSERT,		"\033\177" }, /* ESC DEL */
242    { K_NEXTFLD,		"\033\025" }, /* ESC Ctrl-U */
243    { K_PREVFLD,		"\033\010" }, /* ESC Ctrl-H */
244    { K_FIELDHOME,	"\033\006" }, /* ESC Ctrl-F */
245    /* K_INSERT = ESC+DEL handled in code below. */
246 };
247 #endif
248 
249 /****f* lib5250/tn5250_curses_terminal_new
250  * NAME
251  *    tn5250_curses_terminal_new
252  * SYNOPSIS
253  *    ret = tn5250_curses_terminal_new ();
254  * INPUTS
255  *    None
256  * DESCRIPTION
257  *    Create a new curses terminal object.
258  *****/
tn5250_curses_terminal_new()259 Tn5250Terminal *tn5250_curses_terminal_new()
260 {
261    Tn5250Terminal *r = tn5250_new(Tn5250Terminal, 1);
262    if (r == NULL)
263       return NULL;
264 
265    r->data = tn5250_new(struct _Tn5250TerminalPrivate, 1);
266    if (r->data == NULL) {
267       free(r);
268       return NULL;
269    }
270 
271    r->data->have_underscores = 0;
272    r->data->underscores = 0;
273    r->data->quit_flag = 0;
274    r->data->last_width = 0;
275    r->data->last_height = 0;
276    r->data->is_xterm = 0;
277    r->data->font_80 = NULL;
278    r->data->font_132 = NULL;
279    r->data->display_ruler = 0;
280    r->data->local_print = 0;
281    r->data->display = NULL;
282    r->data->config = NULL;
283 
284 #ifdef USE_OWN_KEY_PARSING
285    r->data->k_buf_len = 0;
286    r->data->k_map_len = 0;
287    r->data->k_map = NULL;
288 #endif
289 
290    r->conn_fd = -1;
291    r->init = curses_terminal_init;
292    r->term = curses_terminal_term;
293    r->destroy = curses_terminal_destroy;
294    r->width = curses_terminal_width;
295    r->height = curses_terminal_height;
296    r->flags = curses_terminal_flags;
297    r->update = curses_terminal_update;
298    r->update_indicators = curses_terminal_update_indicators;
299    r->waitevent = curses_terminal_waitevent;
300    r->getkey = curses_terminal_getkey;
301    r->putkey = NULL;
302    r->beep = curses_terminal_beep;
303    r->enhanced = curses_terminal_enhanced;
304    r->config = curses_terminal_config;
305    return r;
306 }
307 
308 /****i* lib5250/curses_terminal_init
309  * NAME
310  *    curses_terminal_init
311  * SYNOPSIS
312  *    curses_terminal_init (This);
313  * INPUTS
314  *    Tn5250Terminal *     This       -
315  * DESCRIPTION
316  *    DOCUMENT ME!!!
317  *****/
curses_terminal_init(Tn5250Terminal * This)318 static void curses_terminal_init(Tn5250Terminal * This)
319 {
320    int i = 0, c, s;
321    char *str;
322    int x;
323 
324    (void)initscr();
325    raw();
326 
327 #ifdef USE_OWN_KEY_PARSING
328    if ((str = (unsigned char *)tgetstr ("ks", NULL)) != NULL)
329       tputs (str, 1, putchar);
330    fflush (stdout);
331 #else
332    keypad(stdscr, 1);
333 #endif
334 
335    nodelay(stdscr, 1);
336    noecho();
337 
338    /* Determine if we're talking to an xterm ;) */
339    if ((str = getenv("TERM")) != NULL &&
340 	 (!strcmp (str, "xterm") || !strcmp (str, "xterm-5250")
341 	  || !strcmp (str, "xterm-color")))
342       This->data->is_xterm = 1;
343 
344    /* Initialize colors if the terminal supports it. */
345    if (has_colors()) {
346       start_color();
347       init_pair(COLOR_BLACK, colorlist[COLOR_BLACK].ref,
348                              colorlist[COLOR_BLACK].ref);
349       init_pair(COLOR_GREEN, colorlist[COLOR_GREEN].ref,
350                              colorlist[COLOR_BLACK].ref);
351       init_pair(COLOR_RED,   colorlist[COLOR_RED  ].ref,
352                              colorlist[COLOR_BLACK].ref);
353       init_pair(COLOR_CYAN,  colorlist[COLOR_CYAN ].ref,
354                              colorlist[COLOR_BLACK].ref);
355       init_pair(COLOR_WHITE, colorlist[COLOR_WHITE].ref,
356                              colorlist[COLOR_BLACK].ref);
357       init_pair(COLOR_MAGENTA,colorlist[COLOR_MAGENTA].ref,
358                              colorlist[COLOR_BLACK].ref);
359       init_pair(COLOR_BLUE  ,colorlist[COLOR_BLUE ].ref,
360                              colorlist[COLOR_BLACK].ref);
361       init_pair(COLOR_YELLOW,colorlist[COLOR_YELLOW].ref,
362                              colorlist[COLOR_BLACK].ref);
363    }
364 
365    x=-1;
366    attribute_map[++x] = A_5250_GREEN;
367    attribute_map[++x] = A_5250_GREEN | A_REVERSE;
368    attribute_map[++x] = A_5250_WHITE;
369    attribute_map[++x] = A_5250_WHITE | A_REVERSE;
370    attribute_map[++x] = A_5250_GREEN | A_UNDERLINE;
371    attribute_map[++x] = A_5250_GREEN | A_UNDERLINE | A_REVERSE;
372    attribute_map[++x] = A_5250_WHITE | A_UNDERLINE;
373    attribute_map[++x] = 0x00;
374    attribute_map[++x] = A_5250_RED;
375    attribute_map[++x] = A_5250_RED | A_REVERSE;
376    attribute_map[++x] = A_5250_RED | A_BLINK;
377    attribute_map[++x] = A_5250_RED | A_BLINK | A_REVERSE;
378    attribute_map[++x] = A_5250_RED | A_UNDERLINE;
379    attribute_map[++x] = A_5250_RED | A_UNDERLINE | A_REVERSE;
380    attribute_map[++x] = A_5250_RED | A_UNDERLINE | A_BLINK;
381    attribute_map[++x] = 0x00;
382    attribute_map[++x] = A_5250_TURQ | A_VERTICAL;
383    attribute_map[++x] = A_5250_TURQ | A_VERTICAL | A_REVERSE;
384    attribute_map[++x] = A_5250_YELLOW | A_VERTICAL;
385    attribute_map[++x] = A_5250_YELLOW | A_VERTICAL | A_REVERSE;
386    attribute_map[++x] = A_5250_TURQ | A_UNDERLINE | A_VERTICAL;
387    attribute_map[++x] = A_5250_TURQ | A_UNDERLINE | A_REVERSE | A_VERTICAL;
388    attribute_map[++x] = A_5250_YELLOW | A_UNDERLINE | A_VERTICAL;
389    attribute_map[++x] = 0x00;
390    attribute_map[++x] = A_5250_PINK;
391    attribute_map[++x] = A_5250_PINK | A_REVERSE;
392    attribute_map[++x] = A_5250_BLUE;
393    attribute_map[++x] = A_5250_BLUE | A_REVERSE;
394    attribute_map[++x] = A_5250_PINK | A_UNDERLINE;
395    attribute_map[++x] = A_5250_PINK | A_UNDERLINE | A_REVERSE;
396    attribute_map[++x] = A_5250_BLUE | A_UNDERLINE;
397    attribute_map[++x] = 0x00;
398 
399    This->data->quit_flag = 0;
400 
401    /* Determine if the terminal supports underlining. */
402    if (This->data->have_underscores == 0) {
403       if ((unsigned char *)tgetstr ("us", NULL) == NULL)
404 	 This->data->underscores = 1;
405       else
406 	 This->data->underscores = 0;
407    }
408 
409 #ifdef USE_OWN_KEY_PARSING
410    /* Allocate and populate an array of escape code => key code
411     * mappings. */
412    This->data->k_map_len = (sizeof (curses_vt100) / sizeof (Key)) * 2
413       + sizeof (curses_caps) / sizeof (Key) + 1;
414    This->data->k_map = (Key*)malloc (sizeof (Key)*This->data->k_map_len);
415 
416    c = sizeof (curses_caps) / sizeof (Key);
417    s = sizeof (curses_vt100) / sizeof (Key);
418    for (i = 0; i < c; i++) {
419       This->data->k_map[i].k_code = curses_caps[i].k_code;
420       if ((str = (unsigned char *)tgetstr (curses_caps[i].k_str, NULL)) != NULL) {
421 	 TN5250_LOG(("Found string for cap '%s': '%s'.\n",
422 		  curses_caps[i].k_str, str));
423 	 strcpy (This->data->k_map[i].k_str, str);
424       } else
425 	 This->data->k_map[i].k_str[0] = '\0';
426    }
427 
428    /* Populate vt100 escape codes, both ESC+ and C-g+ forms. */
429    for (i = 0; i < sizeof (curses_vt100) / sizeof (Key); i++) {
430       This->data->k_map[i + c].k_code =
431 	 This->data->k_map[i + c + s].k_code =
432 	 curses_vt100[i].k_code;
433       strcpy (This->data->k_map[i + c].k_str, curses_vt100[i].k_str);
434       strcpy (This->data->k_map[i + c + s].k_str, curses_vt100[i].k_str);
435 
436       if (This->data->k_map[i + c + s].k_str[0] == '\033')
437 	 This->data->k_map[i + c + s].k_str[0] = K_CTRL('G');
438       else
439 	 This->data->k_map[i + c + s].k_str[0] = '\0';
440    }
441 
442    /* Damn the exceptions to the rules. (ESC + DEL) */
443    This->data->k_map[This->data->k_map_len-1].k_code = K_INSERT;
444    This->data->k_map[This->data->k_map_len-s-1].k_code = K_INSERT;
445    if ((str = (unsigned char *)tgetstr ("kD", NULL)) != NULL) {
446       This->data->k_map[This->data->k_map_len-1].k_str[0] = '\033';
447       This->data->k_map[This->data->k_map_len-s-1].k_str[0] = K_CTRL('G');
448       strcpy (This->data->k_map[This->data->k_map_len-1].k_str + 1, str);
449       strcpy (This->data->k_map[This->data->k_map_len-s-1].k_str + 1, str);
450    } else
451       This->data->k_map[This->data->k_map_len-1].k_str[0] =
452 	 This->data->k_map[This->data->k_map_len-s-1].k_str[0] = 0;
453 #endif
454 }
455 
456 /****i* lib5250/tn5250_curses_terminal_use_underscores
457  * NAME
458  *    tn5250_curses_terminal_use_underscores
459  * SYNOPSIS
460  *    tn5250_curses_terminal_use_underscores (This, f);
461  * INPUTS
462  *    Tn5250Terminal  *    This       - The curses terminal object.
463  *    int                  f          - Flag to use underscores
464  * DESCRIPTION
465  *    This function instructs the curses terminal to use underscore
466  *    characters (`_') for blank cells in under-lined fields instead of
467  *    using the curses underline attribute.  This is necessary on terminals
468  *    which don't support the underline attribute.
469  *
470  *    If this is not explicitly set, the curses terminal will determine
471  *    if it should use underscores by checking for the "us" termcap
472  *    capability.  This may not always produce the desired effect.
473  *****/
tn5250_curses_terminal_use_underscores(Tn5250Terminal * This,int u)474 void tn5250_curses_terminal_use_underscores (Tn5250Terminal *This, int u)
475 {
476    This->data->have_underscores = 1;
477    This->data->underscores = u;
478 }
479 
480 /****i* lib5250/tn5250_curses_terminal_display_ruler
481  * NAME
482  *    tn5250_curses_terminal_display_ruler
483  * SYNOPSIS
484  *    tn5250_curses_terminal_display_ruler (This, f);
485  * INPUTS
486  *    Tn5250Terminal  *    This       - The curses terminal object.
487  *    int                  f          - Flag, set to 1 to show ruler
488  * DESCRIPTION
489  *    Call this function to tell a curses terminal to display a
490  *    "ruler" that pinpoints where the cursor is on a given screen.
491  *****/
tn5250_curses_terminal_display_ruler(Tn5250Terminal * This,int f)492 void tn5250_curses_terminal_display_ruler (Tn5250Terminal *This, int f)
493 {
494    This->data->display_ruler = f;
495 }
496 
497 /****i* lib5250/tn5250_curses_terminal_set_xterm_font
498  * NAME
499  *  tn5250_curses_terminal_set_xterm_font
500  * SYNOPSIS
501  *    tn5250_curses_terminal_set_xterm_font (This, font80, font132);
502  * INPUTS
503  *    Tn5250Terminal  *    This       - curses terminal object
504  *    const char  *	   font80     - string to send when using 80 col font
505  *    const char  *	   font132    - string to send when using 132 col font
506  * DESCRIPTION
507  *    When using an xterm, it is sometimes desirable to change fonts when
508  *    switching from 80 to 132 col mode.  If this is not explicitly set,
509  *    no font-change will be sent to the xterm.
510  *
511  *    Font changes consist of "\x1b]50;<font string>\x07".  You only need
512  *    specify the "<font string>" portion as an argument to this function.
513  *****/
tn5250_curses_terminal_set_xterm_font(Tn5250Terminal * This,const char * font80,const char * font132)514 void tn5250_curses_terminal_set_xterm_font (Tn5250Terminal *This,
515                                  const char *font80, const char *font132)
516 {
517    This->data->font_80 = malloc(strlen(font80) + 6);
518    This->data->font_132 = malloc(strlen(font132) + 6);
519    sprintf(This->data->font_80, "\x1b]50;%s\x07", font80);
520    sprintf(This->data->font_132, "\x1b]50;%s\x07", font132);
521    TN5250_LOG(("font_80 = %s.\n",This->data->font_80));
522    TN5250_LOG(("font_132 = %s.\n",This->data->font_132));
523 }
524 
525 /****i* lib5250/curses_terminal_term
526  * NAME
527  *    curses_terminal_term
528  * SYNOPSIS
529  *    curses_terminal_term (This);
530  * INPUTS
531  *    Tn5250Terminal  *    This       -
532  * DESCRIPTION
533  *    DOCUMENT ME!!!
534  *****/
curses_terminal_term(Tn5250Terminal * This)535 static void curses_terminal_term(Tn5250Terminal /*@unused@*/ * This)
536 {
537    endwin();
538 }
539 
540 /****i* lib5250/curses_terminal_destroy
541  * NAME
542  *    curses_terminal_destroy
543  * SYNOPSIS
544  *    curses_terminal_destroy (This);
545  * INPUTS
546  *    Tn5250Terminal *     This       -
547  * DESCRIPTION
548  *    DOCUMENT ME!!!
549  *****/
curses_terminal_destroy(Tn5250Terminal * This)550 static void curses_terminal_destroy(Tn5250Terminal * This)
551 {
552 #ifdef USE_OWN_KEY_PARSING
553    if (This->data->k_map != NULL)
554       free(This->data->k_map);
555 #endif
556    if (This->data->font_80 !=NULL)
557       free(This->data->font_80);
558    if (This->data->font_132 !=NULL)
559       free(This->data->font_132);
560    if (This->data != NULL)
561       free(This->data);
562    free(This);
563 }
564 
565 /****i* lib5250/curses_terminal_width
566  * NAME
567  *    curses_terminal_width
568  * SYNOPSIS
569  *    ret = curses_terminal_width (This);
570  * INPUTS
571  *    Tn5250Terminal  *    This       -
572  * DESCRIPTION
573  *    DOCUMENT ME!!!
574  *****/
curses_terminal_width(Tn5250Terminal * This)575 static int curses_terminal_width(Tn5250Terminal /*@unused@*/ * This)
576 {
577    int y, x;
578    getmaxyx(stdscr, y, x);
579    return x + 1;
580 }
581 
582 /****i* lib5250/curses_terminal_height
583  * NAME
584  *    curses_terminal_height
585  * SYNOPSIS
586  *    ret = curses_terminal_height (This);
587  * INPUTS
588  *    Tn5250Terminal  *    This       -
589  * DESCRIPTION
590  *    DOCUMENT ME!!!
591  *****/
curses_terminal_height(Tn5250Terminal * This)592 static int curses_terminal_height(Tn5250Terminal /*@unused@*/ * This)
593 {
594    int y, x;
595    getmaxyx(stdscr, y, x);
596    return y + 1;
597 }
598 
599 /****i* lib5250/curses_terminal_flags
600  * NAME
601  *    curses_terminal_flags
602  * SYNOPSIS
603  *    ret = curses_terminal_flags (This);
604  * INPUTS
605  *    Tn5250Terminal  *    This       -
606  * DESCRIPTION
607  *    DOCUMENT ME!!!
608  *****/
curses_terminal_flags(Tn5250Terminal * This)609 static int curses_terminal_flags(Tn5250Terminal /*@unused@*/ * This)
610 {
611    int f = 0;
612    if (has_colors() != 0)
613       f |= TN5250_TERMINAL_HAS_COLOR;
614    return f;
615 }
616 
617 /****i* lib5250/curses_terminal_update
618  * NAME
619  *    curses_terminal_update
620  * SYNOPSIS
621  *    curses_terminal_update (This, display);
622  * INPUTS
623  *    Tn5250Terminal *     This       -
624  *    Tn5250Display *      display    -
625  * DESCRIPTION
626  *    DOCUMENT ME!!!
627  *****/
curses_terminal_update(Tn5250Terminal * This,Tn5250Display * display)628 static void curses_terminal_update(Tn5250Terminal * This, Tn5250Display *display)
629 {
630    int my, mx;
631    int y, x;
632    attr_t curs_attr;
633    unsigned char a = 0x20, c;
634 
635    This->data->display = display;
636 
637    if (This->data->last_width != tn5250_display_width(display)
638        || This->data->last_height != tn5250_display_height(display)) {
639       clear();
640       if(This->data->is_xterm) {
641          if (This->data->font_132!=NULL) {
642                if (tn5250_display_width (display)>100)
643                     printf(This->data->font_132);
644                else
645                     printf(This->data->font_80);
646          }
647 	 printf ("\x1b[8;%d;%dt", tn5250_display_height (display)+1,
648 	       tn5250_display_width (display));
649 	 fflush (stdout);
650 #ifdef HAVE_RESIZETERM
651 	 resizeterm(tn5250_display_height(display)+1, tn5250_display_width(display)+1);
652 #endif
653  	 /* Make sure we get a SIGWINCH - We need curses to resize its
654  	  * buffer. */
655  	 raise (SIGWINCH);
656 	 refresh ();
657       }
658       This->data->last_width = tn5250_display_width(display);
659       This->data->last_height = tn5250_display_height(display);
660 
661 /* XXX: this is somewhat of a hack.  For some reason the change to
662       132 col lags a bit, causing our update to fail, so this just waits
663       for the update to finish...  (is there a better way?) */
664 
665       for (x=0; x<10; x++) {
666           refresh ();
667           if (tn5250_display_width(display)==curses_terminal_width(This)-1)
668               break;
669           usleep(10000);
670       }
671    }
672    attrset(A_NORMAL);
673    getmaxyx(stdscr, my, mx);
674    for (y = 0; y < tn5250_display_height(display); y++) {
675       if (y > my)
676 	 break;
677 
678       move(y, 0);
679       for (x = 0; x < tn5250_display_width(display); x++) {
680 	 c = tn5250_display_char_at(display, y, x);
681 	 if ((c & 0xe0) == 0x20) {	/* ATTRIBUTE */
682 	    a = (c & 0xff);
683                if (curses_terminal_is_ruler(This, display, x, y)) {
684                   addch( A_REVERSE | attribute_map[0] | ' ');
685                } else {
686          	  addch(attribute_map[0] | ' ');
687                }
688 	 } else {		/* DATA */
689 	    curs_attr = attribute_map[a - 0x20];
690 	    if (curs_attr == 0x00) {	/* NONDISPLAY */
691                if (curses_terminal_is_ruler(This, display, x, y)) {
692                   addch( A_REVERSE | attribute_map[0] | ' ');
693                } else {
694          	  addch(attribute_map[0] | ' ');
695                }
696 	    } else {
697                /* UNPRINTABLE -- print block */
698                if ((c==0x1f) || (c==0x3F)) {
699 		  c = ' ';
700 		  curs_attr ^= A_REVERSE;
701                }
702                /* UNPRINTABLE -- print blank */
703 	       else if ((c < 0x40 && c > 0x00) || c == 0xff) {
704 		  c = ' ';
705 	       } else {
706 		  c = tn5250_char_map_to_local (tn5250_display_char_map (display), c);
707 	       }
708 	       if ((curs_attr & A_VERTICAL) != 0) {
709 		  curs_attr |= A_UNDERLINE;
710 		  curs_attr &= ~A_VERTICAL;
711 	       }
712 	       /* This is a kludge since vga hardware doesn't support under-
713 	        * lining characters.  It's pretty ugly. */
714 	       if (This->data->underscores) {
715 		  if ((curs_attr & A_UNDERLINE) != 0) {
716 		     curs_attr &= ~A_UNDERLINE;
717 		     if (c == ' ')
718 			c = '_';
719 		  }
720 	       }
721                if (curses_terminal_is_ruler(This, display, x, y)) {
722 		  curs_attr |= A_REVERSE;
723                }
724 	       addch((chtype)(c | curs_attr));
725 	    }
726 	 }			/* if ((c & 0xe0) ... */
727       }				/* for (int x ... */
728    }				/* for (int y ... */
729 
730    move(tn5250_display_cursor_y(display), tn5250_display_cursor_x(display));
731 
732    /* This performs the refresh () */
733    curses_terminal_update_indicators(This, display);
734 }
735 
736 
737 /****i* lib5250/curses_terminal_is_ruler
738  * NAME
739  *    curses_terminal_is_ruler
740  * SYNOPSIS
741  *    curses_terminal_is_ruler (display, x, y);
742  * INPUTS
743  *    Tn5250Terminal *     This       -
744  *    Tn5250Display *      display    -
745  *    int 		   x          - position of char on X axis
746  *    int		   y          - position of char on Y axis
747  * DESCRIPTION
748  *    Returns 1 if a char on the screen should be displayed as part
749  *    of the ruler, or 0 if it should not.
750  *****/
curses_terminal_is_ruler(Tn5250Terminal * This,Tn5250Display * display,int x,int y)751 static int curses_terminal_is_ruler(Tn5250Terminal *This,
752                Tn5250Display *display, int x, int y) {
753 
754    if (!(This->data->display_ruler)) return 0;
755 
756    if (x==tn5250_display_cursor_x(display)) {
757         return 1;
758    }
759    if (y==tn5250_display_cursor_y(display)) {
760         return 1;
761    }
762 
763    return 0;
764 }
765 
766 
767 /****i* lib5250/curses_terminal_update_indicators
768  * NAME
769  *    curses_terminal_update_indicators
770  * SYNOPSIS
771  *    curses_terminal_update_indicators (This, display);
772  * INPUTS
773  *    Tn5250Terminal  *    This       -
774  *    Tn5250Display *      display    -
775  * DESCRIPTION
776  *    DOCUMENT ME!!!
777  *****/
curses_terminal_update_indicators(Tn5250Terminal * This,Tn5250Display * display)778 static void curses_terminal_update_indicators(Tn5250Terminal /*@unused@*/ * This, Tn5250Display *display)
779 {
780    int inds = tn5250_display_indicators(display);
781    char ind_buf[80];
782 
783    memset(ind_buf, ' ', sizeof(ind_buf));
784    memcpy(ind_buf, "5250", 4);
785    if ((inds & TN5250_DISPLAY_IND_MESSAGE_WAITING) != 0)
786       memcpy(ind_buf + 23, "MW", 2);
787    if ((inds & TN5250_DISPLAY_IND_INHIBIT) != 0)
788       memcpy(ind_buf + 9, "X II", 4);
789    else if ((inds & TN5250_DISPLAY_IND_X_CLOCK) != 0)
790       memcpy(ind_buf + 9, "X CLOCK", 7);
791    else if ((inds & TN5250_DISPLAY_IND_X_SYSTEM) != 0)
792       memcpy(ind_buf + 9, "X SYSTEM", 8);
793    if ((inds & TN5250_DISPLAY_IND_INSERT) != 0)
794       memcpy(ind_buf + 30, "IM", 2);
795    if ((inds & TN5250_DISPLAY_IND_FER) != 0)
796       memcpy(ind_buf + 33, "FER", 3);
797    if ((inds & TN5250_DISPLAY_IND_MACRO) != 0)
798       memcpy(ind_buf + 54, tn5250_macro_printstate (display), 11);
799    sprintf(ind_buf+72,"%03.3d/%03.3d",tn5250_display_cursor_x(display)+1,
800       tn5250_display_cursor_y(display)+1);
801 
802    attrset( (attr_t)COLOR_PAIR(COLOR_WHITE) );
803    mvaddnstr(tn5250_display_height(display), 0, ind_buf, 80);
804    move(tn5250_display_cursor_y(display), tn5250_display_cursor_x(display));
805    attrset(A_NORMAL);
806    refresh();
807 }
808 
809 /****i* lib5250/curses_terminal_waitevent
810  * NAME
811  *    curses_terminal_waitevent
812  * SYNOPSIS
813  *    ret = curses_terminal_waitevent (This);
814  * INPUTS
815  *    Tn5250Terminal *     This       -
816  * DESCRIPTION
817  *    DOCUMENT ME!!!
818  *****/
curses_terminal_waitevent(Tn5250Terminal * This)819 static int curses_terminal_waitevent(Tn5250Terminal * This)
820 {
821    fd_set fdr;
822    int result = 0;
823    int sm;
824 
825 
826    if (This->data->quit_flag)
827       return TN5250_TERMINAL_EVENT_QUIT;
828 
829    FD_ZERO(&fdr);
830 
831    FD_SET(0, &fdr);
832    sm = 1;
833    if (This->conn_fd >= 0) {
834       FD_SET(This->conn_fd, &fdr);
835       sm = This->conn_fd + 1;
836    }
837 
838    select(sm, &fdr, NULL, NULL, NULL);
839 
840    if (FD_ISSET(0, &fdr))
841       result |= TN5250_TERMINAL_EVENT_KEY;
842 
843    if (This->conn_fd >= 0 && FD_ISSET(This->conn_fd, &fdr))
844       result |= TN5250_TERMINAL_EVENT_DATA;
845 
846    return result;
847 }
848 
849 #ifndef USE_OWN_KEY_PARSING
850 /****i* lib5250/curses_terminal_getkey
851  * NAME
852  *    curses_terminal_getkey
853  * SYNOPSIS
854  *    ret = curses_terminal_getkey (This);
855  * INPUTS
856  *    Tn5250Terminal *     This       -
857  * DESCRIPTION
858  *    DOCUMENT ME!!!
859  *****/
curses_terminal_getkey(Tn5250Terminal * This)860 static int curses_terminal_getkey(Tn5250Terminal * This)
861 {
862    int key;
863 
864    key = getch();
865 
866    while (1) {
867       switch (key) {
868 
869       case 0x0d:
870       case 0x0a:
871 	 return K_ENTER;
872 
873       case 0x1b:
874 	 if ((key = curses_terminal_get_esc_key(This, 1)) != ERR)
875 	    return key;
876 	 break;
877 
878       case K_CTRL('A'):
879 	 return K_ATTENTION;
880       case K_CTRL('B'):
881 	 return K_ROLLDN;
882       case K_CTRL('C'):
883 	 return K_SYSREQ;
884       case K_CTRL('D'):
885 	 return K_ROLLUP;
886       case K_CTRL('E'):
887 	 return K_ERASE;
888       case K_CTRL('F'):
889 	 return K_ROLLUP;
890       case K_CTRL('K'):
891 	 return K_FIELDEXIT;
892       case K_CTRL('L'):
893 	 return K_REFRESH;
894       case K_CTRL('O'):
895 	 return K_HOME;
896       case K_CTRL('P'):
897 	 return K_PRINT;
898       case K_CTRL('R'):
899 	 return K_RESET;	/* Error Reset */
900       case K_CTRL('S'):
901 	 return K_MEMO;
902       case K_CTRL('T'):
903 	 return K_TESTREQ;
904       case K_CTRL('U'):
905 	 return K_ROLLDN;
906       case K_CTRL('W'):
907 	 return K_EXEC;
908       case K_CTRL('X'):
909 	 return K_FIELDPLUS;
910 
911       case K_CTRL('Q'):
912 	 This->data->quit_flag = 1;
913 	 return -1;
914 
915       case K_CTRL('G'):	/* C-g <function-key-shortcut> */
916 	 if ((key = curses_terminal_get_esc_key(This, 0)) != ERR)
917 	    return key;
918 	 break;
919 
920       case ERR:
921 	 return -1;
922 
923       case 127:
924 	 return K_DELETE;
925 
926       case KEY_A1:
927 	 return K_HOME;
928 
929       case KEY_A3:
930 	 return K_ROLLDN;
931 
932       case KEY_C1:
933 	 return K_END;
934 
935       case KEY_C3:
936 	 return K_ROLLUP;
937 
938       case KEY_ENTER:
939 	 return K_FIELDEXIT;
940 
941       case KEY_END:
942 	 return K_END;
943 
944       default:
945 	 return key;
946       }
947    }
948 }
949 #endif
950 
951 /****i* lib5250/curses_terminal_beep
952  * NAME
953  *    curses_terminal_beep
954  * SYNOPSIS
955  *    curses_terminal_beep (This);
956  * INPUTS
957  *    Tn5250Terminal *     This       -
958  * DESCRIPTION
959  *    DOCUMENT ME!!!
960  *****/
curses_terminal_beep(Tn5250Terminal * This)961 static void curses_terminal_beep (Tn5250Terminal *This)
962 {
963    TN5250_LOG (("CURSES: beep\n"));
964    beep ();
965    refresh ();
966 }
967 
968 
969 /***** lib5250/curses_terminal_enhanced
970  * NAME
971  *    curses_terminal_enhanced
972  * SYNOPSIS
973  *    ret = curses_terminal_enhanced (This);
974  * INPUTS
975  *    Tn5250Terminal  *    This       -
976  * DESCRIPTION
977  *    Return 1 if we support the enhanced 5250 protocol, 0 otherwise.
978  *****/
979 static int
curses_terminal_enhanced(Tn5250Terminal * This)980 curses_terminal_enhanced (Tn5250Terminal * This)
981 {
982   return (0);
983 }
984 
985 #ifndef USE_OWN_KEY_PARSING
986 /****i* lib5250/curses_terminal_get_esc_key
987  * NAME
988  *    curses_terminal_get_esc_key
989  * SYNOPSIS
990  *    ret = curses_terminal_get_esc_key (This, is_esc);
991  * INPUTS
992  *    Tn5250Terminal *     This       -
993  *    int                  is_esc     -
994  * DESCRIPTION
995  *    If a vt100 escape key sequence was introduced (using either
996  *    <Esc> or <Ctrl+g>), handle the next key in the sequence.
997  *****/
curses_terminal_get_esc_key(Tn5250Terminal * This,int is_esc)998 static int curses_terminal_get_esc_key(Tn5250Terminal * This, int is_esc)
999 {
1000    int y, x, key, display_key;
1001    fd_set fdr;
1002 
1003    getyx(stdscr, y, x);
1004    attrset(COLOR_PAIR(COLOR_WHITE));
1005    if (is_esc)
1006       mvaddstr(24, 60, "Esc ");
1007    else
1008       mvaddstr(24, 60, "C-g ");
1009    move(y, x);
1010    refresh();
1011 
1012    FD_ZERO(&fdr);
1013    FD_SET(0, &fdr);
1014    select(1, &fdr, NULL, NULL, NULL);
1015    key = getch();
1016 
1017    if (isalpha(key))
1018       key = toupper(key);
1019 
1020    display_key = key;
1021    switch (key) {
1022 
1023       /* Function keys */
1024    case '1':
1025       key = K_F1;
1026       break;
1027    case '2':
1028       key = K_F2;
1029       break;
1030    case '3':
1031       key = K_F3;
1032       break;
1033    case '4':
1034       key = K_F4;
1035       break;
1036    case '5':
1037       key = K_F5;
1038       break;
1039    case '6':
1040       key = K_F6;
1041       break;
1042    case '7':
1043       key = K_F7;
1044       break;
1045    case '8':
1046       key = K_F8;
1047       break;
1048    case '9':
1049       key = K_F9;
1050       break;
1051    case '0':
1052       key = K_F10;
1053       break;
1054    case '-':
1055       key = K_F11;
1056       break;
1057    case '=':
1058       key = K_F12;
1059       break;
1060    case '!':
1061       key = K_F13;
1062       break;
1063    case '@':
1064       key = K_F14;
1065       break;
1066    case '#':
1067       key = K_F15;
1068       break;
1069    case '$':
1070       key = K_F16;
1071       break;
1072    case '%':
1073       key = K_F17;
1074       break;
1075    case '^':
1076       key = K_F18;
1077       break;
1078    case '&':
1079       key = K_F19;
1080       break;
1081    case '*':
1082       key = K_F20;
1083       break;
1084    case '(':
1085       key = K_F21;
1086       break;
1087    case ')':
1088       key = K_F22;
1089       break;
1090    case '_':
1091       key = K_F23;
1092       break;
1093    case '+':
1094       key = K_F24;
1095       break;
1096 
1097    case K_CTRL('U'):
1098       key = K_NEXTFLD;
1099       break;
1100    case K_CTRL('H'):
1101       key = K_PREVFLD;
1102       break;
1103    case K_CTRL('F'):
1104       key = K_FIELDHOME;
1105       break;
1106 
1107       /* AS/400 strangeness */
1108    case 'A':
1109       key = K_ATTENTION;
1110       break;
1111    case 'C':
1112       key = K_CLEAR;
1113       break;
1114    case 'D':
1115       key = K_DUPLICATE;
1116       break;
1117    case 'H':
1118       key = K_HELP;
1119       break;
1120    case 'I':
1121       key = K_INSERT;
1122       break;
1123    case 'L':
1124       key = K_REFRESH;
1125       break;
1126    case 'M':
1127       key = K_FIELDMINUS;
1128       break;
1129    case 'P':
1130       key = K_PRINT;
1131       break;
1132    case 'R':
1133       key = K_RESET;
1134       break;
1135    case 'S':
1136       key = K_SYSREQ;
1137       break;
1138    case 'T':
1139       key = K_TOGGLE;
1140       break;
1141    case 'X':
1142       key = K_FIELDEXIT;
1143       break;
1144 
1145    case 127:
1146       key = K_INSERT;
1147       break;			/* ESC DEL */
1148    case KEY_DC:
1149       key = K_INSERT;
1150       break;			/* ESC DEL, also */
1151    case K_CTRL('J'):
1152       key = K_NEWLINE;
1153       break;
1154 
1155    case 'Q':
1156       This->data->quit_flag = 1;
1157       key = -1;
1158       break;
1159 
1160    case ERR:
1161    default:
1162       beep();
1163       key = -1;
1164       break;
1165    }
1166 
1167    if (key == -1)
1168       mvaddstr(24, 64, "???");
1169    else
1170       mvaddch(24, 64, (chtype)display_key);
1171    move(y, x);
1172    refresh();
1173    return key;
1174 }
1175 #endif
1176 
1177 #ifdef USE_OWN_KEY_PARSING
curses_get_key(Tn5250Terminal * This,int rmflag)1178 static int curses_get_key (Tn5250Terminal *This, int rmflag)
1179 {
1180    int i, j;
1181    int have_incomplete_match = -1;
1182    int have_complete_match = -1;
1183    int complete_match_len;
1184 
1185    /* Fast path */
1186    if (This->data->k_buf_len == 0)
1187       return -1;
1188 
1189    /* Look up escape codes. */
1190    for (i = 0; i < This->data->k_map_len; i++) {
1191 
1192       /* Skip empty entries. */
1193       if (This->data->k_map[i].k_str[0] == '\0')
1194 	 continue;
1195 
1196       for (j = 0; j < MAX_K_BUF_LEN + 1; j++) {
1197 	 if (This->data->k_map[i].k_str[j] == '\0') {
1198 	    have_complete_match = i;
1199 	    complete_match_len = j;
1200 	    break;
1201 	 }
1202 	 if (j == This->data->k_buf_len) {
1203 	    have_incomplete_match = i;
1204 	    TN5250_LOG (("Have incomplete match ('%s')\n",
1205 		     This->data->k_map[i].k_str));
1206 	    break;
1207 	 }
1208 	 if (This->data->k_map[i].k_str[j] != This->data->k_buf[j])
1209 	    break; /* No match */
1210       }
1211 
1212    }
1213 
1214    if (have_incomplete_match == -1 && have_complete_match == -1) {
1215       /* At this point, we know that we don't have an escape sequence,
1216        * so just return the next character. */
1217       i = This->data->k_buf[0];
1218       if (rmflag) {
1219 	 memmove (This->data->k_buf, This->data->k_buf + 1, MAX_K_BUF_LEN - 1);
1220 	 This->data->k_buf_len --;
1221       }
1222       return i;
1223    }
1224 
1225    if (have_incomplete_match != -1)
1226       return -1;
1227 
1228    if (have_complete_match != -1) {
1229       if (rmflag) {
1230 	 if (This->data->k_buf_len - complete_match_len > 0) {
1231 	    memmove (This->data->k_buf, This->data->k_buf + complete_match_len,
1232 		  This->data->k_buf_len - complete_match_len);
1233 	 }
1234 	 This->data->k_buf_len -= complete_match_len;
1235       }
1236       return This->data->k_map[have_complete_match].k_code;
1237    }
1238 
1239    return -1;
1240 }
1241 
curses_terminal_getkey(Tn5250Terminal * This)1242 static int curses_terminal_getkey (Tn5250Terminal *This)
1243 {
1244    int ch;
1245 
1246    /* Retreive all keys from the keyboard buffer. */
1247    while (This->data->k_buf_len < MAX_K_BUF_LEN && (ch = getch ()) != ERR) {
1248       TN5250_LOG(("curses_getch: received 0x%02X.\n", ch));
1249 
1250       /* FIXME: Here would be the proper place to get mouse events :) */
1251 
1252       /* HACK! Why are we gettings these 0410s still?  ncurses bug? */
1253       if (ch < 0 || ch > 255)
1254 	 continue;
1255 
1256       This->data->k_buf[This->data->k_buf_len++] = ch;
1257    }
1258 
1259    ch = curses_get_key (This, 1);
1260    switch (ch) {
1261    case K_CTRL('Q'):
1262       This->data->quit_flag = 1;
1263       return -1;
1264    case 0x0a:
1265       return 0x0d;
1266    case K_PRINT:
1267       if (This->data->local_print) {
1268          curses_terminal_print_screen(This, This->data->display);
1269          ch = K_RESET;
1270       }
1271       break;
1272    }
1273    return ch;
1274 }
1275 #endif
1276 
1277 /****i* lib5250/curses_rgb_to_color
1278  * NAME
1279  *    curses_rgb_to_color
1280  * SYNOPSIS
1281  *    curses_rgb_to_color (r, g, b, &clr, &bld);
1282  * INPUTS
1283  *    int                  r          -
1284  *    int                  g          -
1285  *    int                  b          -
1286  *    int            *     rclr       -
1287  *    int            *     rbold      -
1288  * DESCRIPTION
1289  *    DOCUMENT ME!!!
1290  *****/
curses_rgb_to_color(int r,int g,int b,int * rclr,int * rbold)1291 int curses_rgb_to_color(int r, int g, int b, int *rclr, int *rbold) {
1292 
1293     int clr;
1294 
1295     clr = ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff);
1296     *rbold = A_NORMAL;
1297 
1298     switch (clr) {
1299        case 0xFFFFFF:
1300 	 *rclr = COLOR_WHITE;   *rbold = A_BOLD;
1301          break;
1302        case 0xFFFF00:
1303 	 *rclr = COLOR_YELLOW;  *rbold = A_BOLD;
1304          break;
1305        case 0xFF00FF:
1306 	 *rclr = COLOR_MAGENTA; *rbold = A_BOLD;
1307          break;
1308        case 0xFF0000:
1309 	 *rclr = COLOR_RED;     *rbold = A_BOLD;
1310          break;
1311        case 0x00FFFF:
1312 	 *rclr = COLOR_CYAN;    *rbold = A_BOLD;
1313          break;
1314        case 0x00FF00:
1315 	 *rclr = COLOR_GREEN;   *rbold = A_BOLD;
1316          break;
1317        case 0x0000FF:
1318 	 *rclr = COLOR_BLUE;    *rbold = A_BOLD;
1319          break;
1320        case 0x808080:
1321 	 *rclr = COLOR_WHITE;
1322          break;
1323        case 0xC0C0C0:
1324 	 *rclr = COLOR_WHITE;
1325          break;
1326        case 0x808000:
1327 	 *rclr = COLOR_YELLOW;
1328          break;
1329        case 0x800080:
1330 	 *rclr = COLOR_MAGENTA;
1331          break;
1332        case 0x800000:
1333 	 *rclr = COLOR_RED;
1334          break;
1335        case 0x008080:
1336 	 *rclr = COLOR_CYAN;
1337          break;
1338        case 0x008000:
1339 	 *rclr = COLOR_GREEN;
1340          break;
1341        case 0x000080:
1342 	 *rclr = COLOR_BLUE;
1343          break;
1344        case 0x000000:
1345 	 *rclr = COLOR_BLACK;
1346          break;
1347        default:
1348          return -1;
1349     }
1350 
1351     return 0;
1352 }
1353 
1354 /****i* lib5250/tn5250_curses_terminal_load_colorlist
1355  * NAME
1356  *    tn5250_curses_terminal_load_colorlist
1357  * SYNOPSIS
1358  *    tn5250_curses_terminal_load_colorlist(config);
1359  * INPUTS
1360  *    Tn5250Config   *     config     -
1361  * DESCRIPTION
1362  *    DOCUMENT ME!!!
1363  *****/
tn5250_curses_terminal_load_colorlist(Tn5250Config * config)1364 void tn5250_curses_terminal_load_colorlist(Tn5250Config *config) {
1365 
1366    int r, g, b, x, clr, bld;
1367 
1368    if (tn5250_config_get_bool(config, "black_on_white")) {
1369        for (x=COLOR_BLACK+1; x<=COLOR_WHITE; x++) {
1370            colorlist[x].ref = COLOR_BLACK;
1371            colorlist[x].bld = A_NORMAL;
1372        }
1373        colorlist[COLOR_BLACK].ref = COLOR_WHITE;
1374        colorlist[COLOR_BLACK].bld = A_BOLD;
1375    }
1376 
1377    if (tn5250_config_get_bool(config, "white_on_black")) {
1378        for (x=COLOR_BLACK+1; x<=COLOR_WHITE; x++) {
1379            colorlist[x].ref = COLOR_WHITE;
1380            colorlist[x].bld = A_BOLD;
1381        }
1382        colorlist[COLOR_BLACK].ref = COLOR_BLACK;
1383        colorlist[COLOR_BLACK].bld = A_NORMAL;
1384    }
1385 
1386    x=0;
1387    while (colorlist[x].name != NULL) {
1388        if (tn5250_parse_color(config, colorlist[x].name, &r, &g, &b)!=-1) {
1389            if (curses_rgb_to_color(r,g,b, &clr, &bld) != -1) {
1390                 colorlist[x].ref = clr;
1391                 colorlist[x].bld = bld;
1392            }
1393        }
1394        x++;
1395    }
1396 
1397 }
1398 
1399 
1400 /****i* lib5250/curses_terminal_config
1401  * NAME
1402  *    curses_terminal_config
1403  * SYNOPSIS
1404  *    curses_terminal_config(This, config);
1405  * INPUTS
1406  *    Tn5250Terminal *     This       -
1407  *    Tn5250Display  *     config     -
1408  * DESCRIPTION
1409  *    Assign a set of configuration data to the terminal
1410  *****/
curses_terminal_config(Tn5250Terminal * This,Tn5250Config * config)1411 int curses_terminal_config(Tn5250Terminal *This, Tn5250Config *config) {
1412     This->data->config = config;
1413     if (tn5250_config_get_bool(config, "local_print_key"))
1414           This->data->local_print = 1;
1415     return 0;
1416 }
1417 
1418 
1419 /****i* lib5250/curses_terminal_print_screen
1420  * NAME
1421  *    curses_terminal_print_screen
1422  * SYNOPSIS
1423  *    curses_terminal_print_screen(This, This->data->display);
1424  * INPUTS
1425  *    Tn5250Terminal *     This       -
1426  *    Tn5250Display  *     display    -
1427  * DESCRIPTION
1428  *    Generate PostScript output of current screen image.
1429  *****/
curses_terminal_print_screen(Tn5250Terminal * This,Tn5250Display * display)1430 void curses_terminal_print_screen(Tn5250Terminal *This, Tn5250Display *display) {
1431 
1432    int x, y, c, a;
1433    attr_t curs_attr;
1434    int px, py;
1435    int leftmar, topmar;
1436    FILE *out;
1437    const char *outcmd;
1438    double pgwid, pglen;
1439    double colwidth, rowheight, fontsize;
1440    int textlen;
1441    char *prttext;
1442 
1443    if (display==NULL)
1444        return;
1445 
1446    /* default values for printing screens are: */
1447 
1448    outcmd = "lpr";
1449    pglen = 11 * 72;
1450    pgwid = 8.5 * 72;
1451    leftmar = 18;
1452    topmar = 36;
1453    if (tn5250_display_width(display) == 132)
1454         fontsize = 7.0;
1455    else
1456         fontsize = 10.0;
1457 
1458    /* override defaults with values from config if available */
1459 
1460    if (This->data->config != NULL) {
1461        int fs80=0, fs132=0;
1462        if (tn5250_config_get(This->data->config, "outputcommand"))
1463            outcmd = tn5250_config_get(This->data->config, "outputcommand");
1464        if (tn5250_config_get(This->data->config, "pagewidth"))
1465            pgwid = atoi(tn5250_config_get(This->data->config, "pagewidth"));
1466        if (tn5250_config_get(This->data->config, "pagelength"))
1467            pglen = atoi(tn5250_config_get(This->data->config, "pagelength"));
1468        if (tn5250_config_get(This->data->config, "leftmargin"))
1469            leftmar = atoi(tn5250_config_get(This->data->config, "leftmargin"));
1470        if (tn5250_config_get(This->data->config, "topmargin"))
1471            topmar = atoi(tn5250_config_get(This->data->config, "topmargin"));
1472        if (tn5250_config_get(This->data->config, "psfontsize_80"))
1473            fs80 = atoi(tn5250_config_get(This->data->config, "psfontsize_80"));
1474        if (tn5250_config_get(This->data->config, "psfontsize_80"))
1475            fs132 =atoi(tn5250_config_get(This->data->config, "psfontsize_132"));
1476        if (tn5250_display_width(display)==132 && fs132!=0)
1477            fontsize = fs132;
1478        if (tn5250_display_width(display)==80 && fs80!=0)
1479            fontsize = fs80;
1480    }
1481 
1482    colwidth  = (pgwid - leftmar*2) / tn5250_display_width(display);
1483    rowheight = (pglen  - topmar*2) / 66;
1484 
1485 
1486    /* allocate enough memory to store the largest possible string that we
1487       could output.   Note that it could be twice the size of the screen
1488       if every single character needs to be escaped... */
1489    prttext = malloc((2 * tn5250_display_width(display) *
1490 		     tn5250_display_height(display)) + 1);
1491 
1492    out = popen(outcmd, "w");
1493    if (out == NULL)
1494        return;
1495 
1496    fprintf(out, "%%!PS-Adobe-3.0\n");
1497    fprintf(out, "%%%%Pages: 1\n");
1498    fprintf(out, "%%%%Title: TN5250 Print Screen\n");
1499    fprintf(out, "%%%%BoundingBox: 0 0 %.0f %.0f\n", pgwid, pglen);
1500    fprintf(out, "%%%%LanguageLevel: 2\n");
1501    fprintf(out, "%%%%EndComments\n\n");
1502    fprintf(out, "%%%%BeginProlog\n");
1503    fprintf(out, "%%%%BeginResource: procset general 1.0.0\n");
1504    fprintf(out, "%%%%Title: (General Procedures)\n");
1505    fprintf(out, "%%%%Version: 1.0\n");
1506    fprintf(out, "%% Courier is a fixed-pitch font, so one character is as\n");
1507    fprintf(out, "%%   good as another for determining the height/width\n");
1508    fprintf(out, "/Courier %.2f selectfont\n", fontsize);
1509    fprintf(out, "/chrwid (W) stringwidth pop def\n");
1510    fprintf(out, "/pglen %.2f def\n", pglen);
1511    fprintf(out, "/pgwid %.2f def\n", pgwid);
1512    fprintf(out, "/chrhgt %.2f def\n", rowheight);
1513    fprintf(out, "/leftmar %d def\n", leftmar + 2);
1514    fprintf(out, "/topmar %d def\n", topmar);
1515    fprintf(out, "/exploc {           %% expand x y to dot positions\n"
1516                 "   chrhgt mul\n"
1517                 "   topmar add\n"
1518                 "   3 add\n"
1519                 "   pglen exch sub\n"
1520                 "   exch\n"
1521                 "   chrwid mul\n"
1522                 "   leftmar add\n"
1523                 "   3 add\n"
1524                 "   exch\n"
1525                 "} bind def\n");
1526    fprintf(out, "/prtnorm {          %% print text normally (text) x y color\n"
1527                 "   setgray\n"
1528                 "   exploc moveto\n"
1529                 "   show\n"
1530                 "} bind def\n");
1531    fprintf(out, "/drawunderline  { %% draw underline: (string) x y color\n"
1532                 "   gsave\n"
1533                 "   0 setlinewidth\n"
1534                 "   setgray\n"
1535                 "   exploc\n"
1536                 "   2 sub\n"
1537                 "   moveto\n"
1538                 "   stringwidth pop 0\n"
1539                 "   rlineto\n"
1540                 "   stroke\n"
1541                 "   grestore\n"
1542                 "} bind def\n");
1543    fprintf(out, "/blkbox {       %% draw a black box behind the text\n"
1544                 "   gsave\n"
1545                 "   newpath\n"
1546                 "   0 setgray\n"
1547                 "   exploc\n"
1548                 "   3 sub\n"
1549                 "   moveto\n"
1550                 "   0 chrhgt rlineto\n"
1551                 "   stringwidth pop 0 rlineto\n"
1552                 "   0 0 chrhgt sub rlineto\n"
1553                 "   closepath\n"
1554                 "   fill\n"
1555                 "   grestore\n"
1556                 "} bind def\n");
1557    fprintf(out, "/borderbox { %% Print a border around screen dump\n"
1558                 "   gsave\n"
1559                 "   newpath\n"
1560                 "   0 setlinewidth\n"
1561                 "   0 setgray\n"
1562                 "   leftmar\n"
1563                 "   topmar chrhgt sub pglen exch sub\n"
1564                 "   moveto\n"
1565                 "   chrwid %d mul 6 add 0 rlineto\n"
1566                 "   0 0 chrhgt %d mul 6 add sub rlineto\n"
1567                 "   0 chrwid %d mul 6 add sub 0 rlineto\n"
1568                 "   closepath\n"
1569                 "   stroke\n"
1570                 "   grestore\n"
1571                 "} bind def\n",
1572                 tn5250_display_width(display),
1573                 tn5250_display_height(display)+1,
1574                 tn5250_display_width(display));
1575    fprintf(out, "%%%%EndResource\n");
1576    fprintf(out, "%%%%EndProlog\n\n");
1577    fprintf(out, "%%%%Page 1 1\n");
1578    fprintf(out, "%%%%BeginPageSetup\n");
1579    fprintf(out, "/pgsave save def\n");
1580    fprintf(out, "%%%%EndPageSetup\n");
1581 
1582    curs_attr =  0x00;
1583    textlen = 0;
1584    px = -1;
1585 
1586    for (y = 0; y < tn5250_display_height(display); y++) {
1587 
1588       for (x = 0; x < tn5250_display_width(display); x++) {
1589 
1590 	 c = tn5250_display_char_at(display, y, x);
1591 	 if ((c & 0xe0) == 0x20) {
1592             if (textlen > 0) {
1593                 curses_postscript_print(out, px, py, prttext, curs_attr);
1594                 textlen = 0;
1595             }
1596 	    a = (c & 0xff);
1597             curs_attr = attribute_map[a - 0x20];
1598             px = -1;
1599          } else {
1600             if (px == -1) {
1601                 px = x;
1602                 py = y;
1603             }
1604 	    if ((c < 0x40 && c > 0x00) || c == 0xff) {
1605 	       c = ' ';
1606 	    } else {
1607 	       c = tn5250_char_map_to_local (
1608                       tn5250_display_char_map (display), c);
1609 	    }
1610             if (c == '\\' || c == '(' || c == ')') {
1611                  prttext[textlen] = '\\';
1612                  textlen++;
1613             }
1614             prttext[textlen] = c;
1615             textlen++;
1616             prttext[textlen] = '\0';
1617          }
1618       }
1619       if (textlen > 0) {
1620           curses_postscript_print(out, px, py, prttext, curs_attr);
1621           textlen = 0;
1622       }
1623       px = -1;
1624    }
1625 
1626    fprintf(out, "borderbox\n");
1627    fprintf(out, "pgsave restore\n");
1628    fprintf(out, "showpage\n");
1629    fprintf(out, "%%%%PageTrailer\n");
1630    fprintf(out, "%%%%Trailer\n");
1631    fprintf(out, "%%%%Pages: 1\n");
1632    fprintf(out, "%%%%EOF\n");
1633 
1634    pclose(out);
1635 
1636    free(prttext);
1637 
1638    attrset(attribute_map[0]);
1639    clear();
1640    mvprintw(0, 0, "Print Screen Successful!");
1641    mvprintw(1, 0, "Press ENTER to continue.");
1642    refresh();
1643 
1644    while (curses_terminal_getkey(This)!=K_ENTER) { /* wait */ }
1645 
1646    curses_terminal_update(This, display);
1647 }
1648 
1649 
1650 /****i* lib5250/curses_postscript_print
1651  * NAME
1652  *    curses_postscript_print
1653  * SYNOPSIS
1654  *    curses_postscript_print(out, px, py, "Print this", A_NORMAL);
1655  * INPUTS
1656  *    FILE           *     out        -
1657  *    int                  x          -
1658  *    int                  y          -
1659  *    char           *     string     -
1660  *    attr_t               attr       -
1661  * DESCRIPTION
1662  *    Adds a printed string to the postscript output generated by
1663  *    curses_terminal_print_screen.   Converts the curses attributes
1664  *    to attributes understood by postscript (using the procedures
1665  *    we put in the prolog of the ps document)
1666  *****/
1667 
curses_postscript_print(FILE * out,int x,int y,char * string,attr_t attr)1668 void curses_postscript_print(FILE *out, int x, int y, char *string, attr_t attr) {
1669     int color;
1670 
1671     if (attr == 0x00)    /* NONDISPLAY */
1672         return;
1673 
1674     color = 0;
1675     if (attr & A_REVERSE) {   /* Print white text on black background */
1676         color = 1;
1677         fprintf(out, "(%s) %d %d blkbox\n", string, x, y);
1678     }
1679 
1680     fprintf(out, "(%s) %d %d %d prtnorm\n", string, x, y, color);
1681 
1682     if (attr & A_UNDERLINE)    /* draw underline below text */
1683         fprintf(out, "(%s) %d %d %d drawunderline\n", string, x, y, color);
1684 
1685 }
1686 
1687 
1688 #endif /* USE_CURSES */
1689 
1690 /* vi:set cindent sts=3 sw=3: */
1691