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