1 /* Public Domain Curses */
5 RCSID("$Id: x11.c,v 1.94 2008/07/14 04:33:26 wmcbrine Exp $")
7 #ifdef HAVE_DECKEYSYM_H
8 # include <DECkeysym.h>
11 #ifdef HAVE_SUNKEYSYM_H
12 # include <Sunkeysym.h>
26 #ifndef XPOINTER_TYPEDEFED
27 typedef char * XPointer;
34 XCursesAppData xc_app_data;
36 #if NeedWidePrototypes
37 # define PDC_SCROLLBAR_TYPE double
39 # define PDC_SCROLLBAR_TYPE float
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 */
46 #define XCURSESDISPLAY (XtDisplay(drawing))
47 #define XCURSESWIN (XtWindow(drawing))
49 /* Default icons for XCurses applications. */
51 #include "big_icon.xbm"
52 #include "little_icon.xbm"
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);
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 *);
69 unsigned short normal;
70 unsigned short shifted;
71 unsigned short control;
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)},
132 #ifdef HAVE_DECKEYSYM_H
133 {DXK_Remove, FALSE, KEY_DC, KEY_SDC, CTL_DEL, ALT_DEL},
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},
173 #ifdef HAVE_XK_KP_INSERT
174 {XK_KP_Insert, TRUE, PAD0, '0', CTL_PAD0, ALT_PAD0},
176 #ifdef HAVE_XK_KP_END
177 {XK_KP_End, TRUE, KEY_C1, '1', CTL_PAD1, ALT_PAD1},
179 #ifdef HAVE_XK_KP_DOWN
180 {XK_KP_Down, TRUE, KEY_C2, '2', CTL_PAD2, ALT_PAD2},
182 #ifdef HAVE_XK_KP_NEXT
183 {XK_KP_Next, TRUE, KEY_C3, '3', CTL_PAD3, ALT_PAD3},
185 #ifdef HAVE_XK_KP_LEFT
186 {XK_KP_Left, TRUE, KEY_B1, '4', CTL_PAD4, ALT_PAD4},
188 #ifdef HAVE_XK_KP_BEGIN
189 {XK_KP_Begin, TRUE, KEY_B2, '5', CTL_PAD5, ALT_PAD5},
191 #ifdef HAVE_XK_KP_RIGHT
192 {XK_KP_Right, TRUE, KEY_B3, '6', CTL_PAD6, ALT_PAD6},
194 #ifdef HAVE_XK_KP_HOME
195 {XK_KP_Home, TRUE, KEY_A1, '7', CTL_PAD7, ALT_PAD7},
198 {XK_KP_Up, TRUE, KEY_A2, '8', CTL_PAD8, ALT_PAD8},
200 #ifdef HAVE_XK_KP_PRIOR
201 {XK_KP_Prior, TRUE, KEY_A3, '9', CTL_PAD9, ALT_PAD9},
207 # include "compose.h"
210 #define BITMAPDEPTH 1
212 unsigned long pdc_key_modifiers = 0L;
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;
220 static char *pixmap_file = NULL;
222 static KeySym keysym = 0;
224 static int state_mask[8] =
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;
248 static Pixmap icon_pixmap;
249 static Pixmap icon_pixmap_mask;
251 static bool visible_cursor = FALSE;
252 static bool window_entered = TRUE;
253 static char *program_name;
255 /* Macros just for app_resources */
258 # define DEFFONT "-misc-fixed-medium-r-normal--20-200-75-75-c-100-iso10646-1"
260 # define DEFFONT "7x13"
263 #define APPDATAOFF(n) XtOffsetOf(XCursesAppData, n)
265 #define RINT(name1, name2, value) { \
266 #name1, #name2, XtRInt, \
267 sizeof(int), APPDATAOFF(name1), XtRImmediate, \
271 #define RPIXEL(name1, name2, value) { \
272 #name1, #name2, XtRPixel, \
273 sizeof(Pixel), APPDATAOFF(name1), XtRString, \
277 #define RCOLOR(name, value) RPIXEL(color##name, Color##name, value)
280 #define RSTRINGP(name1, name2, param) { \
281 #name1, #name2, XtRString, \
282 MAX_PATH, APPDATAOFF(name1), XtRString, (XtPointer)param \
285 #define RSTRING(name1, name2) RSTRINGP(name1, name2, "")
287 #define RFONT(name1, name2, value) { \
288 #name1, #name2, XtRFontStruct, \
289 sizeof(XFontStruct), APPDATAOFF(name1), XtRString, \
293 #define RCURSOR(name1, name2, value) { \
294 #name1, #name2, XtRCursor, \
295 sizeof(Cursor), APPDATAOFF(name1), XtRString, \
299 static XtResource app_resources[] =
301 RINT(lines, Lines, 24),
302 RINT(cols, Cols, 80),
304 RPIXEL(cursorColor, CursorColor, Red),
306 RCOLOR(Black, Black),
308 RCOLOR(Green, green3),
309 RCOLOR(Yellow, yellow3),
311 RCOLOR(Magenta, magenta3),
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),
324 RFONT(normalFont, NormalFont, DEFFONT),
325 RFONT(italicFont, ItalicFont, DEFFONT),
327 RSTRING(bitmap, Bitmap),
329 RSTRING(pixmap, Pixmap),
331 RSTRINGP(composeKey, ComposeKey, "Multi_key"),
333 RCURSOR(pointer, Pointer, xterm),
335 RPIXEL(pointerForeColor, PointerForeColor, Black),
336 RPIXEL(pointerBackColor, PointerBackColor, White),
338 RINT(shmmin, Shmmin, 0),
339 RINT(borderWidth, BorderWidth, 0),
341 RPIXEL(borderColor, BorderColor, Black),
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),
348 RSTRING(textCursor, TextCursor)
360 /* Macros for options */
362 #define COPT(name) {"-" #name, "*" #name, XrmoptionSepArg, NULL}
363 #define CCOLOR(name) COPT(color##name)
365 static XrmOptionDescRec options[] =
367 COPT(lines), COPT(cols), COPT(normalFont), COPT(italicFont),
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),
377 CCOLOR(Black), CCOLOR(Red), CCOLOR(Green), CCOLOR(Yellow),
378 CCOLOR(Blue), CCOLOR(Magenta), CCOLOR(Cyan), CCOLOR(White),
380 CCOLOR(BoldBlack), CCOLOR(BoldRed), CCOLOR(BoldGreen),
381 CCOLOR(BoldYellow), CCOLOR(BoldBlue), CCOLOR(BoldMagenta),
382 CCOLOR(BoldCyan), CCOLOR(BoldWhite)
388 static XtActionsRec action_table[] =
390 {"XCursesButton", (XtActionProc)XCursesButton},
391 {"XCursesKeyPress", (XtActionProc)XCursesKeyPress},
392 {"XCursesPasteSelection", (XtActionProc)XCursesPasteSelection},
393 {"string", (XtActionProc)XCursesHandleString}
396 static bool after_first_curses_request = FALSE;
397 static Pixel colors[MAX_COLORS + 2];
398 static bool vertical_cursor = FALSE;
401 static XIM Xim = NULL;
402 static XIC Xic = NULL;
405 static const char *default_translations =
407 "<Key>: XCursesKeyPress() \n" \
408 "<KeyUp>: XCursesKeyPress() \n" \
409 "<BtnDown>: XCursesButton() \n" \
410 "<BtnUp>: XCursesButton() \n" \
411 "<BtnMotion>: XCursesButton()"
414 static int _to_utf8(char *outcode, chtype code)
417 if (code & A_ALTCHARSET && !(code & 0xff80))
418 code = acs_map[code & 0x7f];
430 outcode[0] = ((code & 0x07c0) >> 6) | 0xc0;
431 outcode[1] = (code & 0x003f) | 0x80;
436 outcode[0] = ((code & 0xf000) >> 12) | 0xe0;
437 outcode[1] = ((code & 0x0fc0) >> 6) | 0x80;
438 outcode[2] = (code & 0x003f) | 0x80;
443 static int _from_utf8(wchar_t *pwc, const char *s, size_t n)
447 const unsigned char *string;
455 string = (const unsigned char *)s;
459 /* Simplistic UTF-8 decoder -- only does the BMP, minimal validation */
463 if ((key & 0xe0) == 0xc0)
467 key = ((key & 0x1f) << 6) | (string[1] & 0x3f);
471 else if ((key & 0xe0) == 0xe0)
475 key = ((key & 0x0f) << 12) |
476 ((string[1] & 0x3f) << 6) | (string[2] & 0x3f);
490 #ifndef X_HAVE_UTF8_STRING
491 static Atom XA_UTF8_STRING(Display *dpy)
493 static AtomPtr p = NULL;
496 p = XmuMakeAtom("UTF8_STRING");
498 return XmuInternAtom(dpy, p);
502 signal_handler XCursesSetSignal(int signo, signal_handler action)
504 #if defined(SA_INTERRUPT) || defined(SA_RESTART)
505 struct sigaction sigact, osigact;
507 sigact.sa_handler = action;
512 SA_INTERRUPT | SA_RESTART;
516 # else /* must be SA_RESTART */
519 sigemptyset(&sigact.sa_mask);
521 if (sigaction(signo, &sigact, &osigact))
524 return osigact.sa_handler;
526 #else /* not SA_INTERRUPT or SA_RESTART, use plain signal */
527 return signal(signo, action);
531 RETSIGTYPE XCursesSigwinchHandler(int signo)
533 PDC_LOG(("%s:XCursesSigwinchHandler() - called: SIGNO: %d\n",
536 /* Patch by: Georg Fuchs, georg.fuchs@rz.uni-regensburg.de
541 /* Always trap SIGWINCH if the C library supports SIGWINCH */
544 XCursesSetSignal(SIGWINCH, XCursesSigwinchHandler);
548 /* Convert character positions x and y to pixel positions, stored in
551 static void _make_xy(int x, int y, int *xpos, int *ypos)
553 *xpos = (x * font_width) + xc_app_data.borderWidth;
554 *ypos = xc_app_data.normalFont->ascent + (y * font_height) +
555 xc_app_data.borderWidth;
558 /* Output a block of characters with common attributes */
560 static int _new_packet(chtype attr, bool rev, int len, int col, int row,
571 PDC_pair_content(PAIR_NUMBER(attr), &fore, &back);
574 text[len].byte1 = text[len].byte2 = 0;
579 /* Specify the color table offsets */
581 fore |= (attr & A_BOLD) ? 8 : 0;
582 back |= (attr & A_BLINK) ? 8 : 0;
584 /* Reverse flag = highlighted selection XOR A_REVERSE set */
586 rev ^= !!(attr & A_REVERSE);
588 /* Determine which GC to use - normal or italic */
590 gc = (attr & A_ITALIC) ? italic_gc : normal_gc;
594 XSetForeground(XCURSESDISPLAY, gc, colors[rev ? back : fore]);
595 XSetBackground(XCURSESDISPLAY, gc, colors[rev ? fore : back]);
597 _make_xy(col, row, &xpos, &ypos);
604 XCURSESDISPLAY, XCURSESWIN, gc, xpos, ypos, text, len);
606 /* Underline, etc. */
608 if (attr & (A_LEFTLINE|A_RIGHTLINE|A_UNDERLINE))
612 if (SP->line_color != -1)
613 XSetForeground(XCURSESDISPLAY, gc, colors[SP->line_color]);
615 if (attr & A_UNDERLINE) /* UNDER */
616 XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc,
617 xpos, ypos + 1, xpos + font_width * len, ypos + 1);
619 if (attr & A_LEFTLINE) /* LEFT */
620 for (k = 0; k < len; k++)
622 int x = xpos + font_width * k - 1;
623 XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc,
624 x, ypos - font_ascent, x, ypos + font_descent);
627 if (attr & A_RIGHTLINE) /* RIGHT */
628 for (k = 0; k < len; k++)
630 int x = xpos + font_width * (k + 1) - 1;
631 XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc,
632 x, ypos - font_ascent, x, ypos + font_descent);
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));
643 /* The core display routine -- update one line of text */
645 static int _display_text(const chtype *ch, int row, int col,
646 int num_cols, bool highlight)
653 chtype old_attr, attr;
656 PDC_LOG(("%s:_display_text() - called: row: %d col: %d "
657 "num_cols: %d\n", XCLOGMSG, row, col, num_cols));
662 old_attr = *ch & A_ATTRIBUTES;
664 for (i = 0, j = 0; j < num_cols; j++)
668 attr = curr & A_ATTRIBUTES;
671 if (attr & A_ALTCHARSET && !(curr & 0xff80))
673 attr ^= A_ALTCHARSET;
674 curr = acs_map[curr & 0x7f];
679 /* Special handling for ACS_BLOCK */
681 if (!(curr & A_CHARTEXT))
687 if (attr != old_attr)
689 if (_new_packet(old_attr, highlight, i, col, row, text) == ERR)
698 text[i].byte1 = (curr & 0xff00) >> 8;
699 text[i++].byte2 = curr & 0x00ff;
701 text[i++] = curr & 0xff;
705 return _new_packet(old_attr, highlight, i, col, row, text);
708 static void _get_gc(GC *gc, XFontStruct *font_info, int fore, int back)
712 /* Create default Graphics Context */
714 *gc = XCreateGC(XCURSESDISPLAY, XCURSESWIN, 0L, &values);
718 XSetFont(XCURSESDISPLAY, *gc, font_info->fid);
720 XSetForeground(XCURSESDISPLAY, *gc, colors[fore]);
721 XSetBackground(XCURSESDISPLAY, *gc, colors[back]);
724 static void _initialize_colors(void)
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;
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;
744 colors[COLOR_CURSOR] = xc_app_data.cursorColor;
745 colors[COLOR_BORDER] = xc_app_data.borderColor;
748 static void _refresh_scrollbar(void)
750 XC_LOG(("_refresh_scrollbar() - called\n"));
754 PDC_SCROLLBAR_TYPE total_y = SP->sb_total_y;
755 PDC_SCROLLBAR_TYPE total_x = SP->sb_total_x;
758 XawScrollbarSetThumb(scrollVert,
759 (PDC_SCROLLBAR_TYPE)(SP->sb_cur_y) / total_y,
760 (PDC_SCROLLBAR_TYPE)(SP->sb_viewport_y) / total_y);
763 XawScrollbarSetThumb(scrollHoriz,
764 (PDC_SCROLLBAR_TYPE)(SP->sb_cur_x) / total_x,
765 (PDC_SCROLLBAR_TYPE)(SP->sb_viewport_x) / total_x);
769 static void _set_cursor_color(chtype *ch, short *fore, short *back)
774 attr = PAIR_NUMBER(*ch);
778 PDC_pair_content(attr, &f, &b);
797 static void _get_icon(void)
799 XIconSize *icon_size;
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;
806 XC_LOG(("_get_icon() - called\n"));
808 icon_size = XAllocIconSize();
810 rc = XGetIconSizes(XtDisplay(topLevel),
811 RootWindowOfScreen(XtScreen(topLevel)),
812 &icon_size, &size_count);
814 /* if the WM can advise on icon sizes... */
816 if (rc && size_count)
818 int i, max_height = 0, max_width = 0;
820 PDC_LOG(("%s:size_count: %d rc: %d\n", XCLOGMSG, size_count, rc));
822 for (i = 0; i < size_count; i++)
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;
829 PDC_LOG(("%s:min: %d %d\n", XCLOGMSG,
830 icon_size[i].min_width, icon_size[i].min_height));
832 PDC_LOG(("%s:max: %d %d\n", XCLOGMSG,
833 icon_size[i].max_width, icon_size[i].max_height));
835 PDC_LOG(("%s:inc: %d %d\n", XCLOGMSG,
836 icon_size[i].width_inc, icon_size[i].height_inc));
839 if (max_width >= big_icon_width && max_height >= big_icon_height)
841 icon_bitmap_width = big_icon_width;
842 icon_bitmap_height = big_icon_height;
843 bitmap_bits = (unsigned char *)big_icon_bits;
847 icon_bitmap_width = little_icon_width;
848 icon_bitmap_height = little_icon_height;
849 bitmap_bits = (unsigned char *)little_icon_bits;
853 else /* use small icon */
855 icon_bitmap_width = little_icon_width;
856 icon_bitmap_height = little_icon_height;
857 bitmap_bits = (unsigned char *)little_icon_bits;
863 if (xc_app_data.pixmap && xc_app_data.pixmap[0]) /* supplied pixmap */
865 XpmReadFileToPixmap(XtDisplay(topLevel),
866 RootWindowOfScreen(XtScreen(topLevel)),
867 (char *)xc_app_data.pixmap,
868 &icon_pixmap, &icon_pixmap_mask, NULL);
873 if (xc_app_data.bitmap && xc_app_data.bitmap[0]) /* supplied bitmap */
875 int x_hot = 0, y_hot = 0;
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);
885 case BitmapOpenFailed:
886 fprintf(stderr, "bitmap file %s: not found\n",
889 case BitmapFileInvalid:
890 fprintf(stderr, "bitmap file %s: contents invalid\n",
898 icon_bitmap = XCreateBitmapFromData(XtDisplay(topLevel),
899 RootWindowOfScreen(XtScreen(topLevel)),
900 (char *)bitmap_bits, icon_bitmap_width, icon_bitmap_height);
903 static void _draw_border(void)
905 /* Draw the border if required */
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);
915 /* Redraw the entire screen */
917 static void _display_screen(void)
921 XC_LOG(("_display_screen() - called\n"));
923 for (row = 0; row < XCursesLINES; row++)
925 XC_get_line_lock(row);
927 _display_text((const chtype *)(Xcurscr + XCURSCR_Y_OFF(row)),
928 row, 0, COLS, FALSE);
930 XC_release_line_lock(row);
937 /* Draw changed portions of the screen */
939 static void _refresh_screen(void)
941 int row, start_col, num_cols;
943 XC_LOG(("_refresh_screen() - called\n"));
945 for (row = 0; row < XCursesLINES; row++)
947 num_cols = (int)*(Xcurscr + XCURSCR_LENGTH_OFF + row);
951 XC_get_line_lock(row);
953 start_col = (int)*(Xcurscr + XCURSCR_START_OFF + row);
955 _display_text((const chtype *)(Xcurscr + XCURSCR_Y_OFF(row) +
956 (start_col * sizeof(chtype))), row, start_col,
959 *(Xcurscr + XCURSCR_LENGTH_OFF + row) = 0;
961 XC_release_line_lock(row);
969 static void _handle_expose(Widget w, XtPointer client_data, XEvent *event,
972 XC_LOG(("_handle_expose() - called\n"));
974 /* ignore all Exposes except last */
976 if (event->xexpose.count)
979 if (after_first_curses_request && received_map_notify)
983 static void _handle_nonmaskable(Widget w, XtPointer client_data, XEvent *event,
986 XClientMessageEvent *client_event = (XClientMessageEvent *)event;
988 PDC_LOG(("%s:_handle_nonmaskable called: xc_otherpid %d event %d\n",
989 XCLOGMSG, xc_otherpid, event->type));
991 if (event->type == ClientMessage)
993 XC_LOG(("ClientMessage received\n"));
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. */
999 if ((Atom)client_event->data.s[0] == wm_atom[0])
1000 _exit_process(0, SIGKILL, "");
1004 static void XCursesKeyPress(Widget w, XEvent *event, String *params,
1007 enum { STATE_NORMAL, STATE_COMPOSE, STATE_CHAR };
1011 wchar_t buffer[120];
1013 unsigned char buffer[120];
1014 XComposeStatus compose;
1015 static int compose_state = STATE_NORMAL;
1016 static int compose_index = 0;
1019 unsigned long key = 0;
1022 unsigned long modifier = 0;
1023 bool key_code = FALSE;
1025 XC_LOG(("XCursesKeyPress() - called\n"));
1027 /* Handle modifier keys first; ignore other KeyReleases */
1029 if (event->type == KeyRelease)
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) */
1034 if (SP->return_key_modifiers &&
1036 keysym != compose_key &&
1038 IsModifierKey(keysym))
1048 key = KEY_CONTROL_L;
1051 key = KEY_CONTROL_R;
1061 _send_key_to_curses(key, NULL, TRUE);
1070 count = XwcLookupString(Xic, &(event->xkey), buffer, buflen,
1073 count = XLookupString(&(event->xkey), (char *)buffer, buflen,
1077 /* translate keysym into curses key code */
1079 PDC_LOG(("%s:Key mask: %x\n", XCLOGMSG, event->xkey.state));
1082 for (i = 0; i < 4; i++)
1083 PDC_debug("%s:Keysym %x %d\n", XCLOGMSG,
1084 XKeycodeToKeysym(XCURSESDISPLAY, event->xkey.keycode, i), i);
1089 /* Check if the key just pressed is the user-specified compose
1090 key; if it is, set the compose state and exit. */
1092 if (keysym == compose_key)
1095 int xpos, ypos, save_visibility = SP->visibility;
1096 short fore = 0, back = 0;
1098 /* Change the shape of the cursor to an outline rectangle to
1099 indicate we are in "compose" status */
1105 SP->visibility = save_visibility;
1106 _make_xy(SP->curscol, SP->cursrow, &xpos, &ypos);
1108 ch = (chtype *)(Xcurscr + XCURSCR_Y_OFF(SP->cursrow) +
1109 (SP->curscol * sizeof(chtype)));
1111 _set_cursor_color(ch, &fore, &back);
1113 XSetForeground(XCURSESDISPLAY, rect_cursor_gc, colors[back]);
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);
1120 compose_state = STATE_COMPOSE;
1124 switch (compose_state)
1127 if (IsModifierKey(keysym))
1130 if (event->xkey.state & compose_mask)
1132 compose_state = STATE_NORMAL;
1137 if (buffer[0] && count == 1)
1142 for (i = 0; i < (int)strlen(compose_chars); i++)
1143 if (compose_chars[i] == key)
1149 if (compose_index == -1)
1151 compose_state = STATE_NORMAL;
1157 compose_state = STATE_CHAR;
1161 if (IsModifierKey(keysym))
1164 if (event->xkey.state & compose_mask)
1166 compose_state = STATE_NORMAL;
1171 if (buffer[0] && count == 1)
1176 for (i = 0; i < MAX_COMPOSE_CHARS; i++)
1177 if (compose_lookups[compose_index][i] == key)
1185 compose_state = STATE_NORMAL;
1191 _send_key_to_curses(compose_keys[compose_index][char_idx],
1194 compose_state = STATE_NORMAL;
1202 #endif /* PDC_XIM */
1204 /* To get here we are procesing "normal" keys */
1206 PDC_LOG(("%s:Keysym %x %d\n", XCLOGMSG,
1207 XKeycodeToKeysym(XCURSESDISPLAY, event->xkey.keycode, key), key));
1209 if (SP->save_key_modifiers)
1211 /* 0x10: usually, numlock modifier */
1213 if (event->xkey.state & Mod2Mask)
1214 modifier |= PDC_KEY_MODIFIER_NUMLOCK;
1216 /* 0x01: shift modifier */
1218 if (event->xkey.state & ShiftMask)
1219 modifier |= PDC_KEY_MODIFIER_SHIFT;
1221 /* 0x04: control modifier */
1223 if (event->xkey.state & ControlMask)
1224 modifier |= PDC_KEY_MODIFIER_CONTROL;
1226 /* 0x08: usually, alt modifier */
1228 if (event->xkey.state & Mod1Mask)
1229 modifier |= PDC_KEY_MODIFIER_ALT;
1232 for (i = 0; key_table[i].keycode; i++)
1234 if (key_table[i].keycode == keysym)
1236 PDC_LOG(("%s:State %x\n", XCLOGMSG, event->xkey.state));
1238 /* ControlMask: 0x04: control modifier
1239 Mod1Mask: 0x08: usually, alt modifier
1240 Mod2Mask: 0x10: usually, numlock modifier
1241 ShiftMask: 0x01: shift modifier */
1243 if ((event->xkey.state & ShiftMask) ||
1244 (key_table[i].numkeypad &&
1245 (event->xkey.state & Mod2Mask)))
1247 key = key_table[i].shifted;
1249 else if (event->xkey.state & ControlMask)
1251 key = key_table[i].control;
1253 else if (event->xkey.state & Mod1Mask)
1255 key = key_table[i].alt;
1258 /* To get here, we ignore all other modifiers */
1261 key = key_table[i].normal;
1263 key_code = (key > 0x100);
1268 if (!key && buffer[0] && count == 1)
1271 PDC_LOG(("%s:Key: %s pressed - %x Mod: %x\n", XCLOGMSG,
1272 XKeysymToString(keysym), key, event->xkey.state));
1274 /* Handle ALT letters and numbers */
1276 if (event->xkey.state == Mod1Mask)
1278 if (key >= 'A' && key <= 'Z')
1284 if (key >= 'a' && key <= 'z')
1290 if (key >= '0' && key <= '9')
1297 /* After all that, send the key back to the application if is
1302 key |= (modifier << 24);
1304 _send_key_to_curses(key, NULL, key_code);
1308 static void XCursesHandleString(Widget w, XEvent *event, String *params,
1316 ptr = (unsigned char *)*params;
1318 if (ptr[0] == '0' && ptr[1] == 'x' && ptr[2] != '\0')
1321 unsigned long total = 0;
1323 for (ptr += 2; (c = tolower(*ptr)); ptr++)
1327 if (c >= '0' && c <= '9')
1330 if (c >= 'a' && c <= 'f')
1331 total += c - ('a' - 10);
1337 _send_key_to_curses(total, NULL, FALSE);
1341 _send_key_to_curses((unsigned long)*ptr, NULL, FALSE);
1344 static void _paste_string(Widget w, XtPointer data, Atom *selection, Atom *type,
1345 XtPointer value, unsigned long *length, int *format)
1347 unsigned long i, key;
1348 unsigned char *string = value;
1350 XC_LOG(("_paste_string() - called\n"));
1352 if (!*type || !*length || !string)
1355 for (i = 0; string[i] && (i < (*length)); i++)
1359 if (key == 10) /* new line - convert to ^M */
1362 _send_key_to_curses(key, NULL, FALSE);
1368 static void _paste_utf8(Widget w, XtPointer event, Atom *selection, Atom *type,
1369 XtPointer value, unsigned long *length, int *format)
1373 char *string = value;
1375 XC_LOG(("_paste_utf8() - called\n"));
1377 if (!*type || !*length)
1379 XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, _paste_string,
1380 event, ((XButtonEvent *)event)->time);
1389 while (string[i] && (i < len))
1391 int retval = _from_utf8(&key, string + i, len - i);
1396 if (key == 10) /* new line - convert to ^M */
1399 _send_key_to_curses(key, NULL, FALSE);
1407 static void XCursesPasteSelection(Widget w, XButtonEvent *button_event)
1409 XC_LOG(("XCursesPasteSelection() - called\n"));
1411 XtGetSelectionValue(w, XA_PRIMARY, XA_UTF8_STRING(XtDisplay(w)),
1412 _paste_utf8, (XtPointer)button_event,
1413 button_event->time);
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)
1420 XC_LOG(("_convert_proc() - called\n"));
1422 if (*target == XA_TARGETS(XtDisplay(topLevel)))
1424 XSelectionRequestEvent *req = XtGetSelectionRequest(w,
1425 *selection, (XtRequestId)NULL);
1428 XPointer std_targets;
1429 unsigned long std_length;
1431 XmuConvertStandardSelection(topLevel, req->time, selection,
1432 target, type_return, &std_targets,
1433 &std_length, format_return);
1435 *length_return = std_length + 2;
1436 *value_return = XtMalloc(sizeof(Atom) * (*length_return));
1438 targetP = *(Atom**)value_return;
1439 *targetP++ = XA_STRING;
1440 *targetP++ = XA_UTF8_STRING(XtDisplay(topLevel));
1442 memmove((void *)targetP, (const void *)std_targets,
1443 sizeof(Atom) * std_length);
1445 XtFree((char *)std_targets);
1446 *type_return = XA_ATOM;
1447 *format_return = sizeof(Atom) * 8;
1451 else if (*target == XA_UTF8_STRING(XtDisplay(topLevel)) ||
1452 *target == XA_STRING)
1454 bool utf8 = !(*target == XA_STRING);
1455 char *data = XtMalloc(tmpsel_length * 3 + 1);
1456 chtype *tmp = tmpsel;
1462 ret_length += _to_utf8(data + ret_length, *tmp++);
1466 data[ret_length++] = *tmp++ & 0xff;
1468 data[ret_length++] = '\0';
1470 *value_return = data;
1471 *length_return = ret_length;
1473 *type_return = *target;
1478 return XmuConvertStandardSelection(topLevel, CurrentTime,
1479 selection, target, type_return, (XPointer*)value_return,
1480 length_return, format_return);
1483 static void _lose_ownership(Widget w, Atom *type)
1485 XC_LOG(("_lose_ownership() - called\n"));
1495 static void _show_selection(int start_x, int start_y, int end_x, int end_y,
1498 int i, num_cols, start_col, row;
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));
1504 for (i = 0; i < end_y - start_y + 1; i++)
1506 if (start_y == end_y) /* only one line */
1508 start_col = start_x;
1509 num_cols = end_x - start_x + 1;
1512 else if (!i) /* first line */
1514 start_col = start_x;
1515 num_cols = COLS - start_x;
1518 else if (start_y + i == end_y) /* last line */
1521 num_cols = end_x + 1;
1524 else /* full line */
1531 XC_get_line_lock(row);
1533 _display_text((const chtype *)(Xcurscr + XCURSCR_Y_OFF(row) +
1534 (start_col * sizeof(chtype))), row, start_col,
1535 num_cols, highlight);
1537 XC_release_line_lock(row);
1541 static void _selection_off(void)
1543 XC_LOG(("_selection_off() - called\n"));
1547 selection_start_x = selection_start_y = selection_end_x =
1548 selection_end_y = 0;
1550 mouse_selection = FALSE;
1553 static void _selection_on(int x, int y)
1555 XC_LOG(("_selection_on() - called\n"));
1557 selection_start_x = selection_end_x = x;
1558 selection_start_y = selection_end_y = y;
1561 static void _selection_extend(int x, int y)
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;
1567 XC_LOG(("_selection_extend() - called\n"));
1569 mouse_selection = TRUE;
1571 /* convert x/y coordinates into start/stop */
1573 current_start = (selection_start_y * COLS) + selection_start_x;
1574 current_end = (selection_end_y * COLS) + selection_end_x;
1576 if (current_start > current_end)
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;
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;
1594 /* Now we have the current selection as a linear expression.
1595 Convert the new position to a linear expression. */
1597 selection_end_x = x;
1598 selection_end_y = y;
1600 /* convert x/y coordinates into start/stop */
1602 new_start = (selection_start_y * COLS) + selection_start_x;
1603 new_end = (selection_end_y * COLS) + selection_end_x;
1605 if (new_start > new_end)
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;
1612 new_start = new_end;
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;
1623 if (new_end > current_end)
1624 _show_selection(current_end_x, current_end_y, new_end_x,
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);
1636 _show_selection(current_start_x, current_start_y,
1637 new_start_x, new_start_y, TRUE);
1640 static void _selection_set(void)
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;
1646 XC_LOG(("_selection_set() - called\n"));
1648 /* convert x/y coordinates into start/stop */
1650 start = (selection_start_y * COLS) + selection_start_x;
1651 end = (selection_end_y * COLS) + selection_end_x;
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;
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;
1681 newlen = length + end_y - start_y + 2;
1683 if (length > (int)tmpsel_length)
1686 tmpsel = malloc(newlen * sizeof(chtype));
1688 tmpsel = realloc(tmpsel, newlen * sizeof(chtype));
1697 tmpsel_length = length;
1700 for (i = 0; i < end_y - start_y + 1; i++)
1703 if (start_y == end_y) /* only one line */
1705 start_col = start_x;
1706 num_cols = end_x - start_x + 1;
1709 else if (!i) /* first line */
1711 start_col = start_x;
1712 num_cols = COLS - start_x;
1715 else if (start_y + i == end_y) /* last line */
1718 num_cols = end_x + 1;
1721 else /* full line */
1728 XC_get_line_lock(row);
1730 ptr = (chtype *)(Xcurscr + XCURSCR_Y_OFF(row) +
1731 start_col * sizeof(chtype));
1733 if (i < end_y - start_y)
1737 for (j = 0; j < num_cols; j++)
1739 ch = (int)(ptr[j] & A_CHARTEXT);
1745 last_nonblank = num_cols - 1;
1747 for (j = 0; j <= last_nonblank; j++)
1748 tmpsel[num_chars++] = ptr[j];
1750 XC_release_line_lock(row);
1752 if (i < end_y - start_y)
1753 tmpsel[num_chars++] = '\n';
1756 tmpsel[num_chars] = '\0';
1757 tmpsel_length = num_chars;
1760 static void _display_cursor(int old_row, int old_x, int new_row, int new_x)
1764 short fore = 0, back = 0;
1766 PDC_LOG(("%s:_display_cursor() - draw char at row: %d col %d\n",
1767 XCLOGMSG, old_row, old_x));
1769 /* if the cursor position is outside the boundary of the screen,
1770 ignore the request */
1772 if (old_row >= XCursesLINES || old_x >= COLS ||
1773 new_row >= XCursesLINES || new_x >= COLS)
1776 /* display the character at the current cursor position */
1778 PDC_LOG(("%s:_display_cursor() - draw char at row: %d col %d\n",
1779 XCLOGMSG, old_row, old_x));
1781 _display_text((const chtype *)(Xcurscr + (XCURSCR_Y_OFF(old_row) +
1782 (old_x * sizeof(chtype)))), old_row, old_x, 1, FALSE);
1784 /* display the cursor at the new cursor position */
1786 if (!SP->visibility)
1787 return; /* cursor not displayed, no more to do */
1789 _make_xy(new_x, new_row, &xpos, &ypos);
1791 ch = (chtype *)(Xcurscr + XCURSCR_Y_OFF(new_row) + new_x * sizeof(chtype));
1793 _set_cursor_color(ch, &fore, &back);
1795 if (vertical_cursor)
1797 XSetForeground(XCURSESDISPLAY, rect_cursor_gc, colors[back]);
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 +
1807 if (SP->visibility == 1)
1809 /* cursor visibility normal */
1811 XSetForeground(XCURSESDISPLAY, rect_cursor_gc, colors[back]);
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);
1819 /* cursor visibility high */
1823 buf[0].byte1 = (*ch & 0xff00) >> 8;
1824 buf[0].byte2 = *ch & 0x00ff;
1826 buf[1].byte1 = buf[1].byte2 = 0;
1830 buf[0] = *ch & 0xff;
1833 XSetForeground(XCURSESDISPLAY, block_cursor_gc, colors[fore]);
1834 XSetBackground(XCURSESDISPLAY, block_cursor_gc, colors[back]);
1840 XCURSESDISPLAY, XCURSESWIN, block_cursor_gc,
1841 xpos, ypos, buf, 1);
1845 PDC_LOG(("%s:_display_cursor() - draw cursor at row %d col %d\n",
1846 XCLOGMSG, new_row, new_x));
1849 static void _redraw_cursor(void)
1851 _display_cursor(SP->cursrow, SP->curscol, SP->cursrow, SP->curscol);
1854 static void _handle_enter_leave(Widget w, XtPointer client_data,
1855 XEvent *event, Boolean *unused)
1857 XC_LOG(("_handle_enter_leave called\n"));
1862 XC_LOG(("EnterNotify received\n"));
1864 window_entered = TRUE;
1868 XC_LOG(("LeaveNotify received\n"));
1870 window_entered = FALSE;
1872 /* Display the cursor so it stays on while the window is
1879 PDC_LOG(("%s:_handle_enter_leave - unknown event %d\n",
1880 XCLOGMSG, event->type));
1884 static void _send_key_to_curses(unsigned long key, MOUSE_STATUS *ms,
1887 PDC_LOG(("%s:_send_key_to_curses() - called: sending %d\n",
1890 SP->key_code = key_code;
1892 if (XC_write_socket(xc_key_sock, &key, sizeof(unsigned long)) < 0)
1893 _exit_process(1, SIGKILL, "exiting from _send_key_to_curses");
1897 MOUSE_LOG(("%s:writing mouse stuff\n", XCLOGMSG));
1899 if (XC_write_socket(xc_key_sock, ms, sizeof(MOUSE_STATUS)) < 0)
1900 _exit_process(1, SIGKILL, "exiting from _send_key_to_curses");
1904 static void _blink_cursor(XtPointer unused, XtIntervalId *id)
1906 XC_LOG(("_blink_cursor() - called:\n"));
1912 /* Cursor currently ON, turn it off */
1914 int save_visibility = SP->visibility;
1917 SP->visibility = save_visibility;
1918 visible_cursor = FALSE;
1922 /* Cursor currently OFF, turn it on */
1925 visible_cursor = TRUE;
1929 XtAppAddTimeOut(app_context, xc_app_data.cursorBlinkRate,
1930 _blink_cursor, NULL);
1933 static void XCursesButton(Widget w, XEvent *event, String *params,
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;
1944 XC_LOG(("XCursesButton() - called\n"));
1946 keysym = 0; /* suppress any modifier key return */
1948 save_mouse_status = Mouse_status;
1949 button_no = event->xbutton.button;
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
1957 button_no = last_button_no;
1959 last_button_no = button_no;
1961 Mouse_status.changes = 0;
1966 /* Handle button 4 and 5, which are normally mapped to the wheel
1967 mouse scroll up and down */
1969 if (button_no == 4 || button_no == 5)
1971 /* Send the KEY_MOUSE to curses program */
1973 memset(&Mouse_status, 0, sizeof(Mouse_status));
1975 Mouse_status.changes = (button_no == 5) ?
1976 PDC_MOUSE_WHEEL_DOWN : PDC_MOUSE_WHEEL_UP;
1978 MOUSE_X_POS = MOUSE_Y_POS = -1;
1979 _send_key_to_curses(KEY_MOUSE, &Mouse_status, TRUE);
1980 remove_release = TRUE;
1985 if (button_no == 2 &&
1986 (!SP->_trap_mbe || (event->xbutton.state & ShiftMask)))
1988 XCursesPasteSelection(drawing, (XButtonEvent *)event);
1989 remove_release = TRUE;
1994 remove_release = False;
1995 handle_real_release = False;
1997 MOUSE_LOG(("\nButtonPress\n"));
1999 if ((event->xbutton.time - last_button_press_time) <
2000 xc_app_data.doubleClickPeriod)
2002 MOUSE_X_POS = save_mouse_status.x;
2003 MOUSE_Y_POS = save_mouse_status.y;
2004 BUTTON_STATUS(button_no) = BUTTON_DOUBLE_CLICKED;
2007 remove_release = True;
2011 napms(SP->mouse_wait);
2012 event->type = ButtonRelease;
2013 XSendEvent(event->xbutton.display, event->xbutton.window,
2015 last_button_press_time = event->xbutton.time;
2020 last_button_press_time = event->xbutton.time;
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));
2028 MOUSE_X_POS = (event->xbutton.x - xc_app_data.borderWidth) /
2030 MOUSE_Y_POS = (event->xbutton.y - xc_app_data.borderWidth) /
2033 if (button_no == 1 &&
2034 (!SP->_trap_mbe || (event->xbutton.state & ShiftMask)))
2036 _selection_extend(MOUSE_X_POS, MOUSE_Y_POS);
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. */
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)
2054 Mouse_status.changes |= PDC_MOUSE_MOVED;
2060 MOUSE_LOG(("Release at: %ld - removed\n", event->xbutton.time));
2065 MOUSE_X_POS = (event->xbutton.x - xc_app_data.borderWidth) /
2067 MOUSE_Y_POS = (event->xbutton.y - xc_app_data.borderWidth) /
2070 if (!handle_real_release)
2072 if ((event->xbutton.time - last_button_press_time) <
2074 (event->xbutton.time != last_button_press_time))
2076 /* The "real" release was shorter than usleep() time;
2077 therefore generate a click event */
2079 MOUSE_LOG(("Release at: %ld - click\n",
2080 event->xbutton.time));
2082 BUTTON_STATUS(button_no) = BUTTON_CLICKED;
2084 if (button_no == 1 && mouse_selection &&
2085 (!SP->_trap_mbe || (event->xbutton.state & ShiftMask)))
2089 if (XtOwnSelection(topLevel, XA_PRIMARY,
2090 event->xbutton.time, _convert_proc,
2091 _lose_ownership, NULL) == False)
2097 /* Ensure the "pseudo" release event is ignored */
2099 remove_release = True;
2100 handle_real_release = False;
2105 /* Button release longer than usleep() time;
2106 therefore generate a press and wait for the real
2107 release to occur later. */
2109 MOUSE_LOG(("Generated Release at: %ld - "
2110 "press & release\n", event->xbutton.time));
2112 BUTTON_STATUS(button_no) = BUTTON_PRESSED;
2114 if (button_no == 1 &&
2115 (!SP->_trap_mbe || (event->xbutton.state & ShiftMask)))
2118 _selection_on(MOUSE_X_POS, MOUSE_Y_POS);
2121 handle_real_release = True;
2127 MOUSE_LOG(("Release at: %ld - released\n",
2128 event->xbutton.time));
2132 MOUSE_LOG(("\nButtonRelease\n"));
2134 BUTTON_STATUS(button_no) = BUTTON_RELEASED;
2136 if (button_no == 1 && mouse_selection &&
2137 (!SP->_trap_mbe || (event->xbutton.state & ShiftMask)))
2141 if (XtOwnSelection(topLevel, XA_PRIMARY,
2142 event->xbutton.time, _convert_proc,
2143 _lose_ownership, NULL) == False)
2154 /* Set up the mouse status fields in preparation for sending */
2156 Mouse_status.changes |= 1 << (button_no - 1);
2158 if (Mouse_status.changes & PDC_MOUSE_MOVED &&
2159 BUTTON_STATUS(button_no) == BUTTON_PRESSED)
2160 BUTTON_STATUS(button_no) = BUTTON_MOVED;
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;
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 */
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));
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));
2180 if (!send_key || MOUSE_X_POS < 0 || MOUSE_X_POS >= XCursesCOLS ||
2181 MOUSE_Y_POS < 0 || MOUSE_Y_POS >= XCursesLINES)
2184 /* Send the KEY_MOUSE to curses program */
2186 _send_key_to_curses(KEY_MOUSE, &Mouse_status, TRUE);
2189 static void _scroll_up_down(Widget w, XtPointer client_data,
2190 XtPointer call_data)
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;
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. */
2203 /* limit panning to size of overall */
2208 if (cur_y > (total_y - viewport_y))
2209 cur_y = total_y - viewport_y;
2211 SP->sb_cur_y = cur_y / font_height;
2213 XawScrollbarSetThumb(w, (double)((double)cur_y / (double)total_y),
2214 (double)((double)viewport_y / (double)total_y));
2216 /* Send a key: if pixels negative, send KEY_SCROLL_DOWN */
2218 _send_key_to_curses(KEY_SF, NULL, TRUE);
2221 static void _scroll_left_right(Widget w, XtPointer client_data,
2222 XtPointer call_data)
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;
2231 /* limit panning to size of overall */
2236 if (cur_x > (total_x - viewport_x))
2237 cur_x = total_x - viewport_x;
2239 SP->sb_cur_x = cur_x / font_width;
2241 XawScrollbarSetThumb(w, (double)((double)cur_x / (double)total_x),
2242 (double)((double)viewport_x / (double)total_x));
2244 _send_key_to_curses(KEY_SR, NULL, TRUE);
2247 static void _thumb_up_down(Widget w, XtPointer client_data,
2248 XtPointer call_data)
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;
2255 /* If the size of the viewport is > overall area simply return,
2256 as no scrolling is permitted. */
2258 if (SP->sb_viewport_y >= SP->sb_total_y)
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;
2265 XawScrollbarSetThumb(w, (double)(cur_y / total_y),
2266 (double)(viewport_y / total_y));
2268 _send_key_to_curses(KEY_SF, NULL, TRUE);
2271 static void _thumb_left_right(Widget w, XtPointer client_data,
2272 XtPointer call_data)
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;
2279 if (SP->sb_viewport_x >= SP->sb_total_x)
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;
2286 XawScrollbarSetThumb(w, (double)(cur_x / total_x),
2287 (double)(viewport_x / total_x));
2289 _send_key_to_curses(KEY_SR, NULL, TRUE);
2292 static void _exit_process(int rc, int sig, char *msg)
2295 fprintf(stderr, "%s:_exit_process() - called: rc:%d sig:%d <%s>\n",
2296 XCLOGMSG, rc, sig, msg);
2299 shmdt((char *)Xcurscr);
2300 shmctl(shmidSP, IPC_RMID, 0);
2301 shmctl(shmid_Xcurscr, IPC_RMID, 0);
2305 XFreePixmap(XCURSESDISPLAY, icon_bitmap);
2312 XFreePixmap(XCURSESDISPLAY, icon_pixmap);
2313 XFreePixmap(XCURSESDISPLAY, icon_pixmap_mask);
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);
2326 shutdown(xc_display_sock, 2);
2327 close(xc_display_sock);
2329 shutdown(xc_exit_sock, 2);
2330 close(xc_exit_sock);
2332 shutdown(xc_key_sock, 2);
2336 kill(xc_otherpid, sig); /* to kill parent process */
2341 static void _resize(void)
2343 short save_atrtab[PDC_COLOR_PAIRS * 2];
2345 after_first_curses_request = FALSE;
2347 SP->lines = XCursesLINES = ((resize_window_height -
2348 (2 * xc_app_data.borderWidth)) / font_height);
2350 LINES = XCursesLINES - SP->linesrippedoff - SP->slklines;
2352 SP->cols = COLS = XCursesCOLS = ((resize_window_width -
2353 (2 * xc_app_data.borderWidth)) / font_width);
2355 window_width = resize_window_width;
2356 window_height = resize_window_height;
2357 visible_cursor = TRUE;
2361 /* Detach and drop the current shared memory segment and create and
2362 attach to a new segment */
2364 memcpy(save_atrtab, xc_atrtab, sizeof(save_atrtab));
2366 SP->XcurscrSize = XCURSCR_SIZE;
2367 shmdt((char *)Xcurscr);
2368 shmctl(shmid_Xcurscr, IPC_RMID, 0);
2370 if ((shmid_Xcurscr = shmget(shmkey_Xcurscr,
2371 SP->XcurscrSize + XCURSESSHMMIN, 0700 | IPC_CREAT)) < 0)
2373 perror("Cannot allocate shared memory for curscr");
2375 _exit_process(4, SIGKILL, "exiting from _process_curses_requests");
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));
2384 /* For PDC_set_title() */
2386 static void _set_title(void)
2388 char title[1024]; /* big enough for window title */
2391 if ((XC_read_socket(xc_display_sock, &pos, sizeof(int)) < 0) ||
2392 (XC_read_socket(xc_display_sock, title, pos) < 0))
2394 _exit_process(5, SIGKILL, "exiting from _set_title");
2397 XtVaSetValues(topLevel, XtNtitle, title, NULL);
2400 /* For color_content() */
2402 static void _get_color(void)
2404 XColor *tmp = (XColor *)(Xcurscr + XCURSCR_XCOLOR_OFF);
2405 int index = tmp->pixel;
2406 Colormap cmap = DefaultColormap(XCURSESDISPLAY,
2407 DefaultScreen(XCURSESDISPLAY));
2409 if (index < 0 || index >= MAX_COLORS)
2410 _exit_process(4, SIGKILL, "exiting from _get_color");
2412 tmp->pixel = colors[index];
2413 XQueryColor(XCURSESDISPLAY, cmap, tmp);
2416 /* For init_color() */
2418 static void _set_color(void)
2420 XColor *tmp = (XColor *)(Xcurscr + XCURSCR_XCOLOR_OFF);
2421 int index = tmp->pixel;
2422 Colormap cmap = DefaultColormap(XCURSESDISPLAY,
2423 DefaultScreen(XCURSESDISPLAY));
2425 if (index < 0 || index >= MAX_COLORS)
2426 _exit_process(4, SIGKILL, "exiting from _set_color");
2428 if (XAllocColor(XCURSESDISPLAY, cmap, tmp))
2430 XFreeColors(XCURSESDISPLAY, cmap, colors + index, 1, 0);
2431 colors[index] = tmp->pixel;
2437 /* For PDC_getclipboard() */
2439 static void _get_selection(Widget w, XtPointer data, Atom *selection,
2440 Atom *type, XtPointer value,
2441 unsigned long *length, int *format)
2443 unsigned char *src = value;
2444 int pos, len = *length;
2446 XC_LOG(("_get_selection() - called\n"));
2450 if (XC_write_display_socket_int(PDC_CLIP_EMPTY) < 0)
2451 _exit_process(4, SIGKILL, "exiting from _get_selection");
2455 /* Here all is OK, send PDC_CLIP_SUCCESS, then length, then
2458 if (XC_write_display_socket_int(PDC_CLIP_SUCCESS) < 0)
2459 _exit_process(4, SIGKILL, "exiting from _get_selection");
2461 if (XC_write_display_socket_int(len) < 0)
2462 _exit_process(4, SIGKILL, "exiting from _get_selection");
2464 for (pos = 0; pos < len; pos++)
2473 if (XC_write_socket(xc_display_sock, &c, sizeof(c)) < 0)
2474 _exit_process(4, SIGKILL, "exiting from _get_selection");
2480 static void _get_selection_utf8(Widget w, XtPointer data, Atom *selection,
2481 Atom *type, XtPointer value,
2482 unsigned long *length, int *format)
2486 XC_LOG(("_get_selection_utf8() - called\n"));
2488 if (!*type || !*length)
2490 XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, _get_selection,
2491 (XtPointer)NULL, 0);
2497 if (XC_write_display_socket_int(PDC_CLIP_EMPTY) >= 0)
2502 wchar_t *wcontents = malloc((len + 1) * sizeof(wchar_t));
2506 while (*src && i < (*length))
2508 int retval = _from_utf8(wcontents + i, src, len);
2518 /* Here all is OK, send PDC_CLIP_SUCCESS, then length, then
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)
2531 _exit_process(4, SIGKILL, "exiting from _get_selection_utf8");
2535 /* For PDC_setclipboard() */
2537 static void _set_selection(void)
2542 if (XC_read_socket(xc_display_sock, &length, sizeof(long)) < 0)
2543 _exit_process(5, SIGKILL, "exiting from _set_selection");
2545 if (length > (long)tmpsel_length)
2548 tmpsel = malloc((length + 1) * sizeof(chtype));
2550 tmpsel = realloc(tmpsel, (length + 1) * sizeof(chtype));
2554 if (XC_write_display_socket_int(PDC_CLIP_MEMORY_ERROR) < 0)
2555 _exit_process(4, SIGKILL, "exiting from _set_selection");
2557 for (pos = 0; pos < length; pos++)
2564 if (XC_read_socket(xc_display_sock, &c, sizeof(c)) < 0)
2565 _exit_process(5, SIGKILL, "exiting from _set_selection");
2570 tmpsel_length = length;
2573 if (XtOwnSelection(topLevel, XA_PRIMARY, CurrentTime,
2574 _convert_proc, _lose_ownership, NULL) == False)
2576 status = PDC_CLIP_ACCESS_ERROR;
2582 status = PDC_CLIP_SUCCESS;
2586 if (XC_write_display_socket_int(status) < 0)
2587 _exit_process(4, SIGKILL, "exiting from _set_selection");
2590 /* The curses process is waiting; tell it to continue */
2592 static void _resume_curses(void)
2594 if (XC_write_display_socket_int(CURSES_CONTINUE) < 0)
2595 _exit_process(4, SIGKILL, "exiting from _process_curses_requests");
2598 /* The curses process sent us a message */
2600 static void _process_curses_requests(XtPointer client_data, int *fid,
2603 struct timeval socket_timeout = {0};
2605 int old_row, new_row;
2609 char buf[12]; /* big enough for 2 integers */
2611 XC_LOG(("_process_curses_requests() - called\n"));
2613 if (!received_map_notify)
2616 FD_ZERO(&xc_readfds);
2617 FD_SET(xc_display_sock, &xc_readfds);
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");
2624 if (!s) /* no requests pending - should never happen! */
2627 if (FD_ISSET(xc_display_sock, &xc_readfds))
2629 /* read first integer to determine total message has been
2632 XC_LOG(("_process_curses_requests() - before XC_read_socket()\n"));
2634 if (XC_read_socket(xc_display_sock, &num_cols, sizeof(int)) < 0)
2635 _exit_process(3, SIGKILL, "exiting from _process_curses_requests"
2638 XC_LOG(("_process_curses_requests() - after XC_read_socket()\n"));
2640 after_first_curses_request = TRUE;
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");
2650 XC_LOG(("CURSES_BELL received from child\n"));
2651 XBell(XCURSESDISPLAY, 50);
2654 /* request from curses to confirm completion of display */
2656 case CURSES_REFRESH:
2657 XC_LOG(("CURSES_REFRESH received from child\n"));
2662 case CURSES_REFRESH_SCROLLBAR:
2663 _refresh_scrollbar();
2667 XC_LOG(("CURSES_CURSOR received from child\n"));
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");
2673 memcpy(&pos, buf, sizeof(int));
2674 old_row = pos & 0xFF;
2677 memcpy(&pos, buf + sizeof(int), sizeof(int));
2678 new_row = pos & 0xFF;
2681 visible_cursor = TRUE;
2682 _display_cursor(old_row, old_x, new_row, new_x);
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"));
2689 /* If the window is not active, ignore this command. The
2690 cursor will stay solid. */
2696 /* Cursor currently ON, turn it off */
2698 int save_visibility = SP->visibility;
2701 SP->visibility = save_visibility;
2702 visible_cursor = FALSE;
2706 /* Cursor currently OFF, turn it on */
2709 visible_cursor = TRUE;
2716 XC_LOG(("CURSES_TITLE received from child\n"));
2721 XC_LOG(("CURSES_RESIZE received from child\n"));
2726 case CURSES_GET_SELECTION:
2727 XC_LOG(("CURSES_GET_SELECTION received from child\n"));
2731 XtGetSelectionValue(topLevel, XA_PRIMARY,
2733 XA_UTF8_STRING(XtDisplay(topLevel)),
2734 _get_selection_utf8,
2736 XA_STRING, _get_selection,
2738 (XtPointer)NULL, 0);
2742 case CURSES_SET_SELECTION:
2743 XC_LOG(("CURSES_SET_SELECTION received from child\n"));
2747 case CURSES_CLEAR_SELECTION:
2748 XC_LOG(("CURSES_CLEAR_SELECTION received from child\n"));
2753 case CURSES_GET_COLOR:
2754 XC_LOG(("CURSES_GET_COLOR recieved from child\n"));
2759 case CURSES_SET_COLOR:
2760 XC_LOG(("CURSES_SET_COLOR recieved from child\n"));
2766 PDC_LOG(("%s:Unknown request %d\n", XCLOGMSG, num_cols));
2771 static void _handle_structure_notify(Widget w, XtPointer client_data,
2772 XEvent *event, Boolean *unused)
2774 XC_LOG(("_handle_structure_notify() - called\n"));
2778 case ConfigureNotify:
2779 XC_LOG(("ConfigureNotify received\n"));
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. */
2785 resize_window_width = event->xconfigure.width;
2786 resize_window_height = event->xconfigure.height;
2788 after_first_curses_request = FALSE;
2793 kill(xc_otherpid, SIGWINCH);
2795 _send_key_to_curses(KEY_RESIZE, NULL, TRUE);
2799 XC_LOG(("MapNotify received\n"));
2801 received_map_notify = 1;
2807 PDC_LOG(("%s:_handle_structure_notify - unknown event %d\n",
2808 XCLOGMSG, event->type));
2812 static RETSIGTYPE _handle_signals(int signo)
2814 int flag = CURSES_EXIT;
2816 PDC_LOG(("%s:_handle_signals() - called: %d\n", XCLOGMSG, signo));
2818 /* Patch by: Georg Fuchs */
2820 XCursesSetSignal(signo, _handle_signals);
2823 if (signo == SIGTSTP)
2830 if (signo == SIGCONT)
2834 if (signo == SIGCLD)
2838 if (signo == SIGTTIN)
2842 if (signo == SIGWINCH)
2846 /* End of patch by: Georg Fuchs */
2848 XCursesSetSignal(signo, SIG_IGN);
2850 /* Send a CURSES_EXIT to myself */
2852 if (XC_write_socket(xc_exit_sock, &flag, sizeof(int)) < 0)
2853 _exit_process(7, signo, "exiting from _handle_signals");
2857 static void _dummy_handler(Widget w, XtPointer client_data,
2858 XEvent *event, Boolean *unused)
2863 int XCursesSetupX(int argc, char *argv[])
2865 char *myargv[] = {"PDCurses", NULL};
2866 extern bool sb_started;
2868 int italic_font_valid;
2869 XColor pointerforecolor, pointerbackcolor;
2870 XrmValue rmfrom, rmto;
2872 int minwidth, minheight;
2874 XC_LOG(("XCursesSetupX called\n"));
2882 program_name = argv[0];
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 */
2887 xc_exit_sock = xc_display_sockets[0];
2888 xc_display_sock = xc_display_sockets[1];
2890 close(xc_key_sockets[0]);
2891 xc_key_sock = xc_key_sockets[1];
2893 /* Trap all signals when XCurses is the child process, but only if
2894 they haven't already been ignored by the application. */
2896 for (i = 0; i < PDC_MAX_SIGNALS; i++)
2897 if (XCursesSetSignal(i, _handle_signals) == SIG_IGN)
2898 XCursesSetSignal(i, SIG_IGN);
2900 /* Start defining X Toolkit things */
2902 #if XtSpecificationRelease > 4
2903 XtSetLanguageProc(NULL, (XtLanguageProc)NULL, NULL);
2906 /* Exit if no DISPLAY variable set */
2908 if (!getenv("DISPLAY"))
2910 fprintf(stderr, "Error: no DISPLAY variable set\n");
2911 kill(xc_otherpid, SIGKILL);
2915 /* Initialise the top level widget */
2917 topLevel = XtVaAppInitialize(&app_context, class_name, options,
2918 XtNumber(options), &argc, argv, NULL, NULL);
2920 XtVaGetApplicationResources(topLevel, &xc_app_data, app_resources,
2921 XtNumber(app_resources), NULL);
2923 /* Check application resource values here */
2925 font_width = xc_app_data.normalFont->max_bounds.rbearing -
2926 xc_app_data.normalFont->min_bounds.lbearing;
2928 font_height = xc_app_data.normalFont->max_bounds.ascent +
2929 xc_app_data.normalFont->max_bounds.descent;
2931 font_ascent = xc_app_data.normalFont->max_bounds.ascent;
2932 font_descent = xc_app_data.normalFont->max_bounds.descent;
2934 /* Check that the italic font and normal fonts are the same size */
2935 /* This appears backwards */
2937 italic_font_valid = font_width !=
2938 xc_app_data.italicFont->max_bounds.rbearing -
2939 xc_app_data.italicFont->min_bounds.lbearing ||
2941 xc_app_data.italicFont->max_bounds.ascent +
2942 xc_app_data.italicFont->max_bounds.descent;
2944 /* Calculate size of display window */
2946 XCursesCOLS = xc_app_data.cols;
2947 XCursesLINES = xc_app_data.lines;
2949 window_width = font_width * XCursesCOLS +
2950 2 * xc_app_data.borderWidth;
2952 window_height = font_height * XCursesLINES +
2953 2 * xc_app_data.borderWidth;
2955 minwidth = font_width * 2 + xc_app_data.borderWidth * 2;
2956 minheight = font_height * 2 + xc_app_data.borderWidth * 2;
2958 /* Set up the icon for the application; the default is an internal
2959 one for PDCurses. Then set various application level resources. */
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);
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);
2977 /* Create a BOX widget in which to draw */
2979 if (xc_app_data.scrollbarWidth && sb_started)
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);
2987 drawing = XtVaCreateManagedWidget(program_name,
2988 boxWidgetClass, scrollBox, XtNwidth,
2989 window_width, XtNheight, window_height, XtNwidthInc,
2990 font_width, XtNheightInc, font_height, NULL);
2992 scrollVert = XtVaCreateManagedWidget("scrollVert",
2993 scrollbarWidgetClass, scrollBox, XtNorientation,
2994 XtorientVertical, XtNheight, window_height, XtNwidth,
2995 xc_app_data.scrollbarWidth, NULL);
2997 XtAddCallback(scrollVert, XtNscrollProc, _scroll_up_down, drawing);
2998 XtAddCallback(scrollVert, XtNjumpProc, _thumb_up_down, drawing);
3000 scrollHoriz = XtVaCreateManagedWidget("scrollHoriz",
3001 scrollbarWidgetClass, scrollBox, XtNorientation,
3002 XtorientHorizontal, XtNwidth, window_width, XtNheight,
3003 xc_app_data.scrollbarWidth, NULL);
3005 XtAddCallback(scrollHoriz, XtNscrollProc, _scroll_left_right, drawing);
3006 XtAddCallback(scrollHoriz, XtNjumpProc, _thumb_left_right, drawing);
3010 drawing = XtVaCreateManagedWidget(program_name, boxWidgetClass,
3011 topLevel, XtNwidth, window_width, XtNheight, window_height,
3012 XtNwidthInc, font_width, XtNheightInc, font_height, NULL);
3014 XtVaSetValues(topLevel, XtNwidthInc, font_width, XtNheightInc,
3018 /* Process any default translations */
3020 XtAugmentTranslations(drawing,
3021 XtParseTranslationTable(default_translations));
3022 XtAppAddActions(app_context, action_table, XtNumber(action_table));
3024 /* Process the supplied colors */
3026 _initialize_colors();
3028 /* Determine text cursor alignment from resources */
3030 if (!strcmp(xc_app_data.textCursor, "vertical"))
3031 vertical_cursor = TRUE;
3033 /* Now have LINES and COLS. Set these in the shared SP so the curses
3034 program can find them. */
3036 LINES = XCursesLINES;
3039 if ((shmidSP = shmget(shmkeySP, sizeof(SCREEN) + XCURSESSHMMIN,
3040 0700 | IPC_CREAT)) < 0)
3042 perror("Cannot allocate shared memory for SCREEN");
3043 kill(xc_otherpid, SIGKILL);
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;
3053 SP->mouse_wait = xc_app_data.clickPeriod;
3056 PDC_LOG(("%s:SHM size for curscr %d\n", XCLOGMSG, SP->XcurscrSize));
3058 if ((shmid_Xcurscr = shmget(shmkey_Xcurscr, SP->XcurscrSize +
3059 XCURSESSHMMIN, 0700 | IPC_CREAT)) < 0)
3061 perror("Cannot allocate shared memory for curscr");
3062 kill(xc_otherpid, SIGKILL);
3064 shmctl(shmidSP, IPC_RMID, 0);
3068 Xcurscr = (unsigned char *)shmat(shmid_Xcurscr, 0, 0);
3069 memset(Xcurscr, 0, SP->XcurscrSize);
3070 xc_atrtab = (short *)(Xcurscr + XCURSCR_ATRTAB_OFF);
3072 PDC_LOG(("%s:shmid_Xcurscr %d shmkey_Xcurscr %d LINES %d COLS %d\n",
3073 XCLOGMSG, shmid_Xcurscr, shmkey_Xcurscr, LINES, COLS));
3075 /* Add Event handlers to the drawing widget */
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);
3084 /* Add input handler from xc_display_sock (requests from curses
3087 XtAppAddInput(app_context, xc_display_sock, (XtPointer)XtInputReadMask,
3088 _process_curses_requests, NULL);
3090 /* If there is a cursorBlink resource, start the Timeout event */
3092 if (xc_app_data.cursorBlinkRate)
3093 XtAppAddTimeOut(app_context, xc_app_data.cursorBlinkRate,
3094 _blink_cursor, NULL);
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. */
3100 XC_write_display_socket_int(CURSES_CHILD);
3102 XtRealizeWidget(topLevel);
3104 /* Handle trapping of the WM_DELETE_WINDOW property */
3106 wm_atom[0] = XInternAtom(XtDisplay(topLevel), "WM_DELETE_WINDOW", False);
3108 XSetWMProtocols(XtDisplay(topLevel), XtWindow(topLevel), wm_atom, 1);
3110 /* Create the Graphics Context for drawing. This MUST be done AFTER
3111 the associated widget has been realized. */
3113 XC_LOG(("before _get_gc\n"));
3115 _get_gc(&normal_gc, xc_app_data.normalFont, COLOR_WHITE, COLOR_BLACK);
3117 _get_gc(&italic_gc, italic_font_valid ? xc_app_data.italicFont :
3118 xc_app_data.normalFont, COLOR_WHITE, COLOR_BLACK);
3120 _get_gc(&block_cursor_gc, xc_app_data.normalFont,
3121 COLOR_BLACK, COLOR_CURSOR);
3123 _get_gc(&rect_cursor_gc, xc_app_data.normalFont,
3124 COLOR_CURSOR, COLOR_BLACK);
3126 _get_gc(&border_gc, xc_app_data.normalFont, COLOR_BORDER, COLOR_BLACK);
3128 XSetLineAttributes(XCURSESDISPLAY, rect_cursor_gc, 2,
3129 LineSolid, CapButt, JoinMiter);
3131 XSetLineAttributes(XCURSESDISPLAY, border_gc, xc_app_data.borderWidth,
3132 LineSolid, CapButt, JoinMiter);
3134 /* Set the cursor for the application */
3136 XDefineCursor(XCURSESDISPLAY, XCURSESWIN, xc_app_data.pointer);
3137 rmfrom.size = sizeof(Pixel);
3138 rmto.size = sizeof(XColor);
3140 rmto.addr = (XPointer)&pointerforecolor;
3141 rmfrom.addr = (XPointer)&(xc_app_data.pointerForeColor);
3142 XtConvertAndStore(drawing, XtRPixel, &rmfrom, XtRColor, &rmto);
3144 rmfrom.size = sizeof(Pixel);
3145 rmto.size = sizeof(XColor);
3147 rmfrom.addr = (XPointer)&(xc_app_data.pointerBackColor);
3148 rmto.addr = (XPointer)&pointerbackcolor;
3149 XtConvertAndStore(drawing, XtRPixel, &rmfrom, XtRColor, &rmto);
3151 XRecolorCursor(XCURSESDISPLAY, xc_app_data.pointer,
3152 &pointerforecolor, &pointerbackcolor);
3156 /* Convert the supplied compose key to a Keysym */
3158 compose_key = XStringToKeysym(xc_app_data.composeKey);
3160 if (compose_key && IsModifierKey(compose_key))
3164 XModifierKeymap *map;
3165 KeyCode compose_keycode = XKeysymToKeycode(XCURSESDISPLAY, compose_key);
3167 map = XGetModifierMapping(XCURSESDISPLAY);
3168 kcp = map->modifiermap;
3170 for (i = 0; i < 8; i++)
3172 for (j = 0; j < map->max_keypermod; j++, kcp++)
3177 if (compose_keycode == *kcp)
3179 compose_mask = state_mask[i];
3188 XFreeModifiermap(map);
3192 Xim = XOpenIM(XCURSESDISPLAY, NULL, NULL, NULL);
3196 Xic = XCreateIC(Xim, XNInputStyle,
3197 XIMPreeditNothing | XIMStatusNothing,
3198 XNClientWindow, XCURSESWIN, NULL);
3205 XGetICValues(Xic, XNFilterEvents, &im_event_mask, NULL);
3207 XtAddEventHandler(drawing, im_event_mask, False,
3208 _dummy_handler, NULL);
3214 perror("ERROR: Cannot create input context");
3215 kill(xc_otherpid, SIGKILL);
3217 shmdt((char *)Xcurscr);
3218 shmctl(shmidSP, IPC_RMID, 0);
3219 shmctl(shmid_Xcurscr, IPC_RMID, 0);
3225 /* Wait for events */
3227 XtAppMainLoop(app_context);
3228 return OK; /* won't get here */