libpayload: Add PDCurses and ncurses' libform/libmenu
[coreboot.git] / payloads / libpayload / curses / PDCurses-3.4 / x11 / x11.c
1 /* Public Domain Curses */
2
3 #include "pdcx11.h"
4
5 RCSID("$Id: x11.c,v 1.94 2008/07/14 04:33:26 wmcbrine Exp $")
6
7 #ifdef HAVE_DECKEYSYM_H
8 # include <DECkeysym.h>
9 #endif
10
11 #ifdef HAVE_SUNKEYSYM_H
12 # include <Sunkeysym.h>
13 #endif
14
15 #ifdef HAVE_XPM_H
16 # include <xpm.h>
17 #endif
18
19 #if defined PDC_XIM
20 # include <Xlocale.h>
21 #endif
22
23 #include <stdlib.h>
24 #include <string.h>
25
26 #ifndef XPOINTER_TYPEDEFED
27 typedef char * XPointer;
28 #endif
29
30 #ifndef MAX_PATH
31 # define MAX_PATH 256
32 #endif
33
34 XCursesAppData xc_app_data;
35
36 #if NeedWidePrototypes
37 # define PDC_SCROLLBAR_TYPE double
38 #else
39 # define PDC_SCROLLBAR_TYPE float
40 #endif
41
42 #define MAX_COLORS   16             /* maximum of "normal" colors */
43 #define COLOR_CURSOR MAX_COLORS     /* color of cursor */
44 #define COLOR_BORDER MAX_COLORS + 1 /* color of border */
45
46 #define XCURSESDISPLAY (XtDisplay(drawing))
47 #define XCURSESWIN     (XtWindow(drawing))
48
49 /* Default icons for XCurses applications.  */
50
51 #include "big_icon.xbm"
52 #include "little_icon.xbm"
53
54 static void _selection_off(void);
55 static void _display_cursor(int, int, int, int);
56 static void _redraw_cursor(void);
57 static void _exit_process(int, int, char *);
58 static void _send_key_to_curses(unsigned long, MOUSE_STATUS *, bool);
59
60 static void XCursesButton(Widget, XEvent *, String *, Cardinal *);
61 static void XCursesHandleString(Widget, XEvent *, String *, Cardinal *);
62 static void XCursesKeyPress(Widget, XEvent *, String *, Cardinal *);
63 static void XCursesPasteSelection(Widget, XButtonEvent *);
64
65 static struct
66 {
67     KeySym keycode;
68     bool numkeypad;
69     unsigned short normal;
70     unsigned short shifted;
71     unsigned short control;
72     unsigned short alt;
73 } key_table[] =
74 {
75 /* keycode      keypad  normal       shifted       control      alt*/
76  {XK_Left,      FALSE,  KEY_LEFT,    KEY_SLEFT,    CTL_LEFT,    ALT_LEFT},
77  {XK_Right,     FALSE,  KEY_RIGHT,   KEY_SRIGHT,   CTL_RIGHT,   ALT_RIGHT},
78  {XK_Up,        FALSE,  KEY_UP,      KEY_SUP,      CTL_UP,      ALT_UP},
79  {XK_Down,      FALSE,  KEY_DOWN,    KEY_SDOWN,    CTL_DOWN,    ALT_DOWN},
80  {XK_Home,      FALSE,  KEY_HOME,    KEY_SHOME,    CTL_HOME,    ALT_HOME},
81 /* Sun Type 4 keyboard */
82  {XK_R7,        FALSE,  KEY_HOME,    KEY_SHOME,    CTL_HOME,    ALT_HOME},
83  {XK_End,       FALSE,  KEY_END,     KEY_SEND,     CTL_END,     ALT_END},
84 /* Sun Type 4 keyboard */
85  {XK_R13,       FALSE,  KEY_END,     KEY_SEND,     CTL_END,     ALT_END},
86  {XK_Prior,     FALSE,  KEY_PPAGE,   KEY_SPREVIOUS,CTL_PGUP,    ALT_PGUP},
87 /* Sun Type 4 keyboard */
88  {XK_R9,        FALSE,  KEY_PPAGE,   KEY_SPREVIOUS,CTL_PGUP,    ALT_PGUP},
89  {XK_Next,      FALSE,  KEY_NPAGE,   KEY_SNEXT,    CTL_PGDN,    ALT_PGDN},
90 /* Sun Type 4 keyboard */
91  {XK_R15,       FALSE,  KEY_NPAGE,   KEY_SNEXT,    CTL_PGDN,    ALT_PGDN},
92  {XK_Insert,    FALSE,  KEY_IC,      KEY_SIC,      CTL_INS,     ALT_INS},
93  {XK_Delete,    FALSE,  KEY_DC,      KEY_SDC,      CTL_DEL,     ALT_DEL},
94  {XK_F1,        FALSE,  KEY_F(1),    KEY_F(13),    KEY_F(25),   KEY_F(37)},
95  {XK_F2,        FALSE,  KEY_F(2),    KEY_F(14),    KEY_F(26),   KEY_F(38)},
96  {XK_F3,        FALSE,  KEY_F(3),    KEY_F(15),    KEY_F(27),   KEY_F(39)},
97  {XK_F4,        FALSE,  KEY_F(4),    KEY_F(16),    KEY_F(28),   KEY_F(40)},
98  {XK_F5,        FALSE,  KEY_F(5),    KEY_F(17),    KEY_F(29),   KEY_F(41)},
99  {XK_F6,        FALSE,  KEY_F(6),    KEY_F(18),    KEY_F(30),   KEY_F(42)},
100  {XK_F7,        FALSE,  KEY_F(7),    KEY_F(19),    KEY_F(31),   KEY_F(43)},
101  {XK_F8,        FALSE,  KEY_F(8),    KEY_F(20),    KEY_F(32),   KEY_F(44)},
102  {XK_F9,        FALSE,  KEY_F(9),    KEY_F(21),    KEY_F(33),   KEY_F(45)},
103  {XK_F10,       FALSE,  KEY_F(10),   KEY_F(22),    KEY_F(34),   KEY_F(46)},
104  {XK_F11,       FALSE,  KEY_F(11),   KEY_F(23),    KEY_F(35),   KEY_F(47)},
105  {XK_F12,       FALSE,  KEY_F(12),   KEY_F(24),    KEY_F(36),   KEY_F(48)},
106  {XK_F13,       FALSE,  KEY_F(13),   KEY_F(25),    KEY_F(37),   KEY_F(49)},
107  {XK_F14,       FALSE,  KEY_F(14),   KEY_F(26),    KEY_F(38),   KEY_F(50)},
108  {XK_F15,       FALSE,  KEY_F(15),   KEY_F(27),    KEY_F(39),   KEY_F(51)},
109  {XK_F16,       FALSE,  KEY_F(16),   KEY_F(28),    KEY_F(40),   KEY_F(52)},
110  {XK_F17,       FALSE,  KEY_F(17),   KEY_F(29),    KEY_F(41),   KEY_F(53)},
111  {XK_F18,       FALSE,  KEY_F(18),   KEY_F(30),    KEY_F(42),   KEY_F(54)},
112  {XK_F19,       FALSE,  KEY_F(19),   KEY_F(31),    KEY_F(43),   KEY_F(55)},
113  {XK_F20,       FALSE,  KEY_F(20),   KEY_F(32),    KEY_F(44),   KEY_F(56)},
114  {XK_BackSpace, FALSE,  0x08,        0x08,         CTL_BKSP,    ALT_BKSP},
115  {XK_Tab,       FALSE,  0x09,        KEY_BTAB,     CTL_TAB,     ALT_TAB},
116  {XK_Select,    FALSE,  KEY_SELECT,  KEY_SELECT,   KEY_SELECT,  KEY_SELECT},
117  {XK_Print,     FALSE,  KEY_PRINT,   KEY_SPRINT,   KEY_PRINT,   KEY_PRINT},
118  {XK_Find,      FALSE,  KEY_FIND,    KEY_SFIND,    KEY_FIND,    KEY_FIND},
119  {XK_Pause,     FALSE,  KEY_SUSPEND, KEY_SSUSPEND, KEY_SUSPEND, KEY_SUSPEND},
120  {XK_Clear,     FALSE,  KEY_CLEAR,   KEY_CLEAR,    KEY_CLEAR,   KEY_CLEAR},
121  {XK_Cancel,    FALSE,  KEY_CANCEL,  KEY_SCANCEL,  KEY_CANCEL,  KEY_CANCEL},
122  {XK_Break,     FALSE,  KEY_BREAK,   KEY_BREAK,    KEY_BREAK,   KEY_BREAK},
123  {XK_Help,      FALSE,  KEY_HELP,    KEY_SHELP,    KEY_LHELP,   KEY_HELP},
124  {XK_L4,        FALSE,  KEY_UNDO,    KEY_SUNDO,    KEY_UNDO,    KEY_UNDO},
125  {XK_L6,        FALSE,  KEY_COPY,    KEY_SCOPY,    KEY_COPY,    KEY_COPY},
126  {XK_L9,        FALSE,  KEY_FIND,    KEY_SFIND,    KEY_FIND,    KEY_FIND},
127  {XK_Menu,      FALSE,  KEY_OPTIONS, KEY_SOPTIONS, KEY_OPTIONS, KEY_OPTIONS},
128 #ifdef HAVE_SUNKEYSYM_H
129  {SunXK_F36,    FALSE,  KEY_F(41),   KEY_F(43),    KEY_F(45),   KEY_F(47)},
130  {SunXK_F37,    FALSE,  KEY_F(42),   KEY_F(44),    KEY_F(46),   KEY_F(48)},
131 #endif
132 #ifdef HAVE_DECKEYSYM_H
133  {DXK_Remove,   FALSE,  KEY_DC,      KEY_SDC,      CTL_DEL,     ALT_DEL},
134 #endif
135  {XK_Escape,    FALSE,  0x1B,        0x1B,         0x1B,        ALT_ESC},
136  {XK_KP_Enter,  TRUE,   PADENTER,    PADENTER,     CTL_PADENTER,ALT_PADENTER},
137  {XK_KP_Add,    TRUE,   PADPLUS,     '+',          CTL_PADPLUS, ALT_PADPLUS},
138  {XK_KP_Subtract,TRUE,  PADMINUS,    '-',          CTL_PADMINUS,ALT_PADMINUS},
139  {XK_KP_Multiply,TRUE,  PADSTAR,     '*',          CTL_PADSTAR, ALT_PADSTAR},
140 /* Sun Type 4 keyboard */
141  {XK_R6,        TRUE,   PADSTAR,     '*',          CTL_PADSTAR, ALT_PADSTAR},
142  {XK_KP_Divide, TRUE,   PADSLASH,    '/',          CTL_PADSLASH,ALT_PADSLASH},
143 /* Sun Type 4 keyboard */
144  {XK_R5,        TRUE,   PADSLASH,    '/',          CTL_PADSLASH,ALT_PADSLASH},
145  {XK_KP_Decimal,TRUE,   PADSTOP,     '.',          CTL_PADSTOP, ALT_PADSTOP},
146  {XK_KP_0,      TRUE,   PAD0,        '0',          CTL_PAD0,    ALT_PAD0},
147  {XK_KP_1,      TRUE,   KEY_C1,      '1',          CTL_PAD1,    ALT_PAD1},
148  {XK_KP_2,      TRUE,   KEY_C2,      '2',          CTL_PAD2,    ALT_PAD2},
149  {XK_KP_3,      TRUE,   KEY_C3,      '3',          CTL_PAD3,    ALT_PAD3},
150  {XK_KP_4,      TRUE,   KEY_B1,      '4',          CTL_PAD4,    ALT_PAD4},
151  {XK_KP_5,      TRUE,   KEY_B2,      '5',          CTL_PAD5,    ALT_PAD5},
152 /* Sun Type 4 keyboard */
153  {XK_R11,       TRUE,   KEY_B2,      '5',          CTL_PAD5,    ALT_PAD5},
154  {XK_KP_6,      TRUE,   KEY_B3,      '6',          CTL_PAD6,    ALT_PAD6},
155  {XK_KP_7,      TRUE,   KEY_A1,      '7',          CTL_PAD7,    ALT_PAD7},
156  {XK_KP_8,      TRUE,   KEY_A2,      '8',          CTL_PAD8,    ALT_PAD8},
157  {XK_KP_9,      TRUE,   KEY_A3,      '9',          CTL_PAD9,    ALT_PAD9},
158 /* the following added to support Sun Type 5 keyboards */
159  {XK_F21,       FALSE,  KEY_SUSPEND, KEY_SSUSPEND, KEY_SUSPEND, KEY_SUSPEND},
160  {XK_F22,       FALSE,  KEY_PRINT,   KEY_SPRINT,   KEY_PRINT,   KEY_PRINT},
161  {XK_F24,       TRUE,   PADMINUS,    '-',          CTL_PADMINUS,ALT_PADMINUS},
162 /* Sun Type 4 keyboard */
163  {XK_F25,       TRUE,   PADSLASH,    '/',          CTL_PADSLASH,ALT_PADSLASH},
164 /* Sun Type 4 keyboard */
165  {XK_F26,       TRUE,   PADSTAR,     '*',          CTL_PADSTAR, ALT_PADSTAR},
166  {XK_F27,       TRUE,   KEY_A1,      '7',          CTL_PAD7,    ALT_PAD7},
167  {XK_F29,       TRUE,   KEY_A3,      '9',          CTL_PAD9,    ALT_PAD9},
168  {XK_F31,       TRUE,   KEY_B2,      '5',          CTL_PAD5,    ALT_PAD5},
169  {XK_F35,       TRUE,   KEY_C3,      '3',          CTL_PAD3,    ALT_PAD3},
170 #ifdef HAVE_XK_KP_DELETE
171  {XK_KP_Delete, TRUE,   PADSTOP,     '.',          CTL_PADSTOP, ALT_PADSTOP},
172 #endif
173 #ifdef HAVE_XK_KP_INSERT
174  {XK_KP_Insert, TRUE,   PAD0,        '0',          CTL_PAD0,    ALT_PAD0},
175 #endif
176 #ifdef HAVE_XK_KP_END
177  {XK_KP_End,    TRUE,   KEY_C1,      '1',          CTL_PAD1,    ALT_PAD1},
178 #endif
179 #ifdef HAVE_XK_KP_DOWN
180  {XK_KP_Down,   TRUE,   KEY_C2,      '2',          CTL_PAD2,    ALT_PAD2},
181 #endif
182 #ifdef HAVE_XK_KP_NEXT
183  {XK_KP_Next,   TRUE,   KEY_C3,      '3',          CTL_PAD3,    ALT_PAD3},
184 #endif
185 #ifdef HAVE_XK_KP_LEFT
186  {XK_KP_Left,   TRUE,   KEY_B1,      '4',          CTL_PAD4,    ALT_PAD4},
187 #endif
188 #ifdef HAVE_XK_KP_BEGIN
189  {XK_KP_Begin,  TRUE,   KEY_B2,      '5',          CTL_PAD5,    ALT_PAD5},
190 #endif
191 #ifdef HAVE_XK_KP_RIGHT
192  {XK_KP_Right,  TRUE,   KEY_B3,      '6',          CTL_PAD6,    ALT_PAD6},
193 #endif
194 #ifdef HAVE_XK_KP_HOME
195  {XK_KP_Home,   TRUE,   KEY_A1,      '7',          CTL_PAD7,    ALT_PAD7},
196 #endif
197 #ifdef HAVE_XK_KP_UP
198  {XK_KP_Up,     TRUE,   KEY_A2,      '8',          CTL_PAD8,    ALT_PAD8},
199 #endif
200 #ifdef HAVE_XK_KP_PRIOR
201  {XK_KP_Prior,  TRUE,   KEY_A3,      '9',          CTL_PAD9,    ALT_PAD9},
202 #endif
203  {0,            0,      0,           0,            0,           0}
204 };
205
206 #ifndef PDC_XIM
207 # include "compose.h"
208 #endif
209
210 #define BITMAPDEPTH 1
211
212 unsigned long pdc_key_modifiers = 0L;
213
214 static GC normal_gc, block_cursor_gc, rect_cursor_gc, italic_gc, border_gc;
215 static int font_height, font_width, font_ascent, font_descent,
216            window_width, window_height;
217 static int resize_window_width = 0, resize_window_height = 0;
218 static char *bitmap_file = NULL;
219 #ifdef HAVE_XPM_H
220 static char *pixmap_file = NULL;
221 #endif
222 static KeySym keysym = 0;
223
224 static int state_mask[8] =
225 {
226     ShiftMask,
227     LockMask,
228     ControlMask,
229     Mod1Mask,
230     Mod2Mask,
231     Mod3Mask,
232     Mod4Mask,
233     Mod5Mask
234 };
235
236 static Atom wm_atom[2];
237 static String class_name = "XCurses";
238 static XtAppContext app_context;
239 static Widget topLevel, drawing, scrollBox, scrollVert, scrollHoriz;
240 static int received_map_notify = 0;
241 static bool mouse_selection = FALSE;
242 static chtype *tmpsel = NULL;
243 static unsigned long tmpsel_length = 0;
244 static int selection_start_x = 0, selection_start_y = 0,
245            selection_end_x = 0, selection_end_y = 0;
246 static Pixmap icon_bitmap;
247 #ifdef HAVE_XPM_H
248 static Pixmap icon_pixmap;
249 static Pixmap icon_pixmap_mask;
250 #endif
251 static bool visible_cursor = FALSE;
252 static bool window_entered = TRUE;
253 static char *program_name;
254
255 /* Macros just for app_resources */
256
257 #ifdef PDC_WIDE
258 # define DEFFONT "-misc-fixed-medium-r-normal--20-200-75-75-c-100-iso10646-1"
259 #else
260 # define DEFFONT "7x13"
261 #endif
262
263 #define APPDATAOFF(n) XtOffsetOf(XCursesAppData, n)
264
265 #define RINT(name1, name2, value) { \
266                 #name1, #name2, XtRInt, \
267                 sizeof(int), APPDATAOFF(name1), XtRImmediate, \
268                 (XtPointer)value \
269         }
270
271 #define RPIXEL(name1, name2, value) { \
272                 #name1, #name2, XtRPixel, \
273                 sizeof(Pixel), APPDATAOFF(name1), XtRString, \
274                 (XtPointer)#value \
275         }
276
277 #define RCOLOR(name, value) RPIXEL(color##name, Color##name, value)
278
279
280 #define RSTRINGP(name1, name2, param) { \
281                 #name1, #name2, XtRString, \
282                 MAX_PATH, APPDATAOFF(name1), XtRString, (XtPointer)param \
283         }
284
285 #define RSTRING(name1, name2) RSTRINGP(name1, name2, "")
286
287 #define RFONT(name1, name2, value) { \
288                 #name1, #name2, XtRFontStruct, \
289                 sizeof(XFontStruct), APPDATAOFF(name1), XtRString, \
290                 (XtPointer)value \
291         }
292
293 #define RCURSOR(name1, name2, value) { \
294                 #name1, #name2, XtRCursor, \
295                 sizeof(Cursor), APPDATAOFF(name1), XtRString, \
296                 (XtPointer)#value \
297         }
298
299 static XtResource app_resources[] =
300 {
301     RINT(lines, Lines, 24),
302     RINT(cols, Cols, 80),
303
304     RPIXEL(cursorColor, CursorColor, Red),
305
306     RCOLOR(Black, Black),
307     RCOLOR(Red, red3),
308     RCOLOR(Green, green3),
309     RCOLOR(Yellow, yellow3),
310     RCOLOR(Blue, blue3),
311     RCOLOR(Magenta, magenta3),
312     RCOLOR(Cyan, cyan3),
313     RCOLOR(White, Grey),
314
315     RCOLOR(BoldBlack, grey40),
316     RCOLOR(BoldRed, red1),
317     RCOLOR(BoldGreen, green1),
318     RCOLOR(BoldYellow, yellow1),
319     RCOLOR(BoldBlue, blue1),
320     RCOLOR(BoldMagenta, magenta1),
321     RCOLOR(BoldCyan, cyan1),
322     RCOLOR(BoldWhite, White),
323
324     RFONT(normalFont, NormalFont, DEFFONT),
325     RFONT(italicFont, ItalicFont, DEFFONT),
326
327     RSTRING(bitmap, Bitmap),
328 #ifdef HAVE_XPM_H
329     RSTRING(pixmap, Pixmap),
330 #endif
331     RSTRINGP(composeKey, ComposeKey, "Multi_key"),
332
333     RCURSOR(pointer, Pointer, xterm),
334
335     RPIXEL(pointerForeColor, PointerForeColor, Black),
336     RPIXEL(pointerBackColor, PointerBackColor, White),
337
338     RINT(shmmin, Shmmin, 0),
339     RINT(borderWidth, BorderWidth, 0),
340
341     RPIXEL(borderColor, BorderColor, Black),
342
343     RINT(doubleClickPeriod, DoubleClickPeriod, (PDC_CLICK_PERIOD * 2)),
344     RINT(clickPeriod, ClickPeriod, PDC_CLICK_PERIOD),
345     RINT(scrollbarWidth, ScrollbarWidth, 15),
346     RINT(cursorBlinkRate, CursorBlinkRate, 0),
347
348     RSTRING(textCursor, TextCursor)
349 };
350
351 #undef RCURSOR
352 #undef RFONT
353 #undef RSTRING
354 #undef RCOLOR
355 #undef RPIXEL
356 #undef RINT
357 #undef APPDATAOFF
358 #undef DEFFONT
359
360 /* Macros for options */
361
362 #define COPT(name) {"-" #name, "*" #name, XrmoptionSepArg, NULL}
363 #define CCOLOR(name) COPT(color##name)
364
365 static XrmOptionDescRec options[] =
366 {
367     COPT(lines), COPT(cols), COPT(normalFont), COPT(italicFont),
368     COPT(bitmap),
369 #ifdef HAVE_XPM_H
370     COPT(pixmap),
371 #endif
372     COPT(pointer), COPT(shmmin), COPT(composeKey), COPT(clickPeriod),
373     COPT(doubleClickPeriod), COPT(scrollbarWidth),
374     COPT(pointerForeColor), COPT(pointerBackColor),
375     COPT(cursorBlinkRate), COPT(cursorColor), COPT(textCursor),
376
377     CCOLOR(Black), CCOLOR(Red), CCOLOR(Green), CCOLOR(Yellow),
378     CCOLOR(Blue), CCOLOR(Magenta), CCOLOR(Cyan), CCOLOR(White),
379
380     CCOLOR(BoldBlack), CCOLOR(BoldRed), CCOLOR(BoldGreen), 
381     CCOLOR(BoldYellow), CCOLOR(BoldBlue), CCOLOR(BoldMagenta), 
382     CCOLOR(BoldCyan), CCOLOR(BoldWhite)
383 };
384
385 #undef CCOLOR
386 #undef COPT
387
388 static XtActionsRec action_table[] =
389 {
390     {"XCursesButton",         (XtActionProc)XCursesButton},
391     {"XCursesKeyPress",       (XtActionProc)XCursesKeyPress},
392     {"XCursesPasteSelection", (XtActionProc)XCursesPasteSelection},
393     {"string",                (XtActionProc)XCursesHandleString}
394 };
395
396 static bool after_first_curses_request = FALSE;
397 static Pixel colors[MAX_COLORS + 2];
398 static bool vertical_cursor = FALSE;
399
400 #ifdef PDC_XIM
401 static XIM Xim = NULL;
402 static XIC Xic = NULL;
403 #endif
404
405 static const char *default_translations =
406 {
407     "<Key>: XCursesKeyPress() \n" \
408     "<KeyUp>: XCursesKeyPress() \n" \
409     "<BtnDown>: XCursesButton() \n" \
410     "<BtnUp>: XCursesButton() \n" \
411     "<BtnMotion>: XCursesButton()"
412 };
413
414 static int _to_utf8(char *outcode, chtype code)
415 {
416 #ifdef PDC_WIDE
417     if (code & A_ALTCHARSET && !(code & 0xff80))
418         code = acs_map[code & 0x7f];
419 #endif
420     code &= A_CHARTEXT;
421
422     if (code < 0x80)
423     {
424         outcode[0] = code;
425         return 1;
426     }
427     else
428         if (code < 0x800)
429         {
430             outcode[0] = ((code & 0x07c0) >> 6) | 0xc0;
431             outcode[1] = (code & 0x003f) | 0x80;
432             return 2;
433         }
434         else
435         {
436             outcode[0] = ((code & 0xf000) >> 12) | 0xe0;
437             outcode[1] = ((code & 0x0fc0) >> 6) | 0x80;
438             outcode[2] = (code & 0x003f) | 0x80;
439             return 3;
440         }
441 }
442
443 static int _from_utf8(wchar_t *pwc, const char *s, size_t n)
444 {
445     wchar_t key;
446     int i = -1;
447     const unsigned char *string;
448
449     if (!s || (n < 1))
450         return -1;
451
452     if (!*s)
453         return 0;
454
455     string = (const unsigned char *)s;
456
457     key = string[0];
458
459     /* Simplistic UTF-8 decoder -- only does the BMP, minimal validation */
460
461     if (key & 0x80)
462     {
463         if ((key & 0xe0) == 0xc0)
464         {
465             if (1 < n)
466             {
467                 key = ((key & 0x1f) << 6) | (string[1] & 0x3f);
468                 i = 2;
469             }
470         }
471         else if ((key & 0xe0) == 0xe0)
472         {
473             if (2 < n)
474             {
475                 key = ((key & 0x0f) << 12) |
476                       ((string[1] & 0x3f) << 6) | (string[2] & 0x3f);
477                 i = 3;
478             }
479         }
480     }
481     else
482         i = 1;
483
484     if (i)
485         *pwc = key;
486
487     return i;
488 }
489
490 #ifndef X_HAVE_UTF8_STRING
491 static Atom XA_UTF8_STRING(Display *dpy)
492 {
493     static AtomPtr p = NULL;
494
495     if (!p)
496         p = XmuMakeAtom("UTF8_STRING");
497
498     return XmuInternAtom(dpy, p);
499 }
500 #endif
501
502 signal_handler XCursesSetSignal(int signo, signal_handler action)
503 {
504 #if defined(SA_INTERRUPT) || defined(SA_RESTART)
505     struct sigaction sigact, osigact;
506
507     sigact.sa_handler = action;
508
509     sigact.sa_flags =
510 # ifdef SA_INTERRUPT
511 #  ifdef SA_RESTART
512         SA_INTERRUPT | SA_RESTART;
513 #  else
514         SA_INTERRUPT;
515 #  endif
516 # else  /* must be SA_RESTART */
517         SA_RESTART;
518 # endif
519     sigemptyset(&sigact.sa_mask);
520
521     if (sigaction(signo, &sigact, &osigact))
522         return SIG_ERR;
523
524     return osigact.sa_handler;
525
526 #else   /* not SA_INTERRUPT or SA_RESTART, use plain signal */
527     return signal(signo, action);
528 #endif
529 }
530
531 RETSIGTYPE XCursesSigwinchHandler(int signo)
532 {
533     PDC_LOG(("%s:XCursesSigwinchHandler() - called: SIGNO: %d\n",
534              XCLOGMSG, signo));
535
536     /* Patch by: Georg Fuchs, georg.fuchs@rz.uni-regensburg.de 
537        02-Feb-1999 */
538
539     SP->resized += 1;
540
541     /* Always trap SIGWINCH if the C library supports SIGWINCH */
542
543 #ifdef SIGWINCH
544     XCursesSetSignal(SIGWINCH, XCursesSigwinchHandler);
545 #endif
546 }
547
548 /* Convert character positions x and y to pixel positions, stored in 
549    xpos and ypos */
550
551 static void _make_xy(int x, int y, int *xpos, int *ypos)
552 {
553     *xpos = (x * font_width) + xc_app_data.borderWidth;
554     *ypos = xc_app_data.normalFont->ascent + (y * font_height) + 
555             xc_app_data.borderWidth;
556 }
557
558 /* Output a block of characters with common attributes */
559
560 static int _new_packet(chtype attr, bool rev, int len, int col, int row,
561 #ifdef PDC_WIDE
562                        XChar2b *text)
563 #else
564                        char *text)
565 #endif
566 {
567     GC gc;
568     int xpos, ypos;
569     short fore, back;
570
571     PDC_pair_content(PAIR_NUMBER(attr), &fore, &back);
572
573 #ifdef PDC_WIDE
574     text[len].byte1 = text[len].byte2 = 0;
575 #else
576     text[len] = '\0';
577 #endif
578
579     /* Specify the color table offsets */
580
581     fore |= (attr & A_BOLD) ? 8 : 0;
582     back |= (attr & A_BLINK) ? 8 : 0;
583
584     /* Reverse flag = highlighted selection XOR A_REVERSE set */
585
586     rev ^= !!(attr & A_REVERSE);
587
588     /* Determine which GC to use - normal or italic */
589
590     gc = (attr & A_ITALIC) ? italic_gc : normal_gc;
591
592     /* Draw it */
593
594     XSetForeground(XCURSESDISPLAY, gc, colors[rev ? back : fore]);
595     XSetBackground(XCURSESDISPLAY, gc, colors[rev ? fore : back]);
596
597     _make_xy(col, row, &xpos, &ypos);
598
599 #ifdef PDC_WIDE
600     XDrawImageString16(
601 #else
602     XDrawImageString(
603 #endif
604                      XCURSESDISPLAY, XCURSESWIN, gc, xpos, ypos, text, len);
605
606     /* Underline, etc. */
607
608     if (attr & (A_LEFTLINE|A_RIGHTLINE|A_UNDERLINE))
609     {
610         int k;
611
612         if (SP->line_color != -1)
613             XSetForeground(XCURSESDISPLAY, gc, colors[SP->line_color]);
614
615         if (attr & A_UNDERLINE)     /* UNDER */
616             XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc,
617                       xpos, ypos + 1, xpos + font_width * len, ypos + 1);
618
619         if (attr & A_LEFTLINE)      /* LEFT */
620             for (k = 0; k < len; k++)
621             {
622                 int x = xpos + font_width * k - 1;
623                 XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc,
624                           x, ypos - font_ascent, x, ypos + font_descent);
625             }
626
627         if (attr & A_RIGHTLINE)     /* RIGHT */
628             for (k = 0; k < len; k++)
629             {
630                 int x = xpos + font_width * (k + 1) - 1;
631                 XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc,
632                           x, ypos - font_ascent, x, ypos + font_descent);
633             }
634     }
635
636     PDC_LOG(("%s:_new_packet() - row: %d col: %d "
637              "num_cols: %d fore: %d back: %d text:<%s>\n",
638              XCLOGMSG, row, col, len, fore, back, text));
639
640     return OK;
641 }
642
643 /* The core display routine -- update one line of text */
644
645 static int _display_text(const chtype *ch, int row, int col,
646                          int num_cols, bool highlight)
647 {
648 #ifdef PDC_WIDE
649     XChar2b text[513];
650 #else
651     char text[513];
652 #endif
653     chtype old_attr, attr;
654     int i, j;
655
656     PDC_LOG(("%s:_display_text() - called: row: %d col: %d "
657              "num_cols: %d\n", XCLOGMSG, row, col, num_cols));
658
659     if (!num_cols)
660         return OK;
661
662     old_attr = *ch & A_ATTRIBUTES;
663
664     for (i = 0, j = 0; j < num_cols; j++)
665     {
666         chtype curr = ch[j];
667
668         attr = curr & A_ATTRIBUTES;
669
670 #ifdef CHTYPE_LONG
671         if (attr & A_ALTCHARSET && !(curr & 0xff80))
672         {
673             attr ^= A_ALTCHARSET;
674             curr = acs_map[curr & 0x7f];
675         }
676 #endif
677
678 #ifndef PDC_WIDE
679         /* Special handling for ACS_BLOCK */
680
681         if (!(curr & A_CHARTEXT))
682         {
683             curr |= ' ';
684             attr ^= A_REVERSE;
685         }
686 #endif
687         if (attr != old_attr)
688         {
689             if (_new_packet(old_attr, highlight, i, col, row, text) == ERR)
690                 return ERR;
691
692             old_attr = attr;
693             col += i;
694             i = 0;
695         }
696
697 #ifdef PDC_WIDE
698         text[i].byte1 = (curr & 0xff00) >> 8;
699         text[i++].byte2 = curr & 0x00ff;
700 #else
701         text[i++] = curr & 0xff;
702 #endif
703     }
704
705     return _new_packet(old_attr, highlight, i, col, row, text);
706 }
707
708 static void _get_gc(GC *gc, XFontStruct *font_info, int fore, int back)
709 {
710     XGCValues values;
711
712     /* Create default Graphics Context */
713
714     *gc = XCreateGC(XCURSESDISPLAY, XCURSESWIN, 0L, &values);
715
716     /* specify font */
717
718     XSetFont(XCURSESDISPLAY, *gc, font_info->fid);
719
720     XSetForeground(XCURSESDISPLAY, *gc, colors[fore]);
721     XSetBackground(XCURSESDISPLAY, *gc, colors[back]);
722 }
723
724 static void _initialize_colors(void)
725 {
726     colors[COLOR_BLACK]   = xc_app_data.colorBlack;
727     colors[COLOR_RED]     = xc_app_data.colorRed;
728     colors[COLOR_GREEN]   = xc_app_data.colorGreen;
729     colors[COLOR_YELLOW]  = xc_app_data.colorYellow;
730     colors[COLOR_BLUE]    = xc_app_data.colorBlue;
731     colors[COLOR_MAGENTA] = xc_app_data.colorMagenta;
732     colors[COLOR_CYAN]    = xc_app_data.colorCyan;
733     colors[COLOR_WHITE]   = xc_app_data.colorWhite;
734
735     colors[COLOR_BLACK + 8]   = xc_app_data.colorBoldBlack;
736     colors[COLOR_RED + 8]     = xc_app_data.colorBoldRed;
737     colors[COLOR_GREEN + 8]   = xc_app_data.colorBoldGreen;
738     colors[COLOR_YELLOW + 8]  = xc_app_data.colorBoldYellow;
739     colors[COLOR_BLUE + 8]    = xc_app_data.colorBoldBlue;
740     colors[COLOR_MAGENTA + 8] = xc_app_data.colorBoldMagenta;
741     colors[COLOR_CYAN + 8]    = xc_app_data.colorBoldCyan;
742     colors[COLOR_WHITE + 8]   = xc_app_data.colorBoldWhite;
743
744     colors[COLOR_CURSOR] = xc_app_data.cursorColor;
745     colors[COLOR_BORDER] = xc_app_data.borderColor;
746 }
747
748 static void _refresh_scrollbar(void)
749 {
750     XC_LOG(("_refresh_scrollbar() - called\n"));
751
752     if (SP->sb_on)
753     {
754         PDC_SCROLLBAR_TYPE total_y = SP->sb_total_y;
755         PDC_SCROLLBAR_TYPE total_x = SP->sb_total_x;
756
757         if (total_y)
758             XawScrollbarSetThumb(scrollVert,
759                 (PDC_SCROLLBAR_TYPE)(SP->sb_cur_y) / total_y,
760                 (PDC_SCROLLBAR_TYPE)(SP->sb_viewport_y) / total_y);
761
762         if (total_x)
763             XawScrollbarSetThumb(scrollHoriz,
764                 (PDC_SCROLLBAR_TYPE)(SP->sb_cur_x) / total_x,
765                 (PDC_SCROLLBAR_TYPE)(SP->sb_viewport_x) / total_x);
766     }
767 }
768
769 static void _set_cursor_color(chtype *ch, short *fore, short *back)
770 {
771     int attr;
772     short f, b;
773
774     attr = PAIR_NUMBER(*ch);
775
776     if (attr)
777     {
778         PDC_pair_content(attr, &f, &b);
779         *fore = 7 - (f % 8);
780         *back = 7 - (b % 8);
781     }
782     else
783     {
784         if (*ch & A_REVERSE)
785         {
786             *back = COLOR_BLACK;
787             *fore = COLOR_WHITE;
788         }
789         else
790         {
791             *back = COLOR_WHITE;
792             *fore = COLOR_BLACK;
793         }
794     }
795 }
796
797 static void _get_icon(void)
798 {
799     XIconSize *icon_size;
800     int size_count = 0;
801     Status rc;
802     unsigned char *bitmap_bits = NULL;
803     unsigned icon_bitmap_width = 0, icon_bitmap_height = 0,
804              file_bitmap_width = 0, file_bitmap_height = 0;
805
806     XC_LOG(("_get_icon() - called\n"));
807
808     icon_size = XAllocIconSize();
809
810     rc = XGetIconSizes(XtDisplay(topLevel),
811                        RootWindowOfScreen(XtScreen(topLevel)),
812                        &icon_size, &size_count);
813
814     /* if the WM can advise on icon sizes... */
815
816     if (rc && size_count)
817     {
818         int i, max_height = 0, max_width = 0;
819
820         PDC_LOG(("%s:size_count: %d rc: %d\n", XCLOGMSG, size_count, rc));
821
822         for (i = 0; i < size_count; i++)
823         {
824             if (icon_size[i].max_width > max_width)
825                 max_width = icon_size[i].max_width;
826             if (icon_size[i].max_height > max_height)
827                 max_height = icon_size[i].max_height;
828
829             PDC_LOG(("%s:min: %d %d\n", XCLOGMSG,
830                      icon_size[i].min_width, icon_size[i].min_height));
831
832             PDC_LOG(("%s:max: %d %d\n", XCLOGMSG,
833                      icon_size[i].max_width, icon_size[i].max_height));
834
835             PDC_LOG(("%s:inc: %d %d\n", XCLOGMSG,
836                      icon_size[i].width_inc, icon_size[i].height_inc));
837         }
838
839         if (max_width >= big_icon_width && max_height >= big_icon_height)
840         {
841             icon_bitmap_width = big_icon_width;
842             icon_bitmap_height = big_icon_height;
843             bitmap_bits = (unsigned char *)big_icon_bits;
844         }
845         else
846         {
847             icon_bitmap_width = little_icon_width;
848             icon_bitmap_height = little_icon_height;
849             bitmap_bits = (unsigned char *)little_icon_bits;
850         }
851
852     }
853     else  /* use small icon */
854     {
855         icon_bitmap_width = little_icon_width;
856         icon_bitmap_height = little_icon_height;
857         bitmap_bits = (unsigned char *)little_icon_bits;
858     }
859
860     XFree(icon_size);
861
862 #ifdef HAVE_XPM_H
863     if (xc_app_data.pixmap && xc_app_data.pixmap[0]) /* supplied pixmap */
864     {
865         XpmReadFileToPixmap(XtDisplay(topLevel),
866                             RootWindowOfScreen(XtScreen(topLevel)),
867                             (char *)xc_app_data.pixmap,
868                             &icon_pixmap, &icon_pixmap_mask, NULL);
869         return;
870     }
871 #endif
872
873     if (xc_app_data.bitmap && xc_app_data.bitmap[0]) /* supplied bitmap */
874     {
875         int x_hot = 0, y_hot = 0;
876
877         rc = XReadBitmapFile(XtDisplay(topLevel),
878                              RootWindowOfScreen(XtScreen(topLevel)),
879                              (char *)xc_app_data.bitmap,
880                              &file_bitmap_width, &file_bitmap_height,
881                              &icon_bitmap, &x_hot, &y_hot);
882
883         switch(rc)
884         {
885         case BitmapOpenFailed:
886             fprintf(stderr, "bitmap file %s: not found\n",
887                     xc_app_data.bitmap);
888             break;
889         case BitmapFileInvalid:
890             fprintf(stderr, "bitmap file %s: contents invalid\n",
891                     xc_app_data.bitmap);
892             break;
893         default:
894             return;
895         }
896     }
897
898     icon_bitmap = XCreateBitmapFromData(XtDisplay(topLevel),
899         RootWindowOfScreen(XtScreen(topLevel)),
900         (char *)bitmap_bits, icon_bitmap_width, icon_bitmap_height);
901 }
902
903 static void _draw_border(void)
904 {
905     /* Draw the border if required */
906
907     if (xc_app_data.borderWidth)
908         XDrawRectangle(XCURSESDISPLAY, XCURSESWIN, border_gc,
909                        xc_app_data.borderWidth / 2,
910                        xc_app_data.borderWidth / 2,
911                        window_width - xc_app_data.borderWidth,
912                        window_height - xc_app_data.borderWidth);
913 }
914
915 /* Redraw the entire screen */
916
917 static void _display_screen(void)
918 {
919     int row;
920
921     XC_LOG(("_display_screen() - called\n"));
922
923     for (row = 0; row < XCursesLINES; row++)
924     {
925         XC_get_line_lock(row);
926
927         _display_text((const chtype *)(Xcurscr + XCURSCR_Y_OFF(row)),
928                       row, 0, COLS, FALSE);
929
930         XC_release_line_lock(row);
931     }
932
933     _redraw_cursor();
934     _draw_border();
935 }
936
937 /* Draw changed portions of the screen */
938
939 static void _refresh_screen(void)
940 {
941     int row, start_col, num_cols;
942
943     XC_LOG(("_refresh_screen() - called\n"));
944
945     for (row = 0; row < XCursesLINES; row++)
946     {
947         num_cols = (int)*(Xcurscr + XCURSCR_LENGTH_OFF + row);
948
949         if (num_cols)
950         {
951             XC_get_line_lock(row);
952
953             start_col = (int)*(Xcurscr + XCURSCR_START_OFF + row);
954
955             _display_text((const chtype *)(Xcurscr + XCURSCR_Y_OFF(row) +
956                           (start_col * sizeof(chtype))), row, start_col, 
957                           num_cols, FALSE);
958
959             *(Xcurscr + XCURSCR_LENGTH_OFF + row) = 0;
960
961             XC_release_line_lock(row);
962         }
963     }
964
965     if (mouse_selection)
966         _selection_off();
967 }
968
969 static void _handle_expose(Widget w, XtPointer client_data, XEvent *event,
970                            Boolean *unused)
971 {
972     XC_LOG(("_handle_expose() - called\n"));
973
974     /* ignore all Exposes except last */
975
976     if (event->xexpose.count)
977         return;
978
979     if (after_first_curses_request && received_map_notify)
980         _display_screen();
981 }
982
983 static void _handle_nonmaskable(Widget w, XtPointer client_data, XEvent *event,
984                                 Boolean *unused)
985 {
986     XClientMessageEvent *client_event = (XClientMessageEvent *)event;
987
988     PDC_LOG(("%s:_handle_nonmaskable called: xc_otherpid %d event %d\n",
989              XCLOGMSG, xc_otherpid, event->type));
990
991     if (event->type == ClientMessage)
992     {
993         XC_LOG(("ClientMessage received\n"));
994
995         /* This code used to include handling of WM_SAVE_YOURSELF, but 
996            it resulted in continual failure of THE on my Toshiba laptop. 
997            Removed on 3-3-2001. Now only exits on WM_DELETE_WINDOW. */
998
999         if ((Atom)client_event->data.s[0] == wm_atom[0])
1000             _exit_process(0, SIGKILL, "");
1001     }
1002 }
1003
1004 static void XCursesKeyPress(Widget w, XEvent *event, String *params,
1005                             Cardinal *nparams)
1006 {
1007     enum { STATE_NORMAL, STATE_COMPOSE, STATE_CHAR };
1008
1009 #ifdef PDC_XIM
1010     Status status;
1011     wchar_t buffer[120];
1012 #else
1013     unsigned char buffer[120];
1014     XComposeStatus compose;
1015     static int compose_state = STATE_NORMAL;
1016     static int compose_index = 0;
1017     int char_idx = 0;
1018 #endif
1019     unsigned long key = 0;
1020     int buflen = 40;
1021     int i, count;
1022     unsigned long modifier = 0;
1023     bool key_code = FALSE;
1024
1025     XC_LOG(("XCursesKeyPress() - called\n"));
1026
1027     /* Handle modifier keys first; ignore other KeyReleases */
1028
1029     if (event->type == KeyRelease)
1030     {
1031         /* The keysym value was set by a previous call to this function 
1032            with a KeyPress event (or reset by the mouse event handler) */
1033
1034         if (SP->return_key_modifiers &&
1035 #ifndef PDC_XIM
1036             keysym != compose_key && 
1037 #endif
1038             IsModifierKey(keysym))
1039         {
1040             switch (keysym) {
1041             case XK_Shift_L:
1042                 key = KEY_SHIFT_L;
1043                 break;
1044             case XK_Shift_R:
1045                 key = KEY_SHIFT_R;
1046                 break;
1047             case XK_Control_L:
1048                 key = KEY_CONTROL_L;
1049                 break;
1050             case XK_Control_R:
1051                 key = KEY_CONTROL_R;
1052                 break;
1053             case XK_Alt_L:
1054                 key = KEY_ALT_L;
1055                 break;
1056             case XK_Alt_R:
1057                 key = KEY_ALT_R;
1058             }
1059
1060             if (key)
1061                 _send_key_to_curses(key, NULL, TRUE);
1062         }
1063
1064         return;
1065     }
1066
1067     buffer[0] = '\0';
1068
1069 #ifdef PDC_XIM
1070     count = XwcLookupString(Xic, &(event->xkey), buffer, buflen,
1071                             &keysym, &status);
1072 #else
1073     count = XLookupString(&(event->xkey), (char *)buffer, buflen,
1074                           &keysym, &compose);
1075 #endif
1076
1077     /* translate keysym into curses key code */
1078
1079     PDC_LOG(("%s:Key mask: %x\n", XCLOGMSG, event->xkey.state));
1080
1081 #ifdef PDCDEBUG
1082     for (i = 0; i < 4; i++)
1083         PDC_debug("%s:Keysym %x %d\n", XCLOGMSG,
1084                   XKeycodeToKeysym(XCURSESDISPLAY, event->xkey.keycode, i), i);
1085 #endif
1086
1087 #ifndef PDC_XIM
1088
1089     /* Check if the key just pressed is the user-specified compose 
1090        key; if it is, set the compose state and exit. */
1091
1092     if (keysym == compose_key)
1093     {
1094         chtype *ch;
1095         int xpos, ypos, save_visibility = SP->visibility;
1096         short fore = 0, back = 0;
1097
1098         /* Change the shape of the cursor to an outline rectangle to
1099            indicate we are in "compose" status */
1100
1101         SP->visibility = 0;
1102
1103         _redraw_cursor();
1104
1105         SP->visibility = save_visibility;
1106         _make_xy(SP->curscol, SP->cursrow, &xpos, &ypos);
1107
1108         ch = (chtype *)(Xcurscr + XCURSCR_Y_OFF(SP->cursrow) + 
1109              (SP->curscol * sizeof(chtype)));
1110
1111         _set_cursor_color(ch, &fore, &back);
1112
1113         XSetForeground(XCURSESDISPLAY, rect_cursor_gc, colors[back]);
1114
1115         XDrawRectangle(XCURSESDISPLAY, XCURSESWIN, rect_cursor_gc, 
1116                        xpos + 1, ypos - font_height + 
1117                        xc_app_data.normalFont->descent + 1, 
1118                        font_width - 2, font_height - 2);
1119
1120         compose_state = STATE_COMPOSE;
1121         return;
1122     }
1123
1124     switch (compose_state)
1125     {
1126     case STATE_COMPOSE:
1127         if (IsModifierKey(keysym))
1128             return;
1129
1130         if (event->xkey.state & compose_mask)
1131         {
1132             compose_state = STATE_NORMAL;
1133             _redraw_cursor();
1134             break;
1135         }
1136
1137         if (buffer[0] && count == 1)
1138             key = buffer[0];
1139
1140         compose_index = -1;
1141          
1142         for (i = 0; i < (int)strlen(compose_chars); i++)
1143             if (compose_chars[i] == key)
1144             {
1145                 compose_index = i;
1146                 break;
1147             }
1148
1149         if (compose_index == -1)
1150         {
1151             compose_state = STATE_NORMAL;
1152             compose_index = 0;
1153             _redraw_cursor();
1154             break;
1155         }
1156
1157         compose_state = STATE_CHAR;
1158         return;
1159
1160     case STATE_CHAR:
1161         if (IsModifierKey(keysym))
1162             return;
1163
1164         if (event->xkey.state & compose_mask)
1165         {
1166             compose_state = STATE_NORMAL;
1167             _redraw_cursor();
1168             break;
1169         }
1170
1171         if (buffer[0] && count == 1)
1172             key = buffer[0];
1173
1174         char_idx = -1;
1175
1176         for (i = 0; i < MAX_COMPOSE_CHARS; i++)
1177             if (compose_lookups[compose_index][i] == key)
1178             {
1179                 char_idx = i;
1180                 break;
1181             }
1182
1183         if (char_idx == -1)
1184         {
1185             compose_state = STATE_NORMAL;
1186             compose_index = 0;
1187             _redraw_cursor();
1188             break;
1189         }
1190
1191         _send_key_to_curses(compose_keys[compose_index][char_idx],
1192             NULL, FALSE);
1193
1194         compose_state = STATE_NORMAL;
1195         compose_index = 0;
1196          
1197         _redraw_cursor();
1198
1199         return;
1200     }
1201
1202 #endif /* PDC_XIM */
1203
1204     /* To get here we are procesing "normal" keys */
1205
1206     PDC_LOG(("%s:Keysym %x %d\n", XCLOGMSG,
1207              XKeycodeToKeysym(XCURSESDISPLAY, event->xkey.keycode, key), key));
1208
1209     if (SP->save_key_modifiers)
1210     {
1211         /* 0x10: usually, numlock modifier */
1212
1213         if (event->xkey.state & Mod2Mask)
1214             modifier |= PDC_KEY_MODIFIER_NUMLOCK;
1215
1216         /* 0x01: shift modifier */
1217
1218         if (event->xkey.state & ShiftMask)
1219             modifier |= PDC_KEY_MODIFIER_SHIFT;
1220
1221         /* 0x04: control modifier */
1222
1223         if (event->xkey.state & ControlMask)
1224             modifier |= PDC_KEY_MODIFIER_CONTROL;
1225
1226         /* 0x08: usually, alt modifier */
1227
1228         if (event->xkey.state & Mod1Mask)
1229             modifier |= PDC_KEY_MODIFIER_ALT;
1230     }
1231
1232     for (i = 0; key_table[i].keycode; i++)
1233     {
1234         if (key_table[i].keycode == keysym)
1235         {
1236             PDC_LOG(("%s:State %x\n", XCLOGMSG, event->xkey.state));
1237
1238             /* ControlMask: 0x04: control modifier
1239                Mod1Mask: 0x08: usually, alt modifier
1240                Mod2Mask: 0x10: usually, numlock modifier
1241                ShiftMask: 0x01: shift modifier */
1242
1243             if ((event->xkey.state & ShiftMask) ||
1244                 (key_table[i].numkeypad &&
1245                 (event->xkey.state & Mod2Mask)))
1246             {
1247                 key = key_table[i].shifted;
1248             }
1249             else if (event->xkey.state & ControlMask)
1250             {
1251                 key = key_table[i].control;
1252             }
1253             else if (event->xkey.state & Mod1Mask)
1254             {
1255                 key = key_table[i].alt;
1256             }
1257
1258             /* To get here, we ignore all other modifiers */
1259
1260             else
1261                 key = key_table[i].normal;
1262
1263             key_code = (key > 0x100);
1264             break;
1265         }
1266     }
1267
1268     if (!key && buffer[0] && count == 1)
1269         key = buffer[0];
1270
1271     PDC_LOG(("%s:Key: %s pressed - %x Mod: %x\n", XCLOGMSG,
1272              XKeysymToString(keysym), key, event->xkey.state));
1273
1274     /* Handle ALT letters and numbers */
1275
1276     if (event->xkey.state == Mod1Mask)
1277     {
1278         if (key >= 'A' && key <= 'Z')
1279         {
1280             key += ALT_A - 'A';
1281             key_code = TRUE;
1282         }
1283
1284         if (key >= 'a' && key <= 'z')
1285         {
1286             key += ALT_A - 'a';
1287             key_code = TRUE;
1288         }
1289
1290         if (key >= '0' && key <= '9')
1291         {
1292             key += ALT_0 - '0';
1293             key_code = TRUE;
1294         }
1295     }
1296
1297     /* After all that, send the key back to the application if is 
1298        NOT zero. */
1299
1300     if (key)
1301     {
1302         key |= (modifier << 24);
1303
1304         _send_key_to_curses(key, NULL, key_code);
1305     }
1306 }
1307
1308 static void XCursesHandleString(Widget w, XEvent *event, String *params,
1309                                 Cardinal *nparams)
1310 {
1311     unsigned char *ptr;
1312
1313     if (*nparams != 1)
1314         return;
1315
1316     ptr = (unsigned char *)*params;
1317
1318     if (ptr[0] == '0' && ptr[1] == 'x' && ptr[2] != '\0')
1319     {    
1320         unsigned char c;
1321         unsigned long total = 0;
1322
1323         for (ptr += 2; (c = tolower(*ptr)); ptr++)
1324         {
1325             total <<= 4;
1326
1327             if (c >= '0' && c <= '9')
1328                 total += c - '0';
1329             else
1330                 if (c >= 'a' && c <= 'f')
1331                     total += c - ('a' - 10);
1332                 else
1333                     break;
1334         }
1335
1336         if (c == '\0')
1337             _send_key_to_curses(total, NULL, FALSE);
1338     }
1339     else
1340         for (; *ptr; ptr++)
1341             _send_key_to_curses((unsigned long)*ptr, NULL, FALSE);
1342 }
1343
1344 static void _paste_string(Widget w, XtPointer data, Atom *selection, Atom *type,
1345                           XtPointer value, unsigned long *length, int *format)
1346 {
1347     unsigned long i, key;
1348     unsigned char *string = value;
1349
1350     XC_LOG(("_paste_string() - called\n"));
1351
1352     if (!*type || !*length || !string)
1353         return;
1354
1355     for (i = 0; string[i] && (i < (*length)); i++)
1356     {
1357         key = string[i];
1358
1359         if (key == 10)      /* new line - convert to ^M */
1360             key = 13;
1361
1362         _send_key_to_curses(key, NULL, FALSE);
1363     }
1364
1365     XtFree(value);
1366 }
1367
1368 static void _paste_utf8(Widget w, XtPointer event, Atom *selection, Atom *type,
1369                         XtPointer value, unsigned long *length, int *format)
1370 {
1371     wchar_t key;
1372     size_t i = 0, len;
1373     char *string = value;
1374
1375     XC_LOG(("_paste_utf8() - called\n"));
1376
1377     if (!*type || !*length)
1378     {
1379         XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, _paste_string, 
1380                             event, ((XButtonEvent *)event)->time);
1381         return;
1382     }
1383
1384     len = *length;
1385
1386     if (!string)
1387         return;
1388
1389     while (string[i] && (i < len))
1390     {
1391         int retval = _from_utf8(&key, string + i, len - i);
1392
1393         if (retval < 1)
1394             return;
1395
1396         if (key == 10)      /* new line - convert to ^M */
1397             key = 13;
1398
1399         _send_key_to_curses(key, NULL, FALSE);
1400
1401         i += retval;
1402     }
1403
1404     XtFree(value);
1405 }
1406
1407 static void XCursesPasteSelection(Widget w, XButtonEvent *button_event)
1408 {
1409     XC_LOG(("XCursesPasteSelection() - called\n"));
1410
1411     XtGetSelectionValue(w, XA_PRIMARY, XA_UTF8_STRING(XtDisplay(w)),
1412                         _paste_utf8, (XtPointer)button_event,
1413                         button_event->time);
1414 }
1415
1416 static Boolean _convert_proc(Widget w, Atom *selection, Atom *target,
1417                              Atom *type_return, XtPointer *value_return,
1418                              unsigned long *length_return, int *format_return)
1419 {
1420     XC_LOG(("_convert_proc() - called\n"));
1421
1422     if (*target == XA_TARGETS(XtDisplay(topLevel)))
1423     {
1424         XSelectionRequestEvent *req = XtGetSelectionRequest(w, 
1425             *selection, (XtRequestId)NULL);
1426
1427         Atom *targetP;
1428         XPointer std_targets;
1429         unsigned long std_length;
1430
1431         XmuConvertStandardSelection(topLevel, req->time, selection, 
1432                                     target, type_return, &std_targets,
1433                                     &std_length, format_return);
1434
1435         *length_return = std_length + 2;
1436         *value_return = XtMalloc(sizeof(Atom) * (*length_return));
1437
1438         targetP = *(Atom**)value_return;
1439         *targetP++ = XA_STRING;
1440         *targetP++ = XA_UTF8_STRING(XtDisplay(topLevel));
1441
1442         memmove((void *)targetP, (const void *)std_targets,
1443                 sizeof(Atom) * std_length);
1444
1445         XtFree((char *)std_targets);
1446         *type_return = XA_ATOM;
1447         *format_return = sizeof(Atom) * 8;
1448
1449         return True;
1450     }
1451     else if (*target == XA_UTF8_STRING(XtDisplay(topLevel)) ||
1452              *target == XA_STRING)
1453     {
1454         bool utf8 = !(*target == XA_STRING);
1455         char *data = XtMalloc(tmpsel_length * 3 + 1);
1456         chtype *tmp = tmpsel;
1457         int ret_length = 0;
1458
1459         if (utf8)
1460         {
1461             while (*tmp)
1462                 ret_length += _to_utf8(data + ret_length, *tmp++);
1463         }
1464         else
1465             while (*tmp)
1466                 data[ret_length++] = *tmp++ & 0xff;
1467
1468         data[ret_length++] = '\0';
1469
1470         *value_return = data;
1471         *length_return = ret_length;
1472         *format_return = 8;
1473         *type_return = *target;
1474
1475         return True;
1476     }
1477     else
1478         return XmuConvertStandardSelection(topLevel, CurrentTime, 
1479             selection, target, type_return, (XPointer*)value_return,
1480             length_return, format_return);
1481 }
1482
1483 static void _lose_ownership(Widget w, Atom *type)
1484 {
1485     XC_LOG(("_lose_ownership() - called\n"));
1486
1487     if (tmpsel)
1488         free(tmpsel);
1489
1490     tmpsel = NULL;
1491     tmpsel_length = 0;
1492     _selection_off();
1493 }
1494
1495 static void _show_selection(int start_x, int start_y, int end_x, int end_y,
1496                             bool highlight)
1497 {
1498     int i, num_cols, start_col, row;
1499
1500     PDC_LOG(("%s:_show_selection() - called StartX: %d StartY: %d "
1501              "EndX: %d EndY: %d Highlight: %d\n", XCLOGMSG,
1502              start_x, start_y, end_x, end_y, highlight));
1503
1504     for (i = 0; i < end_y - start_y + 1; i++)
1505     {
1506         if (start_y == end_y)       /* only one line */
1507         {
1508             start_col = start_x;
1509             num_cols = end_x - start_x + 1;
1510             row = start_y;
1511         }
1512         else if (!i)            /* first line */
1513         {
1514             start_col = start_x;
1515             num_cols = COLS - start_x;
1516             row = start_y;
1517         }
1518         else if (start_y + i == end_y)  /* last line */
1519         {
1520             start_col = 0;
1521             num_cols = end_x + 1;
1522             row = end_y;
1523         }
1524         else                /* full line */
1525         {
1526             start_col = 0;
1527             num_cols = COLS;
1528             row = start_y + i;
1529         }
1530
1531         XC_get_line_lock(row);
1532
1533         _display_text((const chtype *)(Xcurscr + XCURSCR_Y_OFF(row) +
1534                       (start_col * sizeof(chtype))), row, start_col,
1535                       num_cols, highlight);
1536
1537         XC_release_line_lock(row);
1538     }
1539 }
1540
1541 static void _selection_off(void)
1542 {
1543     XC_LOG(("_selection_off() - called\n"));
1544
1545     _display_screen();
1546
1547     selection_start_x = selection_start_y = selection_end_x = 
1548         selection_end_y = 0;
1549
1550     mouse_selection = FALSE;
1551 }
1552
1553 static void _selection_on(int x, int y)
1554 {
1555     XC_LOG(("_selection_on() - called\n"));
1556
1557     selection_start_x = selection_end_x = x;
1558     selection_start_y = selection_end_y = y;
1559 }
1560
1561 static void _selection_extend(int x, int y)
1562 {
1563     int temp, current_start, current_end, current_start_x, 
1564         current_end_x, current_start_y, current_end_y, new_start, 
1565         new_end, new_start_x, new_end_x, new_start_y, new_end_y;
1566
1567     XC_LOG(("_selection_extend() - called\n"));
1568
1569     mouse_selection = TRUE;
1570
1571     /* convert x/y coordinates into start/stop */
1572
1573     current_start = (selection_start_y * COLS) + selection_start_x;
1574     current_end = (selection_end_y * COLS) + selection_end_x;
1575
1576     if (current_start > current_end)
1577     {
1578         current_start_x = selection_end_x;
1579         current_start_y = selection_end_y;
1580         current_end_x = selection_start_x;
1581         current_end_y = selection_start_y;
1582         temp = current_start;
1583         current_start = current_end;
1584         current_end = temp;
1585     }
1586     else
1587     {
1588         current_end_x = selection_end_x;
1589         current_end_y = selection_end_y;
1590         current_start_x = selection_start_x;
1591         current_start_y = selection_start_y;
1592     }
1593
1594     /* Now we have the current selection as a linear expression. 
1595        Convert the new position to a linear expression. */
1596
1597     selection_end_x = x;
1598     selection_end_y = y;
1599
1600     /* convert x/y coordinates into start/stop */
1601
1602     new_start = (selection_start_y * COLS) + selection_start_x;
1603     new_end = (selection_end_y * COLS) + selection_end_x;
1604
1605     if (new_start > new_end)
1606     {
1607         new_start_x = selection_end_x;
1608         new_start_y = selection_end_y;
1609         new_end_x = selection_start_x;
1610         new_end_y = selection_start_y;
1611         temp = new_start;
1612         new_start = new_end;
1613         new_end = temp;
1614     }
1615     else
1616     {
1617         new_end_x = selection_end_x;
1618         new_end_y = selection_end_y;
1619         new_start_x = selection_start_x;
1620         new_start_y = selection_start_y;
1621     }
1622
1623     if (new_end > current_end)
1624         _show_selection(current_end_x, current_end_y, new_end_x, 
1625                         new_end_y, TRUE);
1626     else if (new_end < current_end)
1627         _show_selection(new_end_x, new_end_y, current_end_x, 
1628                         current_end_y, FALSE);
1629     else if (new_start < current_start)
1630         _show_selection(new_start_x, new_start_y, current_start_x, 
1631                         current_start_y, TRUE);
1632     else if (new_start > current_start)
1633         _show_selection(current_start_x, current_start_y, 
1634                         new_start_x, new_start_y, FALSE);
1635     else
1636         _show_selection(current_start_x, current_start_y, 
1637                         new_start_x, new_start_y, TRUE);
1638 }
1639
1640 static void _selection_set(void)
1641 {
1642     int i, j, start, end, start_x, end_x, start_y, end_y, num_cols, 
1643         start_col, row, num_chars, ch, last_nonblank, length, newlen;
1644     chtype *ptr = NULL;
1645
1646     XC_LOG(("_selection_set() - called\n"));
1647
1648     /* convert x/y coordinates into start/stop */
1649
1650     start = (selection_start_y * COLS) + selection_start_x;
1651     end = (selection_end_y * COLS) + selection_end_x;
1652
1653     if (start == end)
1654     {
1655         if (tmpsel)
1656             free(tmpsel);
1657
1658         tmpsel = NULL;
1659         tmpsel_length = 0;
1660
1661         return;
1662     }
1663
1664     if (start > end)
1665     {
1666         start_x = selection_end_x;
1667         start_y = selection_end_y;
1668         end_x = selection_start_x;
1669         end_y = selection_start_y;
1670         length = start - end + 1;
1671     }
1672     else
1673     {
1674         end_x = selection_end_x;
1675         end_y = selection_end_y;
1676         start_x = selection_start_x;
1677         start_y = selection_start_y;
1678         length = end - start + 1;
1679     }
1680
1681     newlen = length + end_y - start_y + 2;
1682
1683     if (length > (int)tmpsel_length)
1684     {
1685         if (!tmpsel_length)
1686             tmpsel = malloc(newlen * sizeof(chtype));
1687         else
1688             tmpsel = realloc(tmpsel, newlen * sizeof(chtype));
1689     }
1690
1691     if (!tmpsel)
1692     {
1693         tmpsel_length = 0;
1694         return;
1695     }
1696
1697     tmpsel_length = length;
1698     num_chars = 0;
1699
1700     for (i = 0; i < end_y - start_y + 1; i++)
1701     {
1702
1703         if (start_y == end_y)       /* only one line */
1704         {
1705             start_col = start_x;
1706             num_cols = end_x - start_x + 1;
1707             row = start_y;
1708         }
1709         else if (!i)            /* first line */
1710         {
1711             start_col = start_x;
1712             num_cols = COLS - start_x;
1713             row = start_y;
1714         }
1715         else if (start_y + i == end_y)  /* last line */
1716         {
1717             start_col = 0;
1718             num_cols = end_x + 1;
1719             row = end_y;
1720         }
1721         else                /* full line */
1722         {
1723             start_col = 0;
1724             num_cols = COLS;
1725             row = start_y + i;
1726         }
1727
1728         XC_get_line_lock(row);
1729
1730         ptr = (chtype *)(Xcurscr + XCURSCR_Y_OFF(row) +
1731               start_col * sizeof(chtype));
1732
1733         if (i < end_y - start_y)
1734         {
1735             last_nonblank = 0;
1736
1737             for (j = 0; j < num_cols; j++)
1738             {
1739                 ch = (int)(ptr[j] & A_CHARTEXT);
1740                 if (ch != (int)' ')
1741                     last_nonblank = j;
1742             }
1743         }
1744         else
1745             last_nonblank = num_cols - 1;
1746
1747         for (j = 0; j <= last_nonblank; j++)
1748             tmpsel[num_chars++] = ptr[j];
1749
1750         XC_release_line_lock(row);
1751
1752         if (i < end_y - start_y)
1753             tmpsel[num_chars++] = '\n';
1754     }
1755
1756     tmpsel[num_chars] = '\0';
1757     tmpsel_length = num_chars;
1758 }
1759
1760 static void _display_cursor(int old_row, int old_x, int new_row, int new_x)
1761 {
1762     int xpos, ypos, i;
1763     chtype *ch;
1764     short fore = 0, back = 0;
1765
1766     PDC_LOG(("%s:_display_cursor() - draw char at row: %d col %d\n",
1767              XCLOGMSG, old_row, old_x));
1768
1769     /* if the cursor position is outside the boundary of the screen, 
1770        ignore the request */
1771
1772     if (old_row >= XCursesLINES || old_x >= COLS ||
1773         new_row >= XCursesLINES || new_x >= COLS)
1774         return;
1775
1776     /* display the character at the current cursor position */
1777
1778     PDC_LOG(("%s:_display_cursor() - draw char at row: %d col %d\n",
1779              XCLOGMSG, old_row, old_x));
1780
1781     _display_text((const chtype *)(Xcurscr + (XCURSCR_Y_OFF(old_row) +
1782                   (old_x * sizeof(chtype)))), old_row, old_x, 1, FALSE);
1783
1784     /* display the cursor at the new cursor position */
1785
1786     if (!SP->visibility)
1787         return;     /* cursor not displayed, no more to do */
1788
1789     _make_xy(new_x, new_row, &xpos, &ypos);
1790
1791     ch = (chtype *)(Xcurscr + XCURSCR_Y_OFF(new_row) + new_x * sizeof(chtype));
1792
1793     _set_cursor_color(ch, &fore, &back);
1794
1795     if (vertical_cursor)
1796     {
1797         XSetForeground(XCURSESDISPLAY, rect_cursor_gc, colors[back]);
1798
1799         for (i = 1; i <= SP->visibility; i++)
1800             XDrawLine(XCURSESDISPLAY, XCURSESWIN, rect_cursor_gc,
1801                       xpos + i, ypos - xc_app_data.normalFont->ascent,
1802                       xpos + i, ypos - xc_app_data.normalFont->ascent + 
1803                       font_height - 1);
1804     }
1805     else
1806     {
1807         if (SP->visibility == 1)
1808         {
1809             /* cursor visibility normal */
1810
1811             XSetForeground(XCURSESDISPLAY, rect_cursor_gc, colors[back]);
1812
1813             for (i = 0; i < xc_app_data.normalFont->descent + 2; i++)
1814                 XDrawLine(XCURSESDISPLAY, XCURSESWIN, rect_cursor_gc,
1815                           xpos, ypos - 2 + i, xpos + font_width, ypos - 2 + i);
1816         }
1817         else
1818         {
1819             /* cursor visibility high */
1820 #ifdef PDC_WIDE
1821             XChar2b buf[2];
1822
1823             buf[0].byte1 = (*ch & 0xff00) >> 8;
1824             buf[0].byte2 = *ch & 0x00ff;
1825
1826             buf[1].byte1 = buf[1].byte2 = 0;
1827 #else
1828             char buf[2];
1829
1830             buf[0] = *ch & 0xff;
1831             buf[1] = '\0';
1832 #endif
1833             XSetForeground(XCURSESDISPLAY, block_cursor_gc, colors[fore]);
1834             XSetBackground(XCURSESDISPLAY, block_cursor_gc, colors[back]);
1835 #ifdef PDC_WIDE
1836             XDrawImageString16(
1837 #else
1838             XDrawImageString(
1839 #endif
1840                              XCURSESDISPLAY, XCURSESWIN, block_cursor_gc,
1841                              xpos, ypos, buf, 1);
1842         }
1843     }
1844
1845     PDC_LOG(("%s:_display_cursor() - draw cursor at row %d col %d\n",
1846              XCLOGMSG, new_row, new_x));
1847 }
1848
1849 static void _redraw_cursor(void)
1850 {
1851     _display_cursor(SP->cursrow, SP->curscol, SP->cursrow, SP->curscol);
1852 }
1853
1854 static void _handle_enter_leave(Widget w, XtPointer client_data,
1855                                 XEvent *event, Boolean *unused)
1856 {
1857     XC_LOG(("_handle_enter_leave called\n"));
1858
1859     switch(event->type)
1860     {
1861     case EnterNotify:
1862         XC_LOG(("EnterNotify received\n"));
1863
1864         window_entered = TRUE;
1865         break;
1866
1867     case LeaveNotify:
1868         XC_LOG(("LeaveNotify received\n"));
1869
1870         window_entered = FALSE;
1871
1872         /* Display the cursor so it stays on while the window is 
1873            not current */
1874
1875         _redraw_cursor();
1876         break;
1877
1878     default:
1879         PDC_LOG(("%s:_handle_enter_leave - unknown event %d\n",
1880                  XCLOGMSG, event->type));
1881     }
1882 }
1883
1884 static void _send_key_to_curses(unsigned long key, MOUSE_STATUS *ms,
1885                                 bool key_code)
1886 {
1887     PDC_LOG(("%s:_send_key_to_curses() - called: sending %d\n",
1888              XCLOGMSG, key));
1889
1890     SP->key_code = key_code;
1891
1892     if (XC_write_socket(xc_key_sock, &key, sizeof(unsigned long)) < 0)
1893         _exit_process(1, SIGKILL, "exiting from _send_key_to_curses");
1894
1895     if (ms)
1896     {
1897         MOUSE_LOG(("%s:writing mouse stuff\n", XCLOGMSG));
1898
1899         if (XC_write_socket(xc_key_sock, ms, sizeof(MOUSE_STATUS)) < 0)
1900             _exit_process(1, SIGKILL, "exiting from _send_key_to_curses");
1901     }
1902 }
1903
1904 static void _blink_cursor(XtPointer unused, XtIntervalId *id)
1905 {
1906     XC_LOG(("_blink_cursor() - called:\n"));
1907
1908     if (window_entered)
1909     {
1910         if (visible_cursor)
1911         {
1912             /* Cursor currently ON, turn it off */
1913
1914             int save_visibility = SP->visibility;
1915             SP->visibility = 0;
1916             _redraw_cursor();
1917             SP->visibility = save_visibility;
1918             visible_cursor = FALSE;
1919         }
1920         else
1921         {
1922             /* Cursor currently OFF, turn it on */
1923
1924             _redraw_cursor();
1925             visible_cursor = TRUE;
1926         }
1927     }
1928
1929     XtAppAddTimeOut(app_context, xc_app_data.cursorBlinkRate,
1930                     _blink_cursor, NULL);
1931 }
1932
1933 static void XCursesButton(Widget w, XEvent *event, String *params,
1934                           Cardinal *nparams)
1935 {
1936     int button_no;
1937     static int last_button_no = 0;
1938     static Time last_button_press_time = 0;
1939     MOUSE_STATUS save_mouse_status;
1940     bool send_key = TRUE;
1941     static bool remove_release;
1942     static bool handle_real_release;
1943
1944     XC_LOG(("XCursesButton() - called\n"));
1945
1946     keysym = 0; /* suppress any modifier key return */
1947
1948     save_mouse_status = Mouse_status;
1949     button_no = event->xbutton.button;
1950
1951     /* It appears that under X11R6 (at least on Linux), that an
1952        event_type of ButtonMotion does not include the mouse button in
1953        the event. The following code is designed to cater for this
1954        situation. */
1955
1956     if (!button_no)
1957         button_no = last_button_no;
1958
1959     last_button_no = button_no;
1960
1961     Mouse_status.changes = 0;
1962
1963     switch(event->type)
1964     {
1965     case ButtonPress:
1966         /* Handle button 4 and 5, which are normally mapped to the wheel
1967            mouse scroll up and down */
1968
1969         if (button_no == 4 || button_no == 5)
1970         {
1971             /* Send the KEY_MOUSE to curses program */
1972
1973             memset(&Mouse_status, 0, sizeof(Mouse_status));
1974
1975             Mouse_status.changes = (button_no == 5) ? 
1976                 PDC_MOUSE_WHEEL_DOWN : PDC_MOUSE_WHEEL_UP;
1977
1978             MOUSE_X_POS = MOUSE_Y_POS = -1;
1979             _send_key_to_curses(KEY_MOUSE, &Mouse_status, TRUE);
1980             remove_release = TRUE;
1981
1982             return;
1983         }
1984
1985         if (button_no == 2 &&
1986             (!SP->_trap_mbe || (event->xbutton.state & ShiftMask)))
1987         {
1988             XCursesPasteSelection(drawing, (XButtonEvent *)event);
1989             remove_release = TRUE;
1990
1991             return;
1992         }
1993
1994         remove_release = False;
1995         handle_real_release = False;
1996
1997         MOUSE_LOG(("\nButtonPress\n"));
1998
1999         if ((event->xbutton.time - last_button_press_time) <
2000             xc_app_data.doubleClickPeriod)
2001         {
2002             MOUSE_X_POS = save_mouse_status.x;
2003             MOUSE_Y_POS = save_mouse_status.y;
2004             BUTTON_STATUS(button_no) = BUTTON_DOUBLE_CLICKED;
2005
2006             _selection_off();
2007             remove_release = True;
2008         }
2009         else
2010         {
2011             napms(SP->mouse_wait);
2012             event->type = ButtonRelease;
2013             XSendEvent(event->xbutton.display, event->xbutton.window,
2014                        True, 0, event);
2015             last_button_press_time = event->xbutton.time;
2016
2017             return;
2018         }
2019
2020         last_button_press_time = event->xbutton.time;
2021         break;
2022
2023     case MotionNotify:
2024         MOUSE_LOG(("\nMotionNotify: y: %d x: %d Width: %d "
2025                    "Height: %d\n", event->xbutton.y, event->xbutton.x, 
2026                    font_width, font_height));
2027
2028         MOUSE_X_POS = (event->xbutton.x - xc_app_data.borderWidth) /
2029                       font_width;
2030         MOUSE_Y_POS = (event->xbutton.y - xc_app_data.borderWidth) /
2031                       font_height;
2032
2033         if (button_no == 1 &&
2034             (!SP->_trap_mbe || (event->xbutton.state & ShiftMask)))
2035         {
2036             _selection_extend(MOUSE_X_POS, MOUSE_Y_POS);
2037             send_key = FALSE;
2038         }
2039         else
2040             _selection_off();
2041
2042         /* Throw away mouse movements if they are in the same character 
2043            position as the last mouse event, or if we are currently in 
2044            the middle of a double click event. */
2045
2046         if ((MOUSE_X_POS == save_mouse_status.x &&
2047              MOUSE_Y_POS == save_mouse_status.y) || 
2048              save_mouse_status.button[button_no - 1] == BUTTON_DOUBLE_CLICKED)
2049         {
2050             send_key = FALSE;
2051             break;
2052         }
2053
2054         Mouse_status.changes |= PDC_MOUSE_MOVED;
2055         break;
2056
2057     case ButtonRelease:
2058         if (remove_release)
2059         {
2060             MOUSE_LOG(("Release at: %ld - removed\n", event->xbutton.time));
2061             return;
2062         }
2063         else
2064         {
2065             MOUSE_X_POS = (event->xbutton.x - xc_app_data.borderWidth) /
2066                           font_width;
2067             MOUSE_Y_POS = (event->xbutton.y - xc_app_data.borderWidth) /
2068                           font_height;
2069
2070             if (!handle_real_release)
2071             {
2072                 if ((event->xbutton.time - last_button_press_time) <
2073                     SP->mouse_wait && 
2074                     (event->xbutton.time != last_button_press_time))
2075                 {
2076                     /* The "real" release was shorter than usleep() time;
2077                        therefore generate a click event */
2078
2079                     MOUSE_LOG(("Release at: %ld - click\n",
2080                                event->xbutton.time));
2081
2082                     BUTTON_STATUS(button_no) = BUTTON_CLICKED;
2083
2084                     if (button_no == 1 && mouse_selection &&
2085                         (!SP->_trap_mbe || (event->xbutton.state & ShiftMask)))
2086                     {
2087                         send_key = FALSE;
2088
2089                         if (XtOwnSelection(topLevel, XA_PRIMARY, 
2090                                            event->xbutton.time, _convert_proc,
2091                                            _lose_ownership, NULL) == False)
2092                             _selection_off();
2093                     }
2094                     else
2095                         _selection_off();
2096
2097                     /* Ensure the "pseudo" release event is ignored */
2098
2099                     remove_release = True;
2100                     handle_real_release = False;
2101                     break;
2102                 }
2103                 else
2104                 {
2105                     /* Button release longer than usleep() time; 
2106                        therefore generate a press and wait for the real 
2107                        release to occur later. */
2108
2109                     MOUSE_LOG(("Generated Release at: %ld - "
2110                                "press & release\n", event->xbutton.time));
2111
2112                     BUTTON_STATUS(button_no) = BUTTON_PRESSED;
2113
2114                     if (button_no == 1 &&
2115                         (!SP->_trap_mbe || (event->xbutton.state & ShiftMask)))
2116                     {
2117                         _selection_off();
2118                         _selection_on(MOUSE_X_POS, MOUSE_Y_POS);
2119                     }
2120
2121                     handle_real_release = True;
2122                     break;
2123                 }
2124             }
2125             else
2126             {
2127                 MOUSE_LOG(("Release at: %ld - released\n",
2128                            event->xbutton.time));
2129             }
2130         }
2131
2132         MOUSE_LOG(("\nButtonRelease\n"));
2133
2134         BUTTON_STATUS(button_no) = BUTTON_RELEASED;
2135
2136         if (button_no == 1 && mouse_selection &&
2137             (!SP->_trap_mbe || (event->xbutton.state & ShiftMask)))
2138         {
2139             send_key = FALSE;
2140
2141             if (XtOwnSelection(topLevel, XA_PRIMARY, 
2142                                event->xbutton.time, _convert_proc, 
2143                                _lose_ownership, NULL) == False)
2144                 _selection_off();
2145
2146             _selection_set();
2147         }
2148         else
2149             _selection_off();
2150
2151         break;
2152     }
2153
2154     /* Set up the mouse status fields in preparation for sending */
2155
2156     Mouse_status.changes |= 1 << (button_no - 1);
2157
2158     if (Mouse_status.changes & PDC_MOUSE_MOVED &&
2159         BUTTON_STATUS(button_no) == BUTTON_PRESSED)
2160         BUTTON_STATUS(button_no) = BUTTON_MOVED;
2161
2162     if (event->xbutton.state & ShiftMask)
2163         BUTTON_STATUS(button_no) |= BUTTON_SHIFT;
2164     if (event->xbutton.state & ControlMask)
2165         BUTTON_STATUS(button_no) |= BUTTON_CONTROL;
2166     if (event->xbutton.state & Mod1Mask)
2167         BUTTON_STATUS(button_no) |= BUTTON_ALT;
2168
2169     /* If we are ignoring the event, or the mouse position is outside
2170        the bounds of the screen (because of the border), return here */
2171
2172     MOUSE_LOG(("Button: %d x: %d y: %d Button status: %x "
2173         "Mouse status: %x\n", button_no, MOUSE_X_POS, MOUSE_Y_POS,
2174         BUTTON_STATUS(button_no), Mouse_status.changes));
2175
2176     MOUSE_LOG(("Send: %d Button1: %x Button2: %x Button3: %x %d %d\n",
2177         send_key, BUTTON_STATUS(1), BUTTON_STATUS(2), 
2178         BUTTON_STATUS(3), XCursesLINES, XCursesCOLS));
2179
2180     if (!send_key || MOUSE_X_POS < 0 || MOUSE_X_POS >= XCursesCOLS ||
2181         MOUSE_Y_POS < 0 || MOUSE_Y_POS >= XCursesLINES)
2182         return;
2183
2184     /* Send the KEY_MOUSE to curses program */
2185
2186     _send_key_to_curses(KEY_MOUSE, &Mouse_status, TRUE);
2187 }
2188
2189 static void _scroll_up_down(Widget w, XtPointer client_data,
2190                             XtPointer call_data)
2191 {
2192     int pixels = (long) call_data;
2193     int total_y = SP->sb_total_y * font_height;
2194     int viewport_y = SP->sb_viewport_y * font_height;
2195     int cur_y = SP->sb_cur_y * font_height;
2196
2197     /* When pixels is negative, right button pressed, move data down,
2198        thumb moves up.  Otherwise, left button pressed, pixels positive,
2199        move data up, thumb down. */
2200
2201     cur_y += pixels;
2202
2203     /* limit panning to size of overall */
2204
2205     if (cur_y < 0)
2206         cur_y = 0;
2207     else
2208         if (cur_y > (total_y - viewport_y))
2209             cur_y = total_y - viewport_y;
2210
2211     SP->sb_cur_y = cur_y / font_height;
2212
2213     XawScrollbarSetThumb(w, (double)((double)cur_y / (double)total_y),
2214                          (double)((double)viewport_y / (double)total_y));
2215
2216     /* Send a key: if pixels negative, send KEY_SCROLL_DOWN */
2217
2218     _send_key_to_curses(KEY_SF, NULL, TRUE);
2219 }
2220
2221 static void _scroll_left_right(Widget w, XtPointer client_data,
2222                                XtPointer call_data)
2223 {
2224     int pixels = (long) call_data;
2225     int total_x = SP->sb_total_x * font_width;
2226     int viewport_x = SP->sb_viewport_x * font_width;
2227     int cur_x = SP->sb_cur_x * font_width;
2228
2229     cur_x += pixels;
2230
2231     /* limit panning to size of overall */
2232
2233     if (cur_x < 0)
2234         cur_x = 0;
2235     else
2236         if (cur_x > (total_x - viewport_x))
2237             cur_x = total_x - viewport_x;
2238
2239     SP->sb_cur_x = cur_x / font_width;
2240
2241     XawScrollbarSetThumb(w, (double)((double)cur_x / (double)total_x),
2242                          (double)((double)viewport_x / (double)total_x));
2243
2244     _send_key_to_curses(KEY_SR, NULL, TRUE);
2245 }
2246
2247 static void _thumb_up_down(Widget w, XtPointer client_data,
2248                            XtPointer call_data)
2249 {
2250     double percent = *(double *) call_data;
2251     double total_y = (double)SP->sb_total_y;
2252     double viewport_y = (double)SP->sb_viewport_y;
2253     int cur_y = SP->sb_cur_y;
2254
2255     /* If the size of the viewport is > overall area simply return, 
2256        as no scrolling is permitted. */
2257
2258     if (SP->sb_viewport_y >= SP->sb_total_y)
2259         return;
2260
2261     if ((SP->sb_cur_y = (int)((double)total_y * percent)) >=
2262         (total_y - viewport_y))
2263         SP->sb_cur_y = total_y - viewport_y;
2264
2265     XawScrollbarSetThumb(w, (double)(cur_y / total_y),
2266                          (double)(viewport_y / total_y));
2267
2268     _send_key_to_curses(KEY_SF, NULL, TRUE);
2269 }
2270
2271 static void _thumb_left_right(Widget w, XtPointer client_data,
2272                   XtPointer call_data)
2273 {
2274     double percent = *(double *) call_data;
2275     double total_x = (double)SP->sb_total_x;
2276     double viewport_x = (double)SP->sb_viewport_x;
2277     int cur_x = SP->sb_cur_x;
2278
2279     if (SP->sb_viewport_x >= SP->sb_total_x)
2280         return;
2281
2282     if ((SP->sb_cur_x = (int)((float)total_x * percent)) >=
2283         (total_x - viewport_x))
2284         SP->sb_cur_x = total_x - viewport_x;
2285
2286     XawScrollbarSetThumb(w, (double)(cur_x / total_x),
2287                          (double)(viewport_x / total_x));
2288     
2289     _send_key_to_curses(KEY_SR, NULL, TRUE);
2290 }
2291
2292 static void _exit_process(int rc, int sig, char *msg)
2293 {
2294     if (rc || sig)
2295         fprintf(stderr, "%s:_exit_process() - called: rc:%d sig:%d <%s>\n",
2296                 XCLOGMSG, rc, sig, msg);
2297
2298     shmdt((char *)SP);
2299     shmdt((char *)Xcurscr);
2300     shmctl(shmidSP, IPC_RMID, 0);
2301     shmctl(shmid_Xcurscr, IPC_RMID, 0);
2302
2303     if (bitmap_file)
2304     {
2305         XFreePixmap(XCURSESDISPLAY, icon_bitmap);
2306         free(bitmap_file);
2307     }
2308
2309 #ifdef HAVE_XPM_H
2310     if (pixmap_file)
2311     {
2312         XFreePixmap(XCURSESDISPLAY, icon_pixmap);
2313         XFreePixmap(XCURSESDISPLAY, icon_pixmap_mask);
2314         free(pixmap_file);
2315     }
2316 #endif
2317     XFreeGC(XCURSESDISPLAY, normal_gc);
2318     XFreeGC(XCURSESDISPLAY, italic_gc);
2319     XFreeGC(XCURSESDISPLAY, block_cursor_gc);
2320     XFreeGC(XCURSESDISPLAY, rect_cursor_gc);
2321     XFreeGC(XCURSESDISPLAY, border_gc);
2322 #ifdef PDC_XIM
2323     XDestroyIC(Xic);
2324 #endif
2325
2326     shutdown(xc_display_sock, 2);
2327     close(xc_display_sock);
2328
2329     shutdown(xc_exit_sock, 2);
2330     close(xc_exit_sock);
2331
2332     shutdown(xc_key_sock, 2);
2333     close(xc_key_sock);
2334
2335     if (sig)
2336         kill(xc_otherpid, sig); /* to kill parent process */
2337
2338     _exit(rc);
2339 }
2340
2341 static void _resize(void)
2342 {
2343     short save_atrtab[PDC_COLOR_PAIRS * 2]; 
2344
2345     after_first_curses_request = FALSE;
2346
2347     SP->lines = XCursesLINES = ((resize_window_height -
2348         (2 * xc_app_data.borderWidth)) / font_height);
2349
2350     LINES = XCursesLINES - SP->linesrippedoff - SP->slklines;
2351
2352     SP->cols = COLS = XCursesCOLS = ((resize_window_width -
2353         (2 * xc_app_data.borderWidth)) / font_width);
2354
2355     window_width = resize_window_width;
2356     window_height = resize_window_height;
2357     visible_cursor = TRUE;
2358
2359     _draw_border();
2360
2361     /* Detach and drop the current shared memory segment and create and
2362        attach to a new segment */
2363
2364     memcpy(save_atrtab, xc_atrtab, sizeof(save_atrtab));
2365
2366     SP->XcurscrSize = XCURSCR_SIZE;
2367     shmdt((char *)Xcurscr);
2368     shmctl(shmid_Xcurscr, IPC_RMID, 0);
2369
2370     if ((shmid_Xcurscr = shmget(shmkey_Xcurscr,
2371         SP->XcurscrSize + XCURSESSHMMIN, 0700 | IPC_CREAT)) < 0)
2372     { 
2373         perror("Cannot allocate shared memory for curscr");
2374
2375         _exit_process(4, SIGKILL, "exiting from _process_curses_requests");
2376     } 
2377
2378     Xcurscr = (unsigned char*)shmat(shmid_Xcurscr, 0, 0);
2379     memset(Xcurscr, 0, SP->XcurscrSize);
2380     xc_atrtab = (short *)(Xcurscr + XCURSCR_ATRTAB_OFF);
2381     memcpy(xc_atrtab, save_atrtab, sizeof(save_atrtab));
2382 }
2383
2384 /* For PDC_set_title() */
2385
2386 static void _set_title(void)
2387 {
2388     char title[1024];   /* big enough for window title */ 
2389     int pos;
2390
2391     if ((XC_read_socket(xc_display_sock, &pos, sizeof(int)) < 0) ||
2392         (XC_read_socket(xc_display_sock, title, pos) < 0))
2393     {
2394         _exit_process(5, SIGKILL, "exiting from _set_title");
2395     }
2396
2397     XtVaSetValues(topLevel, XtNtitle, title, NULL);
2398 }
2399
2400 /* For color_content() */
2401
2402 static void _get_color(void)
2403 {
2404     XColor *tmp = (XColor *)(Xcurscr + XCURSCR_XCOLOR_OFF);
2405     int index = tmp->pixel;
2406     Colormap cmap = DefaultColormap(XCURSESDISPLAY,
2407                                     DefaultScreen(XCURSESDISPLAY));
2408
2409     if (index < 0 || index >= MAX_COLORS)
2410         _exit_process(4, SIGKILL, "exiting from _get_color");
2411
2412     tmp->pixel = colors[index];
2413     XQueryColor(XCURSESDISPLAY, cmap, tmp);
2414 }
2415
2416 /* For init_color() */
2417
2418 static void _set_color(void)
2419 {
2420     XColor *tmp = (XColor *)(Xcurscr + XCURSCR_XCOLOR_OFF);
2421     int index = tmp->pixel;
2422     Colormap cmap = DefaultColormap(XCURSESDISPLAY,
2423                                     DefaultScreen(XCURSESDISPLAY));
2424
2425     if (index < 0 || index >= MAX_COLORS)
2426         _exit_process(4, SIGKILL, "exiting from _set_color");
2427
2428     if (XAllocColor(XCURSESDISPLAY, cmap, tmp))
2429     {
2430         XFreeColors(XCURSESDISPLAY, cmap, colors + index, 1, 0);
2431         colors[index] = tmp->pixel;
2432
2433         _display_screen();
2434     }
2435 }
2436
2437 /* For PDC_getclipboard() */
2438
2439 static void _get_selection(Widget w, XtPointer data, Atom *selection,
2440                            Atom *type, XtPointer value,
2441                            unsigned long *length, int *format)
2442 {
2443     unsigned char *src = value;
2444     int pos, len = *length;
2445
2446     XC_LOG(("_get_selection() - called\n"));
2447
2448     if (!value && !len)
2449     {
2450         if (XC_write_display_socket_int(PDC_CLIP_EMPTY) < 0)
2451             _exit_process(4, SIGKILL, "exiting from _get_selection");
2452     }
2453     else
2454     {
2455         /* Here all is OK, send PDC_CLIP_SUCCESS, then length, then 
2456            contents */
2457
2458         if (XC_write_display_socket_int(PDC_CLIP_SUCCESS) < 0)
2459             _exit_process(4, SIGKILL, "exiting from _get_selection");
2460
2461         if (XC_write_display_socket_int(len) < 0)
2462             _exit_process(4, SIGKILL, "exiting from _get_selection");
2463
2464         for (pos = 0; pos < len; pos++)
2465         {
2466 #ifdef PDC_WIDE
2467             wchar_t c;
2468 #else
2469             unsigned char c;
2470 #endif
2471             c = *src++;
2472
2473             if (XC_write_socket(xc_display_sock, &c, sizeof(c)) < 0)
2474                 _exit_process(4, SIGKILL, "exiting from _get_selection");
2475         }
2476     }
2477 }
2478
2479 #ifdef PDC_WIDE
2480 static void _get_selection_utf8(Widget w, XtPointer data, Atom *selection,
2481                                 Atom *type, XtPointer value,
2482                                 unsigned long *length, int *format)
2483 {
2484     int len = *length;
2485
2486     XC_LOG(("_get_selection_utf8() - called\n"));
2487
2488     if (!*type || !*length)
2489     {
2490         XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, _get_selection,
2491                             (XtPointer)NULL, 0);
2492         return;
2493     }
2494
2495     if (!value && !len)
2496     {
2497         if (XC_write_display_socket_int(PDC_CLIP_EMPTY) >= 0)
2498             return;
2499     }
2500     else
2501     {
2502         wchar_t *wcontents = malloc((len + 1) * sizeof(wchar_t));
2503         char *src = value;
2504         int i = 0;
2505
2506         while (*src && i < (*length))
2507         {
2508             int retval = _from_utf8(wcontents + i, src, len);
2509
2510             src += retval;
2511             len -= retval;
2512             i++;
2513         }
2514
2515         wcontents[i] = 0;
2516         len = i;
2517
2518         /* Here all is OK, send PDC_CLIP_SUCCESS, then length, then 
2519            contents */
2520
2521         if (XC_write_display_socket_int(PDC_CLIP_SUCCESS) >= 0)
2522             if (XC_write_display_socket_int(len) >= 0)
2523                 if (XC_write_socket(xc_display_sock, 
2524                     wcontents, len * sizeof(wchar_t)) >= 0)
2525                 {
2526                     free(wcontents);
2527                     return;
2528                 }
2529     }
2530
2531     _exit_process(4, SIGKILL, "exiting from _get_selection_utf8");
2532 }
2533 #endif
2534
2535 /* For PDC_setclipboard() */
2536
2537 static void _set_selection(void)
2538 {
2539     long length, pos;
2540     int status;
2541
2542     if (XC_read_socket(xc_display_sock, &length, sizeof(long)) < 0)
2543         _exit_process(5, SIGKILL, "exiting from _set_selection");
2544
2545     if (length > (long)tmpsel_length)
2546     {
2547         if (!tmpsel_length)
2548             tmpsel = malloc((length + 1) * sizeof(chtype));
2549         else
2550             tmpsel = realloc(tmpsel, (length + 1) * sizeof(chtype));
2551     }
2552
2553     if (!tmpsel)
2554         if (XC_write_display_socket_int(PDC_CLIP_MEMORY_ERROR) < 0)
2555             _exit_process(4, SIGKILL, "exiting from _set_selection");
2556
2557     for (pos = 0; pos < length; pos++)
2558     {
2559 #ifdef PDC_WIDE
2560         wchar_t c;
2561 #else
2562         unsigned char c;
2563 #endif
2564         if (XC_read_socket(xc_display_sock, &c, sizeof(c)) < 0)
2565             _exit_process(5, SIGKILL, "exiting from _set_selection");
2566
2567         tmpsel[pos] = c;
2568     }
2569
2570     tmpsel_length = length;
2571     tmpsel[length] = 0;
2572
2573     if (XtOwnSelection(topLevel, XA_PRIMARY, CurrentTime,
2574                        _convert_proc, _lose_ownership, NULL) == False)
2575     {
2576         status = PDC_CLIP_ACCESS_ERROR;
2577         free(tmpsel);
2578         tmpsel = NULL;
2579         tmpsel_length = 0;
2580     }
2581     else
2582         status = PDC_CLIP_SUCCESS;
2583
2584     _selection_off();
2585
2586     if (XC_write_display_socket_int(status) < 0)
2587         _exit_process(4, SIGKILL, "exiting from _set_selection");
2588 }
2589
2590 /* The curses process is waiting; tell it to continue */
2591
2592 static void _resume_curses(void)
2593 {
2594     if (XC_write_display_socket_int(CURSES_CONTINUE) < 0)
2595         _exit_process(4, SIGKILL, "exiting from _process_curses_requests");
2596 }
2597
2598 /* The curses process sent us a message */
2599
2600 static void _process_curses_requests(XtPointer client_data, int *fid,
2601                                      XtInputId *id) 
2602
2603     struct timeval socket_timeout = {0};
2604     int s;
2605     int old_row, new_row;
2606     int old_x, new_x;
2607     int pos, num_cols;
2608
2609     char buf[12];       /* big enough for 2 integers */ 
2610
2611     XC_LOG(("_process_curses_requests() - called\n"));
2612
2613     if (!received_map_notify) 
2614         return; 
2615
2616     FD_ZERO(&xc_readfds); 
2617     FD_SET(xc_display_sock, &xc_readfds); 
2618
2619     if ((s = select(FD_SETSIZE, (FD_SET_CAST)&xc_readfds, NULL, 
2620                     NULL, &socket_timeout)) < 0)
2621         _exit_process(2, SIGKILL, "exiting from _process_curses_requests"
2622                                   " - select failed");
2623
2624     if (!s)     /* no requests pending - should never happen! */ 
2625         return; 
2626
2627     if (FD_ISSET(xc_display_sock, &xc_readfds)) 
2628     { 
2629         /* read first integer to determine total message has been
2630            received */
2631
2632         XC_LOG(("_process_curses_requests() - before XC_read_socket()\n"));
2633
2634         if (XC_read_socket(xc_display_sock, &num_cols, sizeof(int)) < 0) 
2635             _exit_process(3, SIGKILL, "exiting from _process_curses_requests"
2636                                       " - first read");
2637
2638         XC_LOG(("_process_curses_requests() - after XC_read_socket()\n"));
2639
2640         after_first_curses_request = TRUE;
2641
2642         switch(num_cols) 
2643         { 
2644         case CURSES_EXIT:   /* request from curses to stop */
2645             XC_LOG(("CURSES_EXIT received from child\n"));
2646             _exit_process(0, 0, "XCursesProcess requested to exit by child");
2647             break;
2648
2649         case CURSES_BELL:
2650             XC_LOG(("CURSES_BELL received from child\n"));
2651             XBell(XCURSESDISPLAY, 50);
2652             break;
2653
2654         /* request from curses to confirm completion of display */ 
2655
2656         case CURSES_REFRESH:
2657             XC_LOG(("CURSES_REFRESH received from child\n"));
2658             _refresh_screen(); 
2659             _resume_curses();
2660             break;
2661
2662         case CURSES_REFRESH_SCROLLBAR:
2663             _refresh_scrollbar();
2664             break;
2665
2666         case CURSES_CURSOR:
2667             XC_LOG(("CURSES_CURSOR received from child\n"));
2668
2669             if (XC_read_socket(xc_display_sock, buf, sizeof(int) * 2) < 0)
2670                 _exit_process(5, SIGKILL, "exiting from CURSES_CURSOR "
2671                                           "_process_curses_requests");
2672
2673             memcpy(&pos, buf, sizeof(int)); 
2674             old_row = pos & 0xFF;
2675             old_x = pos >> 8;
2676
2677             memcpy(&pos, buf + sizeof(int), sizeof(int));
2678             new_row = pos & 0xFF;
2679             new_x = pos >> 8;
2680
2681             visible_cursor = TRUE;
2682             _display_cursor(old_row, old_x, new_row, new_x);
2683             break;
2684
2685         case CURSES_DISPLAY_CURSOR:
2686             XC_LOG(("CURSES_DISPLAY_CURSOR received from child. Vis now: "));
2687             XC_LOG((visible_cursor ? "1\n" : "0\n"));
2688
2689             /* If the window is not active, ignore this command. The
2690                cursor will stay solid. */
2691
2692             if (window_entered)
2693             { 
2694                 if (visible_cursor) 
2695                 { 
2696                     /* Cursor currently ON, turn it off */
2697
2698                     int save_visibility = SP->visibility;
2699                     SP->visibility = 0;
2700                     _redraw_cursor();
2701                     SP->visibility = save_visibility;
2702                     visible_cursor = FALSE;
2703                 }
2704                 else 
2705                 { 
2706                     /* Cursor currently OFF, turn it on */ 
2707
2708                     _redraw_cursor();
2709                     visible_cursor = TRUE;
2710                 } 
2711             } 
2712
2713             break; 
2714
2715         case CURSES_TITLE:
2716             XC_LOG(("CURSES_TITLE received from child\n"));
2717             _set_title();
2718             break;
2719
2720         case CURSES_RESIZE:
2721             XC_LOG(("CURSES_RESIZE received from child\n"));
2722             _resize();
2723             _resume_curses(); 
2724             break; 
2725
2726         case CURSES_GET_SELECTION:
2727             XC_LOG(("CURSES_GET_SELECTION received from child\n"));
2728
2729             _resume_curses();
2730
2731             XtGetSelectionValue(topLevel, XA_PRIMARY,
2732 #ifdef PDC_WIDE
2733                                 XA_UTF8_STRING(XtDisplay(topLevel)),
2734                                 _get_selection_utf8,
2735 #else
2736                                 XA_STRING, _get_selection,
2737 #endif
2738                                 (XtPointer)NULL, 0);
2739
2740             break;
2741
2742         case CURSES_SET_SELECTION:
2743             XC_LOG(("CURSES_SET_SELECTION received from child\n"));
2744             _set_selection();
2745             break;
2746
2747         case CURSES_CLEAR_SELECTION:
2748             XC_LOG(("CURSES_CLEAR_SELECTION received from child\n"));
2749             _resume_curses();
2750             _selection_off();
2751             break;
2752
2753         case CURSES_GET_COLOR:
2754             XC_LOG(("CURSES_GET_COLOR recieved from child\n"));
2755             _get_color();
2756             _resume_curses();
2757             break;
2758
2759         case CURSES_SET_COLOR:
2760             XC_LOG(("CURSES_SET_COLOR recieved from child\n"));
2761             _set_color();
2762             _resume_curses();
2763             break;
2764
2765         default: 
2766             PDC_LOG(("%s:Unknown request %d\n", XCLOGMSG, num_cols));
2767         } 
2768     } 
2769
2770
2771 static void _handle_structure_notify(Widget w, XtPointer client_data, 
2772                                      XEvent *event, Boolean *unused)
2773 {
2774     XC_LOG(("_handle_structure_notify() - called\n"));
2775
2776     switch(event->type)
2777     {
2778     case ConfigureNotify:
2779         XC_LOG(("ConfigureNotify received\n"));
2780
2781         /* Window has been resized, change width and height to send to
2782            place_text and place_graphics in next Expose. Also will need
2783            to kill (SIGWINCH) curses process if screen size changes. */
2784
2785         resize_window_width = event->xconfigure.width;
2786         resize_window_height = event->xconfigure.height;
2787
2788         after_first_curses_request = FALSE;
2789
2790 #ifdef SIGWINCH
2791         SP->resized = 1;
2792
2793         kill(xc_otherpid, SIGWINCH);
2794 #endif
2795         _send_key_to_curses(KEY_RESIZE, NULL, TRUE);
2796         break;
2797
2798     case MapNotify:
2799         XC_LOG(("MapNotify received\n"));
2800
2801         received_map_notify = 1;
2802
2803         _draw_border();
2804         break;
2805
2806     default:
2807         PDC_LOG(("%s:_handle_structure_notify - unknown event %d\n",
2808                  XCLOGMSG, event->type));
2809     }
2810 }
2811
2812 static RETSIGTYPE _handle_signals(int signo)
2813 {
2814     int flag = CURSES_EXIT;
2815
2816     PDC_LOG(("%s:_handle_signals() - called: %d\n", XCLOGMSG, signo));
2817
2818     /* Patch by: Georg Fuchs */
2819
2820     XCursesSetSignal(signo, _handle_signals);
2821
2822 #ifdef SIGTSTP
2823     if (signo == SIGTSTP)
2824     {
2825         pause();
2826         return;
2827     }
2828 #endif
2829 #ifdef SIGCONT
2830     if (signo == SIGCONT)
2831         return;
2832 #endif
2833 #ifdef SIGCLD
2834     if (signo == SIGCLD)
2835         return;
2836 #endif
2837 #ifdef SIGTTIN
2838     if (signo == SIGTTIN)
2839         return;
2840 #endif
2841 #ifdef SIGWINCH
2842     if (signo == SIGWINCH)
2843         return;
2844 #endif
2845
2846     /* End of patch by: Georg Fuchs */
2847
2848     XCursesSetSignal(signo, SIG_IGN);
2849
2850     /* Send a CURSES_EXIT to myself */
2851
2852     if (XC_write_socket(xc_exit_sock, &flag, sizeof(int)) < 0)
2853         _exit_process(7, signo, "exiting from _handle_signals");
2854 }
2855
2856 #ifdef PDC_XIM
2857 static void _dummy_handler(Widget w, XtPointer client_data,
2858                            XEvent *event, Boolean *unused)
2859 {
2860 }
2861 #endif
2862
2863 int XCursesSetupX(int argc, char *argv[])
2864 {
2865     char *myargv[] = {"PDCurses", NULL};
2866     extern bool sb_started;
2867
2868     int italic_font_valid;
2869     XColor pointerforecolor, pointerbackcolor;
2870     XrmValue rmfrom, rmto;
2871     int i = 0;
2872     int minwidth, minheight;
2873
2874     XC_LOG(("XCursesSetupX called\n"));
2875
2876     if (!argv)
2877     {
2878         argv = myargv;
2879         argc = 1;
2880     }
2881
2882     program_name = argv[0];
2883
2884     /* Keep open the 'write' end of the socket so the XCurses process
2885        can send a CURSES_EXIT to itself from within the signal handler */
2886
2887     xc_exit_sock = xc_display_sockets[0];
2888     xc_display_sock = xc_display_sockets[1];
2889
2890     close(xc_key_sockets[0]);
2891     xc_key_sock = xc_key_sockets[1];
2892
2893     /* Trap all signals when XCurses is the child process, but only if
2894        they haven't already been ignored by the application. */
2895
2896     for (i = 0; i < PDC_MAX_SIGNALS; i++)
2897         if (XCursesSetSignal(i, _handle_signals) == SIG_IGN)
2898             XCursesSetSignal(i, SIG_IGN);
2899
2900     /* Start defining X Toolkit things */
2901
2902 #if XtSpecificationRelease > 4
2903     XtSetLanguageProc(NULL, (XtLanguageProc)NULL, NULL);
2904 #endif
2905
2906     /* Exit if no DISPLAY variable set */
2907
2908     if (!getenv("DISPLAY"))
2909     {
2910         fprintf(stderr, "Error: no DISPLAY variable set\n");
2911         kill(xc_otherpid, SIGKILL);
2912         return ERR;
2913     }
2914
2915     /* Initialise the top level widget */
2916
2917     topLevel = XtVaAppInitialize(&app_context, class_name, options,
2918                                  XtNumber(options), &argc, argv, NULL, NULL);
2919
2920     XtVaGetApplicationResources(topLevel, &xc_app_data, app_resources,
2921                                 XtNumber(app_resources), NULL);
2922
2923     /* Check application resource values here */
2924
2925     font_width = xc_app_data.normalFont->max_bounds.rbearing - 
2926                  xc_app_data.normalFont->min_bounds.lbearing;
2927
2928     font_height = xc_app_data.normalFont->max_bounds.ascent + 
2929                   xc_app_data.normalFont->max_bounds.descent;
2930
2931     font_ascent = xc_app_data.normalFont->max_bounds.ascent;
2932     font_descent = xc_app_data.normalFont->max_bounds.descent;
2933
2934     /* Check that the italic font and normal fonts are the same size */
2935     /* This appears backwards */
2936
2937     italic_font_valid = font_width !=
2938         xc_app_data.italicFont->max_bounds.rbearing -
2939         xc_app_data.italicFont->min_bounds.lbearing ||
2940         font_height !=
2941         xc_app_data.italicFont->max_bounds.ascent + 
2942         xc_app_data.italicFont->max_bounds.descent;
2943
2944     /* Calculate size of display window */
2945
2946     XCursesCOLS = xc_app_data.cols;
2947     XCursesLINES = xc_app_data.lines;
2948
2949     window_width = font_width * XCursesCOLS +
2950                    2 * xc_app_data.borderWidth;
2951
2952     window_height = font_height * XCursesLINES +
2953                     2 * xc_app_data.borderWidth;
2954
2955     minwidth = font_width * 2 + xc_app_data.borderWidth * 2;
2956     minheight = font_height * 2 + xc_app_data.borderWidth * 2;
2957
2958     /* Set up the icon for the application; the default is an internal
2959        one for PDCurses. Then set various application level resources. */
2960
2961     _get_icon();
2962
2963 #ifdef HAVE_XPM_H
2964     if (xc_app_data.pixmap && xc_app_data.pixmap[0])
2965         XtVaSetValues(topLevel, XtNminWidth, minwidth, XtNminHeight,
2966                       minheight, XtNbaseWidth, xc_app_data.borderWidth * 2,
2967                       XtNbaseHeight, xc_app_data.borderWidth * 2,
2968                       XtNiconPixmap, icon_pixmap,
2969                       XtNiconMask, icon_pixmap_mask, NULL);
2970     else
2971 #endif
2972         XtVaSetValues(topLevel, XtNminWidth, minwidth, XtNminHeight,
2973                       minheight, XtNbaseWidth, xc_app_data.borderWidth * 2,
2974                       XtNbaseHeight, xc_app_data.borderWidth * 2,
2975                       XtNiconPixmap, icon_bitmap, NULL);
2976
2977     /* Create a BOX widget in which to draw */
2978
2979     if (xc_app_data.scrollbarWidth && sb_started)
2980     {
2981         scrollBox = XtVaCreateManagedWidget(program_name, 
2982             scrollBoxWidgetClass, topLevel, XtNwidth, 
2983             window_width + xc_app_data.scrollbarWidth, 
2984             XtNheight, window_height + xc_app_data.scrollbarWidth,
2985             XtNwidthInc, font_width, XtNheightInc, font_height, NULL);
2986
2987         drawing = XtVaCreateManagedWidget(program_name, 
2988             boxWidgetClass, scrollBox, XtNwidth, 
2989             window_width, XtNheight, window_height, XtNwidthInc, 
2990             font_width, XtNheightInc, font_height, NULL);
2991
2992         scrollVert = XtVaCreateManagedWidget("scrollVert", 
2993             scrollbarWidgetClass, scrollBox, XtNorientation, 
2994             XtorientVertical, XtNheight, window_height, XtNwidth, 
2995             xc_app_data.scrollbarWidth, NULL);
2996
2997         XtAddCallback(scrollVert, XtNscrollProc, _scroll_up_down, drawing);
2998         XtAddCallback(scrollVert, XtNjumpProc, _thumb_up_down, drawing);
2999
3000         scrollHoriz = XtVaCreateManagedWidget("scrollHoriz", 
3001             scrollbarWidgetClass, scrollBox, XtNorientation, 
3002             XtorientHorizontal, XtNwidth, window_width, XtNheight, 
3003             xc_app_data.scrollbarWidth, NULL);
3004
3005         XtAddCallback(scrollHoriz, XtNscrollProc, _scroll_left_right, drawing);
3006         XtAddCallback(scrollHoriz, XtNjumpProc, _thumb_left_right, drawing);
3007     }
3008     else
3009     {
3010         drawing = XtVaCreateManagedWidget(program_name, boxWidgetClass,
3011             topLevel, XtNwidth, window_width, XtNheight, window_height,
3012             XtNwidthInc, font_width, XtNheightInc, font_height, NULL);
3013
3014         XtVaSetValues(topLevel, XtNwidthInc, font_width, XtNheightInc,
3015                       font_height, NULL);
3016     }
3017
3018     /* Process any default translations */
3019
3020     XtAugmentTranslations(drawing,
3021                           XtParseTranslationTable(default_translations));
3022     XtAppAddActions(app_context, action_table, XtNumber(action_table));
3023
3024     /* Process the supplied colors */
3025
3026     _initialize_colors();
3027
3028     /* Determine text cursor alignment from resources */
3029
3030     if (!strcmp(xc_app_data.textCursor, "vertical"))
3031         vertical_cursor = TRUE;
3032
3033     /* Now have LINES and COLS. Set these in the shared SP so the curses
3034        program can find them. */
3035
3036     LINES = XCursesLINES;
3037     COLS = XCursesCOLS;
3038
3039     if ((shmidSP = shmget(shmkeySP, sizeof(SCREEN) + XCURSESSHMMIN, 
3040         0700 | IPC_CREAT)) < 0)
3041     {
3042         perror("Cannot allocate shared memory for SCREEN");
3043         kill(xc_otherpid, SIGKILL);
3044         return ERR;
3045     }
3046
3047     SP = (SCREEN*)shmat(shmidSP, 0, 0);
3048     memset(SP, 0, sizeof(SCREEN));
3049     SP->XcurscrSize = XCURSCR_SIZE;
3050     SP->lines = XCursesLINES;
3051     SP->cols = XCursesCOLS;
3052
3053     SP->mouse_wait = xc_app_data.clickPeriod;
3054     SP->audible = TRUE;
3055
3056     PDC_LOG(("%s:SHM size for curscr %d\n", XCLOGMSG, SP->XcurscrSize));
3057
3058     if ((shmid_Xcurscr = shmget(shmkey_Xcurscr, SP->XcurscrSize + 
3059         XCURSESSHMMIN, 0700 | IPC_CREAT)) < 0)
3060     {
3061         perror("Cannot allocate shared memory for curscr");
3062         kill(xc_otherpid, SIGKILL);
3063         shmdt((char *)SP);
3064         shmctl(shmidSP, IPC_RMID, 0);
3065         return ERR;
3066     }
3067
3068     Xcurscr = (unsigned char *)shmat(shmid_Xcurscr, 0, 0);
3069     memset(Xcurscr, 0, SP->XcurscrSize); 
3070     xc_atrtab = (short *)(Xcurscr + XCURSCR_ATRTAB_OFF);
3071
3072     PDC_LOG(("%s:shmid_Xcurscr %d shmkey_Xcurscr %d LINES %d COLS %d\n",
3073              XCLOGMSG, shmid_Xcurscr, shmkey_Xcurscr, LINES, COLS));
3074
3075     /* Add Event handlers to the drawing widget */
3076
3077     XtAddEventHandler(drawing, ExposureMask, False, _handle_expose, NULL);
3078     XtAddEventHandler(drawing, StructureNotifyMask, False, 
3079                       _handle_structure_notify, NULL);
3080     XtAddEventHandler(drawing, EnterWindowMask | LeaveWindowMask, False,
3081                       _handle_enter_leave, NULL);
3082     XtAddEventHandler(topLevel, 0, True, _handle_nonmaskable, NULL);
3083
3084     /* Add input handler from xc_display_sock (requests from curses 
3085        program) */
3086
3087     XtAppAddInput(app_context, xc_display_sock, (XtPointer)XtInputReadMask, 
3088                   _process_curses_requests, NULL);
3089
3090     /* If there is a cursorBlink resource, start the Timeout event */
3091
3092     if (xc_app_data.cursorBlinkRate)
3093         XtAppAddTimeOut(app_context, xc_app_data.cursorBlinkRate,
3094                         _blink_cursor, NULL);
3095
3096     /* Leave telling the curses process that it can start to here so 
3097        that when the curses process makes a request, the Xcurses 
3098        process can service the request. */
3099
3100     XC_write_display_socket_int(CURSES_CHILD);
3101
3102     XtRealizeWidget(topLevel);
3103
3104     /* Handle trapping of the WM_DELETE_WINDOW property */
3105
3106     wm_atom[0] = XInternAtom(XtDisplay(topLevel), "WM_DELETE_WINDOW", False);
3107
3108     XSetWMProtocols(XtDisplay(topLevel), XtWindow(topLevel), wm_atom, 1);
3109
3110     /* Create the Graphics Context for drawing. This MUST be done AFTER
3111        the associated widget has been realized. */
3112
3113     XC_LOG(("before _get_gc\n"));
3114
3115     _get_gc(&normal_gc, xc_app_data.normalFont, COLOR_WHITE, COLOR_BLACK);
3116
3117     _get_gc(&italic_gc, italic_font_valid ? xc_app_data.italicFont : 
3118             xc_app_data.normalFont, COLOR_WHITE, COLOR_BLACK);
3119
3120     _get_gc(&block_cursor_gc, xc_app_data.normalFont,
3121             COLOR_BLACK, COLOR_CURSOR);
3122
3123     _get_gc(&rect_cursor_gc, xc_app_data.normalFont,
3124             COLOR_CURSOR, COLOR_BLACK);
3125
3126     _get_gc(&border_gc, xc_app_data.normalFont, COLOR_BORDER, COLOR_BLACK);
3127
3128     XSetLineAttributes(XCURSESDISPLAY, rect_cursor_gc, 2,
3129                        LineSolid, CapButt, JoinMiter);
3130
3131     XSetLineAttributes(XCURSESDISPLAY, border_gc, xc_app_data.borderWidth,
3132                        LineSolid, CapButt, JoinMiter);
3133
3134     /* Set the cursor for the application */
3135
3136     XDefineCursor(XCURSESDISPLAY, XCURSESWIN, xc_app_data.pointer);
3137     rmfrom.size = sizeof(Pixel);
3138     rmto.size = sizeof(XColor);
3139
3140     rmto.addr = (XPointer)&pointerforecolor;
3141     rmfrom.addr = (XPointer)&(xc_app_data.pointerForeColor);
3142     XtConvertAndStore(drawing, XtRPixel, &rmfrom, XtRColor, &rmto);
3143
3144     rmfrom.size = sizeof(Pixel);
3145     rmto.size = sizeof(XColor);
3146
3147     rmfrom.addr = (XPointer)&(xc_app_data.pointerBackColor);
3148     rmto.addr = (XPointer)&pointerbackcolor;
3149     XtConvertAndStore(drawing, XtRPixel, &rmfrom, XtRColor, &rmto);
3150
3151     XRecolorCursor(XCURSESDISPLAY, xc_app_data.pointer,
3152                    &pointerforecolor, &pointerbackcolor);
3153
3154 #ifndef PDC_XIM
3155
3156     /* Convert the supplied compose key to a Keysym */
3157
3158     compose_key = XStringToKeysym(xc_app_data.composeKey);
3159
3160     if (compose_key && IsModifierKey(compose_key))
3161     {
3162         int i, j;
3163         KeyCode *kcp;
3164         XModifierKeymap *map;
3165         KeyCode compose_keycode = XKeysymToKeycode(XCURSESDISPLAY, compose_key);
3166
3167         map = XGetModifierMapping(XCURSESDISPLAY);
3168         kcp = map->modifiermap;
3169
3170         for (i = 0; i < 8; i++)
3171         {
3172             for (j = 0; j < map->max_keypermod; j++, kcp++)
3173             {
3174                 if (!*kcp)
3175                     continue;
3176
3177                 if (compose_keycode == *kcp)
3178                 {
3179                     compose_mask = state_mask[i];
3180                     break;
3181                 }
3182             }
3183
3184             if (compose_mask)
3185                 break;
3186         }
3187
3188         XFreeModifiermap(map);
3189     }
3190
3191 #else
3192     Xim = XOpenIM(XCURSESDISPLAY, NULL, NULL, NULL);
3193
3194     if (Xim)
3195     {
3196         Xic = XCreateIC(Xim, XNInputStyle,
3197                         XIMPreeditNothing | XIMStatusNothing,
3198                         XNClientWindow, XCURSESWIN, NULL);
3199     }
3200
3201     if (Xic)
3202     {
3203         long im_event_mask;
3204
3205         XGetICValues(Xic, XNFilterEvents, &im_event_mask, NULL);
3206         if (im_event_mask)
3207             XtAddEventHandler(drawing, im_event_mask, False,
3208                               _dummy_handler, NULL);
3209
3210         XSetICFocus(Xic);
3211     }
3212     else
3213     {
3214         perror("ERROR: Cannot create input context");
3215         kill(xc_otherpid, SIGKILL);
3216         shmdt((char *)SP);
3217         shmdt((char *)Xcurscr);
3218         shmctl(shmidSP, IPC_RMID, 0);
3219         shmctl(shmid_Xcurscr, IPC_RMID, 0);
3220         return ERR;
3221     }
3222
3223 #endif
3224
3225     /* Wait for events */
3226
3227     XtAppMainLoop(app_context);
3228     return OK;          /* won't get here */
3229 }