/* Public Domain Curses */ #include "pdcx11.h" RCSID("$Id: x11.c,v 1.94 2008/07/14 04:33:26 wmcbrine Exp $") #ifdef HAVE_DECKEYSYM_H # include #endif #ifdef HAVE_SUNKEYSYM_H # include #endif #ifdef HAVE_XPM_H # include #endif #if defined PDC_XIM # include #endif #include #include #ifndef XPOINTER_TYPEDEFED typedef char * XPointer; #endif #ifndef MAX_PATH # define MAX_PATH 256 #endif XCursesAppData xc_app_data; #if NeedWidePrototypes # define PDC_SCROLLBAR_TYPE double #else # define PDC_SCROLLBAR_TYPE float #endif #define MAX_COLORS 16 /* maximum of "normal" colors */ #define COLOR_CURSOR MAX_COLORS /* color of cursor */ #define COLOR_BORDER MAX_COLORS + 1 /* color of border */ #define XCURSESDISPLAY (XtDisplay(drawing)) #define XCURSESWIN (XtWindow(drawing)) /* Default icons for XCurses applications. */ #include "big_icon.xbm" #include "little_icon.xbm" static void _selection_off(void); static void _display_cursor(int, int, int, int); static void _redraw_cursor(void); static void _exit_process(int, int, char *); static void _send_key_to_curses(unsigned long, MOUSE_STATUS *, bool); static void XCursesButton(Widget, XEvent *, String *, Cardinal *); static void XCursesHandleString(Widget, XEvent *, String *, Cardinal *); static void XCursesKeyPress(Widget, XEvent *, String *, Cardinal *); static void XCursesPasteSelection(Widget, XButtonEvent *); static struct { KeySym keycode; bool numkeypad; unsigned short normal; unsigned short shifted; unsigned short control; unsigned short alt; } key_table[] = { /* keycode keypad normal shifted control alt*/ {XK_Left, FALSE, KEY_LEFT, KEY_SLEFT, CTL_LEFT, ALT_LEFT}, {XK_Right, FALSE, KEY_RIGHT, KEY_SRIGHT, CTL_RIGHT, ALT_RIGHT}, {XK_Up, FALSE, KEY_UP, KEY_SUP, CTL_UP, ALT_UP}, {XK_Down, FALSE, KEY_DOWN, KEY_SDOWN, CTL_DOWN, ALT_DOWN}, {XK_Home, FALSE, KEY_HOME, KEY_SHOME, CTL_HOME, ALT_HOME}, /* Sun Type 4 keyboard */ {XK_R7, FALSE, KEY_HOME, KEY_SHOME, CTL_HOME, ALT_HOME}, {XK_End, FALSE, KEY_END, KEY_SEND, CTL_END, ALT_END}, /* Sun Type 4 keyboard */ {XK_R13, FALSE, KEY_END, KEY_SEND, CTL_END, ALT_END}, {XK_Prior, FALSE, KEY_PPAGE, KEY_SPREVIOUS,CTL_PGUP, ALT_PGUP}, /* Sun Type 4 keyboard */ {XK_R9, FALSE, KEY_PPAGE, KEY_SPREVIOUS,CTL_PGUP, ALT_PGUP}, {XK_Next, FALSE, KEY_NPAGE, KEY_SNEXT, CTL_PGDN, ALT_PGDN}, /* Sun Type 4 keyboard */ {XK_R15, FALSE, KEY_NPAGE, KEY_SNEXT, CTL_PGDN, ALT_PGDN}, {XK_Insert, FALSE, KEY_IC, KEY_SIC, CTL_INS, ALT_INS}, {XK_Delete, FALSE, KEY_DC, KEY_SDC, CTL_DEL, ALT_DEL}, {XK_F1, FALSE, KEY_F(1), KEY_F(13), KEY_F(25), KEY_F(37)}, {XK_F2, FALSE, KEY_F(2), KEY_F(14), KEY_F(26), KEY_F(38)}, {XK_F3, FALSE, KEY_F(3), KEY_F(15), KEY_F(27), KEY_F(39)}, {XK_F4, FALSE, KEY_F(4), KEY_F(16), KEY_F(28), KEY_F(40)}, {XK_F5, FALSE, KEY_F(5), KEY_F(17), KEY_F(29), KEY_F(41)}, {XK_F6, FALSE, KEY_F(6), KEY_F(18), KEY_F(30), KEY_F(42)}, {XK_F7, FALSE, KEY_F(7), KEY_F(19), KEY_F(31), KEY_F(43)}, {XK_F8, FALSE, KEY_F(8), KEY_F(20), KEY_F(32), KEY_F(44)}, {XK_F9, FALSE, KEY_F(9), KEY_F(21), KEY_F(33), KEY_F(45)}, {XK_F10, FALSE, KEY_F(10), KEY_F(22), KEY_F(34), KEY_F(46)}, {XK_F11, FALSE, KEY_F(11), KEY_F(23), KEY_F(35), KEY_F(47)}, {XK_F12, FALSE, KEY_F(12), KEY_F(24), KEY_F(36), KEY_F(48)}, {XK_F13, FALSE, KEY_F(13), KEY_F(25), KEY_F(37), KEY_F(49)}, {XK_F14, FALSE, KEY_F(14), KEY_F(26), KEY_F(38), KEY_F(50)}, {XK_F15, FALSE, KEY_F(15), KEY_F(27), KEY_F(39), KEY_F(51)}, {XK_F16, FALSE, KEY_F(16), KEY_F(28), KEY_F(40), KEY_F(52)}, {XK_F17, FALSE, KEY_F(17), KEY_F(29), KEY_F(41), KEY_F(53)}, {XK_F18, FALSE, KEY_F(18), KEY_F(30), KEY_F(42), KEY_F(54)}, {XK_F19, FALSE, KEY_F(19), KEY_F(31), KEY_F(43), KEY_F(55)}, {XK_F20, FALSE, KEY_F(20), KEY_F(32), KEY_F(44), KEY_F(56)}, {XK_BackSpace, FALSE, 0x08, 0x08, CTL_BKSP, ALT_BKSP}, {XK_Tab, FALSE, 0x09, KEY_BTAB, CTL_TAB, ALT_TAB}, {XK_Select, FALSE, KEY_SELECT, KEY_SELECT, KEY_SELECT, KEY_SELECT}, {XK_Print, FALSE, KEY_PRINT, KEY_SPRINT, KEY_PRINT, KEY_PRINT}, {XK_Find, FALSE, KEY_FIND, KEY_SFIND, KEY_FIND, KEY_FIND}, {XK_Pause, FALSE, KEY_SUSPEND, KEY_SSUSPEND, KEY_SUSPEND, KEY_SUSPEND}, {XK_Clear, FALSE, KEY_CLEAR, KEY_CLEAR, KEY_CLEAR, KEY_CLEAR}, {XK_Cancel, FALSE, KEY_CANCEL, KEY_SCANCEL, KEY_CANCEL, KEY_CANCEL}, {XK_Break, FALSE, KEY_BREAK, KEY_BREAK, KEY_BREAK, KEY_BREAK}, {XK_Help, FALSE, KEY_HELP, KEY_SHELP, KEY_LHELP, KEY_HELP}, {XK_L4, FALSE, KEY_UNDO, KEY_SUNDO, KEY_UNDO, KEY_UNDO}, {XK_L6, FALSE, KEY_COPY, KEY_SCOPY, KEY_COPY, KEY_COPY}, {XK_L9, FALSE, KEY_FIND, KEY_SFIND, KEY_FIND, KEY_FIND}, {XK_Menu, FALSE, KEY_OPTIONS, KEY_SOPTIONS, KEY_OPTIONS, KEY_OPTIONS}, #ifdef HAVE_SUNKEYSYM_H {SunXK_F36, FALSE, KEY_F(41), KEY_F(43), KEY_F(45), KEY_F(47)}, {SunXK_F37, FALSE, KEY_F(42), KEY_F(44), KEY_F(46), KEY_F(48)}, #endif #ifdef HAVE_DECKEYSYM_H {DXK_Remove, FALSE, KEY_DC, KEY_SDC, CTL_DEL, ALT_DEL}, #endif {XK_Escape, FALSE, 0x1B, 0x1B, 0x1B, ALT_ESC}, {XK_KP_Enter, TRUE, PADENTER, PADENTER, CTL_PADENTER,ALT_PADENTER}, {XK_KP_Add, TRUE, PADPLUS, '+', CTL_PADPLUS, ALT_PADPLUS}, {XK_KP_Subtract,TRUE, PADMINUS, '-', CTL_PADMINUS,ALT_PADMINUS}, {XK_KP_Multiply,TRUE, PADSTAR, '*', CTL_PADSTAR, ALT_PADSTAR}, /* Sun Type 4 keyboard */ {XK_R6, TRUE, PADSTAR, '*', CTL_PADSTAR, ALT_PADSTAR}, {XK_KP_Divide, TRUE, PADSLASH, '/', CTL_PADSLASH,ALT_PADSLASH}, /* Sun Type 4 keyboard */ {XK_R5, TRUE, PADSLASH, '/', CTL_PADSLASH,ALT_PADSLASH}, {XK_KP_Decimal,TRUE, PADSTOP, '.', CTL_PADSTOP, ALT_PADSTOP}, {XK_KP_0, TRUE, PAD0, '0', CTL_PAD0, ALT_PAD0}, {XK_KP_1, TRUE, KEY_C1, '1', CTL_PAD1, ALT_PAD1}, {XK_KP_2, TRUE, KEY_C2, '2', CTL_PAD2, ALT_PAD2}, {XK_KP_3, TRUE, KEY_C3, '3', CTL_PAD3, ALT_PAD3}, {XK_KP_4, TRUE, KEY_B1, '4', CTL_PAD4, ALT_PAD4}, {XK_KP_5, TRUE, KEY_B2, '5', CTL_PAD5, ALT_PAD5}, /* Sun Type 4 keyboard */ {XK_R11, TRUE, KEY_B2, '5', CTL_PAD5, ALT_PAD5}, {XK_KP_6, TRUE, KEY_B3, '6', CTL_PAD6, ALT_PAD6}, {XK_KP_7, TRUE, KEY_A1, '7', CTL_PAD7, ALT_PAD7}, {XK_KP_8, TRUE, KEY_A2, '8', CTL_PAD8, ALT_PAD8}, {XK_KP_9, TRUE, KEY_A3, '9', CTL_PAD9, ALT_PAD9}, /* the following added to support Sun Type 5 keyboards */ {XK_F21, FALSE, KEY_SUSPEND, KEY_SSUSPEND, KEY_SUSPEND, KEY_SUSPEND}, {XK_F22, FALSE, KEY_PRINT, KEY_SPRINT, KEY_PRINT, KEY_PRINT}, {XK_F24, TRUE, PADMINUS, '-', CTL_PADMINUS,ALT_PADMINUS}, /* Sun Type 4 keyboard */ {XK_F25, TRUE, PADSLASH, '/', CTL_PADSLASH,ALT_PADSLASH}, /* Sun Type 4 keyboard */ {XK_F26, TRUE, PADSTAR, '*', CTL_PADSTAR, ALT_PADSTAR}, {XK_F27, TRUE, KEY_A1, '7', CTL_PAD7, ALT_PAD7}, {XK_F29, TRUE, KEY_A3, '9', CTL_PAD9, ALT_PAD9}, {XK_F31, TRUE, KEY_B2, '5', CTL_PAD5, ALT_PAD5}, {XK_F35, TRUE, KEY_C3, '3', CTL_PAD3, ALT_PAD3}, #ifdef HAVE_XK_KP_DELETE {XK_KP_Delete, TRUE, PADSTOP, '.', CTL_PADSTOP, ALT_PADSTOP}, #endif #ifdef HAVE_XK_KP_INSERT {XK_KP_Insert, TRUE, PAD0, '0', CTL_PAD0, ALT_PAD0}, #endif #ifdef HAVE_XK_KP_END {XK_KP_End, TRUE, KEY_C1, '1', CTL_PAD1, ALT_PAD1}, #endif #ifdef HAVE_XK_KP_DOWN {XK_KP_Down, TRUE, KEY_C2, '2', CTL_PAD2, ALT_PAD2}, #endif #ifdef HAVE_XK_KP_NEXT {XK_KP_Next, TRUE, KEY_C3, '3', CTL_PAD3, ALT_PAD3}, #endif #ifdef HAVE_XK_KP_LEFT {XK_KP_Left, TRUE, KEY_B1, '4', CTL_PAD4, ALT_PAD4}, #endif #ifdef HAVE_XK_KP_BEGIN {XK_KP_Begin, TRUE, KEY_B2, '5', CTL_PAD5, ALT_PAD5}, #endif #ifdef HAVE_XK_KP_RIGHT {XK_KP_Right, TRUE, KEY_B3, '6', CTL_PAD6, ALT_PAD6}, #endif #ifdef HAVE_XK_KP_HOME {XK_KP_Home, TRUE, KEY_A1, '7', CTL_PAD7, ALT_PAD7}, #endif #ifdef HAVE_XK_KP_UP {XK_KP_Up, TRUE, KEY_A2, '8', CTL_PAD8, ALT_PAD8}, #endif #ifdef HAVE_XK_KP_PRIOR {XK_KP_Prior, TRUE, KEY_A3, '9', CTL_PAD9, ALT_PAD9}, #endif {0, 0, 0, 0, 0, 0} }; #ifndef PDC_XIM # include "compose.h" #endif #define BITMAPDEPTH 1 unsigned long pdc_key_modifiers = 0L; static GC normal_gc, block_cursor_gc, rect_cursor_gc, italic_gc, border_gc; static int font_height, font_width, font_ascent, font_descent, window_width, window_height; static int resize_window_width = 0, resize_window_height = 0; static char *bitmap_file = NULL; #ifdef HAVE_XPM_H static char *pixmap_file = NULL; #endif static KeySym keysym = 0; static int state_mask[8] = { ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask }; static Atom wm_atom[2]; static String class_name = "XCurses"; static XtAppContext app_context; static Widget topLevel, drawing, scrollBox, scrollVert, scrollHoriz; static int received_map_notify = 0; static bool mouse_selection = FALSE; static chtype *tmpsel = NULL; static unsigned long tmpsel_length = 0; static int selection_start_x = 0, selection_start_y = 0, selection_end_x = 0, selection_end_y = 0; static Pixmap icon_bitmap; #ifdef HAVE_XPM_H static Pixmap icon_pixmap; static Pixmap icon_pixmap_mask; #endif static bool visible_cursor = FALSE; static bool window_entered = TRUE; static char *program_name; /* Macros just for app_resources */ #ifdef PDC_WIDE # define DEFFONT "-misc-fixed-medium-r-normal--20-200-75-75-c-100-iso10646-1" #else # define DEFFONT "7x13" #endif #define APPDATAOFF(n) XtOffsetOf(XCursesAppData, n) #define RINT(name1, name2, value) { \ #name1, #name2, XtRInt, \ sizeof(int), APPDATAOFF(name1), XtRImmediate, \ (XtPointer)value \ } #define RPIXEL(name1, name2, value) { \ #name1, #name2, XtRPixel, \ sizeof(Pixel), APPDATAOFF(name1), XtRString, \ (XtPointer)#value \ } #define RCOLOR(name, value) RPIXEL(color##name, Color##name, value) #define RSTRINGP(name1, name2, param) { \ #name1, #name2, XtRString, \ MAX_PATH, APPDATAOFF(name1), XtRString, (XtPointer)param \ } #define RSTRING(name1, name2) RSTRINGP(name1, name2, "") #define RFONT(name1, name2, value) { \ #name1, #name2, XtRFontStruct, \ sizeof(XFontStruct), APPDATAOFF(name1), XtRString, \ (XtPointer)value \ } #define RCURSOR(name1, name2, value) { \ #name1, #name2, XtRCursor, \ sizeof(Cursor), APPDATAOFF(name1), XtRString, \ (XtPointer)#value \ } static XtResource app_resources[] = { RINT(lines, Lines, 24), RINT(cols, Cols, 80), RPIXEL(cursorColor, CursorColor, Red), RCOLOR(Black, Black), RCOLOR(Red, red3), RCOLOR(Green, green3), RCOLOR(Yellow, yellow3), RCOLOR(Blue, blue3), RCOLOR(Magenta, magenta3), RCOLOR(Cyan, cyan3), RCOLOR(White, Grey), RCOLOR(BoldBlack, grey40), RCOLOR(BoldRed, red1), RCOLOR(BoldGreen, green1), RCOLOR(BoldYellow, yellow1), RCOLOR(BoldBlue, blue1), RCOLOR(BoldMagenta, magenta1), RCOLOR(BoldCyan, cyan1), RCOLOR(BoldWhite, White), RFONT(normalFont, NormalFont, DEFFONT), RFONT(italicFont, ItalicFont, DEFFONT), RSTRING(bitmap, Bitmap), #ifdef HAVE_XPM_H RSTRING(pixmap, Pixmap), #endif RSTRINGP(composeKey, ComposeKey, "Multi_key"), RCURSOR(pointer, Pointer, xterm), RPIXEL(pointerForeColor, PointerForeColor, Black), RPIXEL(pointerBackColor, PointerBackColor, White), RINT(shmmin, Shmmin, 0), RINT(borderWidth, BorderWidth, 0), RPIXEL(borderColor, BorderColor, Black), RINT(doubleClickPeriod, DoubleClickPeriod, (PDC_CLICK_PERIOD * 2)), RINT(clickPeriod, ClickPeriod, PDC_CLICK_PERIOD), RINT(scrollbarWidth, ScrollbarWidth, 15), RINT(cursorBlinkRate, CursorBlinkRate, 0), RSTRING(textCursor, TextCursor) }; #undef RCURSOR #undef RFONT #undef RSTRING #undef RCOLOR #undef RPIXEL #undef RINT #undef APPDATAOFF #undef DEFFONT /* Macros for options */ #define COPT(name) {"-" #name, "*" #name, XrmoptionSepArg, NULL} #define CCOLOR(name) COPT(color##name) static XrmOptionDescRec options[] = { COPT(lines), COPT(cols), COPT(normalFont), COPT(italicFont), COPT(bitmap), #ifdef HAVE_XPM_H COPT(pixmap), #endif COPT(pointer), COPT(shmmin), COPT(composeKey), COPT(clickPeriod), COPT(doubleClickPeriod), COPT(scrollbarWidth), COPT(pointerForeColor), COPT(pointerBackColor), COPT(cursorBlinkRate), COPT(cursorColor), COPT(textCursor), CCOLOR(Black), CCOLOR(Red), CCOLOR(Green), CCOLOR(Yellow), CCOLOR(Blue), CCOLOR(Magenta), CCOLOR(Cyan), CCOLOR(White), CCOLOR(BoldBlack), CCOLOR(BoldRed), CCOLOR(BoldGreen), CCOLOR(BoldYellow), CCOLOR(BoldBlue), CCOLOR(BoldMagenta), CCOLOR(BoldCyan), CCOLOR(BoldWhite) }; #undef CCOLOR #undef COPT static XtActionsRec action_table[] = { {"XCursesButton", (XtActionProc)XCursesButton}, {"XCursesKeyPress", (XtActionProc)XCursesKeyPress}, {"XCursesPasteSelection", (XtActionProc)XCursesPasteSelection}, {"string", (XtActionProc)XCursesHandleString} }; static bool after_first_curses_request = FALSE; static Pixel colors[MAX_COLORS + 2]; static bool vertical_cursor = FALSE; #ifdef PDC_XIM static XIM Xim = NULL; static XIC Xic = NULL; #endif static const char *default_translations = { ": XCursesKeyPress() \n" \ ": XCursesKeyPress() \n" \ ": XCursesButton() \n" \ ": XCursesButton() \n" \ ": XCursesButton()" }; static int _to_utf8(char *outcode, chtype code) { #ifdef PDC_WIDE if (code & A_ALTCHARSET && !(code & 0xff80)) code = acs_map[code & 0x7f]; #endif code &= A_CHARTEXT; if (code < 0x80) { outcode[0] = code; return 1; } else if (code < 0x800) { outcode[0] = ((code & 0x07c0) >> 6) | 0xc0; outcode[1] = (code & 0x003f) | 0x80; return 2; } else { outcode[0] = ((code & 0xf000) >> 12) | 0xe0; outcode[1] = ((code & 0x0fc0) >> 6) | 0x80; outcode[2] = (code & 0x003f) | 0x80; return 3; } } static int _from_utf8(wchar_t *pwc, const char *s, size_t n) { wchar_t key; int i = -1; const unsigned char *string; if (!s || (n < 1)) return -1; if (!*s) return 0; string = (const unsigned char *)s; key = string[0]; /* Simplistic UTF-8 decoder -- only does the BMP, minimal validation */ if (key & 0x80) { if ((key & 0xe0) == 0xc0) { if (1 < n) { key = ((key & 0x1f) << 6) | (string[1] & 0x3f); i = 2; } } else if ((key & 0xe0) == 0xe0) { if (2 < n) { key = ((key & 0x0f) << 12) | ((string[1] & 0x3f) << 6) | (string[2] & 0x3f); i = 3; } } } else i = 1; if (i) *pwc = key; return i; } #ifndef X_HAVE_UTF8_STRING static Atom XA_UTF8_STRING(Display *dpy) { static AtomPtr p = NULL; if (!p) p = XmuMakeAtom("UTF8_STRING"); return XmuInternAtom(dpy, p); } #endif signal_handler XCursesSetSignal(int signo, signal_handler action) { #if defined(SA_INTERRUPT) || defined(SA_RESTART) struct sigaction sigact, osigact; sigact.sa_handler = action; sigact.sa_flags = # ifdef SA_INTERRUPT # ifdef SA_RESTART SA_INTERRUPT | SA_RESTART; # else SA_INTERRUPT; # endif # else /* must be SA_RESTART */ SA_RESTART; # endif sigemptyset(&sigact.sa_mask); if (sigaction(signo, &sigact, &osigact)) return SIG_ERR; return osigact.sa_handler; #else /* not SA_INTERRUPT or SA_RESTART, use plain signal */ return signal(signo, action); #endif } RETSIGTYPE XCursesSigwinchHandler(int signo) { PDC_LOG(("%s:XCursesSigwinchHandler() - called: SIGNO: %d\n", XCLOGMSG, signo)); /* Patch by: Georg Fuchs, georg.fuchs@rz.uni-regensburg.de 02-Feb-1999 */ SP->resized += 1; /* Always trap SIGWINCH if the C library supports SIGWINCH */ #ifdef SIGWINCH XCursesSetSignal(SIGWINCH, XCursesSigwinchHandler); #endif } /* Convert character positions x and y to pixel positions, stored in xpos and ypos */ static void _make_xy(int x, int y, int *xpos, int *ypos) { *xpos = (x * font_width) + xc_app_data.borderWidth; *ypos = xc_app_data.normalFont->ascent + (y * font_height) + xc_app_data.borderWidth; } /* Output a block of characters with common attributes */ static int _new_packet(chtype attr, bool rev, int len, int col, int row, #ifdef PDC_WIDE XChar2b *text) #else char *text) #endif { GC gc; int xpos, ypos; short fore, back; PDC_pair_content(PAIR_NUMBER(attr), &fore, &back); #ifdef PDC_WIDE text[len].byte1 = text[len].byte2 = 0; #else text[len] = '\0'; #endif /* Specify the color table offsets */ fore |= (attr & A_BOLD) ? 8 : 0; back |= (attr & A_BLINK) ? 8 : 0; /* Reverse flag = highlighted selection XOR A_REVERSE set */ rev ^= !!(attr & A_REVERSE); /* Determine which GC to use - normal or italic */ gc = (attr & A_ITALIC) ? italic_gc : normal_gc; /* Draw it */ XSetForeground(XCURSESDISPLAY, gc, colors[rev ? back : fore]); XSetBackground(XCURSESDISPLAY, gc, colors[rev ? fore : back]); _make_xy(col, row, &xpos, &ypos); #ifdef PDC_WIDE XDrawImageString16( #else XDrawImageString( #endif XCURSESDISPLAY, XCURSESWIN, gc, xpos, ypos, text, len); /* Underline, etc. */ if (attr & (A_LEFTLINE|A_RIGHTLINE|A_UNDERLINE)) { int k; if (SP->line_color != -1) XSetForeground(XCURSESDISPLAY, gc, colors[SP->line_color]); if (attr & A_UNDERLINE) /* UNDER */ XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc, xpos, ypos + 1, xpos + font_width * len, ypos + 1); if (attr & A_LEFTLINE) /* LEFT */ for (k = 0; k < len; k++) { int x = xpos + font_width * k - 1; XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc, x, ypos - font_ascent, x, ypos + font_descent); } if (attr & A_RIGHTLINE) /* RIGHT */ for (k = 0; k < len; k++) { int x = xpos + font_width * (k + 1) - 1; XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc, x, ypos - font_ascent, x, ypos + font_descent); } } PDC_LOG(("%s:_new_packet() - row: %d col: %d " "num_cols: %d fore: %d back: %d text:<%s>\n", XCLOGMSG, row, col, len, fore, back, text)); return OK; } /* The core display routine -- update one line of text */ static int _display_text(const chtype *ch, int row, int col, int num_cols, bool highlight) { #ifdef PDC_WIDE XChar2b text[513]; #else char text[513]; #endif chtype old_attr, attr; int i, j; PDC_LOG(("%s:_display_text() - called: row: %d col: %d " "num_cols: %d\n", XCLOGMSG, row, col, num_cols)); if (!num_cols) return OK; old_attr = *ch & A_ATTRIBUTES; for (i = 0, j = 0; j < num_cols; j++) { chtype curr = ch[j]; attr = curr & A_ATTRIBUTES; #ifdef CHTYPE_LONG if (attr & A_ALTCHARSET && !(curr & 0xff80)) { attr ^= A_ALTCHARSET; curr = acs_map[curr & 0x7f]; } #endif #ifndef PDC_WIDE /* Special handling for ACS_BLOCK */ if (!(curr & A_CHARTEXT)) { curr |= ' '; attr ^= A_REVERSE; } #endif if (attr != old_attr) { if (_new_packet(old_attr, highlight, i, col, row, text) == ERR) return ERR; old_attr = attr; col += i; i = 0; } #ifdef PDC_WIDE text[i].byte1 = (curr & 0xff00) >> 8; text[i++].byte2 = curr & 0x00ff; #else text[i++] = curr & 0xff; #endif } return _new_packet(old_attr, highlight, i, col, row, text); } static void _get_gc(GC *gc, XFontStruct *font_info, int fore, int back) { XGCValues values; /* Create default Graphics Context */ *gc = XCreateGC(XCURSESDISPLAY, XCURSESWIN, 0L, &values); /* specify font */ XSetFont(XCURSESDISPLAY, *gc, font_info->fid); XSetForeground(XCURSESDISPLAY, *gc, colors[fore]); XSetBackground(XCURSESDISPLAY, *gc, colors[back]); } static void _initialize_colors(void) { colors[COLOR_BLACK] = xc_app_data.colorBlack; colors[COLOR_RED] = xc_app_data.colorRed; colors[COLOR_GREEN] = xc_app_data.colorGreen; colors[COLOR_YELLOW] = xc_app_data.colorYellow; colors[COLOR_BLUE] = xc_app_data.colorBlue; colors[COLOR_MAGENTA] = xc_app_data.colorMagenta; colors[COLOR_CYAN] = xc_app_data.colorCyan; colors[COLOR_WHITE] = xc_app_data.colorWhite; colors[COLOR_BLACK + 8] = xc_app_data.colorBoldBlack; colors[COLOR_RED + 8] = xc_app_data.colorBoldRed; colors[COLOR_GREEN + 8] = xc_app_data.colorBoldGreen; colors[COLOR_YELLOW + 8] = xc_app_data.colorBoldYellow; colors[COLOR_BLUE + 8] = xc_app_data.colorBoldBlue; colors[COLOR_MAGENTA + 8] = xc_app_data.colorBoldMagenta; colors[COLOR_CYAN + 8] = xc_app_data.colorBoldCyan; colors[COLOR_WHITE + 8] = xc_app_data.colorBoldWhite; colors[COLOR_CURSOR] = xc_app_data.cursorColor; colors[COLOR_BORDER] = xc_app_data.borderColor; } static void _refresh_scrollbar(void) { XC_LOG(("_refresh_scrollbar() - called\n")); if (SP->sb_on) { PDC_SCROLLBAR_TYPE total_y = SP->sb_total_y; PDC_SCROLLBAR_TYPE total_x = SP->sb_total_x; if (total_y) XawScrollbarSetThumb(scrollVert, (PDC_SCROLLBAR_TYPE)(SP->sb_cur_y) / total_y, (PDC_SCROLLBAR_TYPE)(SP->sb_viewport_y) / total_y); if (total_x) XawScrollbarSetThumb(scrollHoriz, (PDC_SCROLLBAR_TYPE)(SP->sb_cur_x) / total_x, (PDC_SCROLLBAR_TYPE)(SP->sb_viewport_x) / total_x); } } static void _set_cursor_color(chtype *ch, short *fore, short *back) { int attr; short f, b; attr = PAIR_NUMBER(*ch); if (attr) { PDC_pair_content(attr, &f, &b); *fore = 7 - (f % 8); *back = 7 - (b % 8); } else { if (*ch & A_REVERSE) { *back = COLOR_BLACK; *fore = COLOR_WHITE; } else { *back = COLOR_WHITE; *fore = COLOR_BLACK; } } } static void _get_icon(void) { XIconSize *icon_size; int size_count = 0; Status rc; unsigned char *bitmap_bits = NULL; unsigned icon_bitmap_width = 0, icon_bitmap_height = 0, file_bitmap_width = 0, file_bitmap_height = 0; XC_LOG(("_get_icon() - called\n")); icon_size = XAllocIconSize(); rc = XGetIconSizes(XtDisplay(topLevel), RootWindowOfScreen(XtScreen(topLevel)), &icon_size, &size_count); /* if the WM can advise on icon sizes... */ if (rc && size_count) { int i, max_height = 0, max_width = 0; PDC_LOG(("%s:size_count: %d rc: %d\n", XCLOGMSG, size_count, rc)); for (i = 0; i < size_count; i++) { if (icon_size[i].max_width > max_width) max_width = icon_size[i].max_width; if (icon_size[i].max_height > max_height) max_height = icon_size[i].max_height; PDC_LOG(("%s:min: %d %d\n", XCLOGMSG, icon_size[i].min_width, icon_size[i].min_height)); PDC_LOG(("%s:max: %d %d\n", XCLOGMSG, icon_size[i].max_width, icon_size[i].max_height)); PDC_LOG(("%s:inc: %d %d\n", XCLOGMSG, icon_size[i].width_inc, icon_size[i].height_inc)); } if (max_width >= big_icon_width && max_height >= big_icon_height) { icon_bitmap_width = big_icon_width; icon_bitmap_height = big_icon_height; bitmap_bits = (unsigned char *)big_icon_bits; } else { icon_bitmap_width = little_icon_width; icon_bitmap_height = little_icon_height; bitmap_bits = (unsigned char *)little_icon_bits; } } else /* use small icon */ { icon_bitmap_width = little_icon_width; icon_bitmap_height = little_icon_height; bitmap_bits = (unsigned char *)little_icon_bits; } XFree(icon_size); #ifdef HAVE_XPM_H if (xc_app_data.pixmap && xc_app_data.pixmap[0]) /* supplied pixmap */ { XpmReadFileToPixmap(XtDisplay(topLevel), RootWindowOfScreen(XtScreen(topLevel)), (char *)xc_app_data.pixmap, &icon_pixmap, &icon_pixmap_mask, NULL); return; } #endif if (xc_app_data.bitmap && xc_app_data.bitmap[0]) /* supplied bitmap */ { int x_hot = 0, y_hot = 0; rc = XReadBitmapFile(XtDisplay(topLevel), RootWindowOfScreen(XtScreen(topLevel)), (char *)xc_app_data.bitmap, &file_bitmap_width, &file_bitmap_height, &icon_bitmap, &x_hot, &y_hot); switch(rc) { case BitmapOpenFailed: fprintf(stderr, "bitmap file %s: not found\n", xc_app_data.bitmap); break; case BitmapFileInvalid: fprintf(stderr, "bitmap file %s: contents invalid\n", xc_app_data.bitmap); break; default: return; } } icon_bitmap = XCreateBitmapFromData(XtDisplay(topLevel), RootWindowOfScreen(XtScreen(topLevel)), (char *)bitmap_bits, icon_bitmap_width, icon_bitmap_height); } static void _draw_border(void) { /* Draw the border if required */ if (xc_app_data.borderWidth) XDrawRectangle(XCURSESDISPLAY, XCURSESWIN, border_gc, xc_app_data.borderWidth / 2, xc_app_data.borderWidth / 2, window_width - xc_app_data.borderWidth, window_height - xc_app_data.borderWidth); } /* Redraw the entire screen */ static void _display_screen(void) { int row; XC_LOG(("_display_screen() - called\n")); for (row = 0; row < XCursesLINES; row++) { XC_get_line_lock(row); _display_text((const chtype *)(Xcurscr + XCURSCR_Y_OFF(row)), row, 0, COLS, FALSE); XC_release_line_lock(row); } _redraw_cursor(); _draw_border(); } /* Draw changed portions of the screen */ static void _refresh_screen(void) { int row, start_col, num_cols; XC_LOG(("_refresh_screen() - called\n")); for (row = 0; row < XCursesLINES; row++) { num_cols = (int)*(Xcurscr + XCURSCR_LENGTH_OFF + row); if (num_cols) { XC_get_line_lock(row); start_col = (int)*(Xcurscr + XCURSCR_START_OFF + row); _display_text((const chtype *)(Xcurscr + XCURSCR_Y_OFF(row) + (start_col * sizeof(chtype))), row, start_col, num_cols, FALSE); *(Xcurscr + XCURSCR_LENGTH_OFF + row) = 0; XC_release_line_lock(row); } } if (mouse_selection) _selection_off(); } static void _handle_expose(Widget w, XtPointer client_data, XEvent *event, Boolean *unused) { XC_LOG(("_handle_expose() - called\n")); /* ignore all Exposes except last */ if (event->xexpose.count) return; if (after_first_curses_request && received_map_notify) _display_screen(); } static void _handle_nonmaskable(Widget w, XtPointer client_data, XEvent *event, Boolean *unused) { XClientMessageEvent *client_event = (XClientMessageEvent *)event; PDC_LOG(("%s:_handle_nonmaskable called: xc_otherpid %d event %d\n", XCLOGMSG, xc_otherpid, event->type)); if (event->type == ClientMessage) { XC_LOG(("ClientMessage received\n")); /* This code used to include handling of WM_SAVE_YOURSELF, but it resulted in continual failure of THE on my Toshiba laptop. Removed on 3-3-2001. Now only exits on WM_DELETE_WINDOW. */ if ((Atom)client_event->data.s[0] == wm_atom[0]) _exit_process(0, SIGKILL, ""); } } static void XCursesKeyPress(Widget w, XEvent *event, String *params, Cardinal *nparams) { enum { STATE_NORMAL, STATE_COMPOSE, STATE_CHAR }; #ifdef PDC_XIM Status status; wchar_t buffer[120]; #else unsigned char buffer[120]; XComposeStatus compose; static int compose_state = STATE_NORMAL; static int compose_index = 0; int char_idx = 0; #endif unsigned long key = 0; int buflen = 40; int i, count; unsigned long modifier = 0; bool key_code = FALSE; XC_LOG(("XCursesKeyPress() - called\n")); /* Handle modifier keys first; ignore other KeyReleases */ if (event->type == KeyRelease) { /* The keysym value was set by a previous call to this function with a KeyPress event (or reset by the mouse event handler) */ if (SP->return_key_modifiers && #ifndef PDC_XIM keysym != compose_key && #endif IsModifierKey(keysym)) { switch (keysym) { case XK_Shift_L: key = KEY_SHIFT_L; break; case XK_Shift_R: key = KEY_SHIFT_R; break; case XK_Control_L: key = KEY_CONTROL_L; break; case XK_Control_R: key = KEY_CONTROL_R; break; case XK_Alt_L: key = KEY_ALT_L; break; case XK_Alt_R: key = KEY_ALT_R; } if (key) _send_key_to_curses(key, NULL, TRUE); } return; } buffer[0] = '\0'; #ifdef PDC_XIM count = XwcLookupString(Xic, &(event->xkey), buffer, buflen, &keysym, &status); #else count = XLookupString(&(event->xkey), (char *)buffer, buflen, &keysym, &compose); #endif /* translate keysym into curses key code */ PDC_LOG(("%s:Key mask: %x\n", XCLOGMSG, event->xkey.state)); #ifdef PDCDEBUG for (i = 0; i < 4; i++) PDC_debug("%s:Keysym %x %d\n", XCLOGMSG, XKeycodeToKeysym(XCURSESDISPLAY, event->xkey.keycode, i), i); #endif #ifndef PDC_XIM /* Check if the key just pressed is the user-specified compose key; if it is, set the compose state and exit. */ if (keysym == compose_key) { chtype *ch; int xpos, ypos, save_visibility = SP->visibility; short fore = 0, back = 0; /* Change the shape of the cursor to an outline rectangle to indicate we are in "compose" status */ SP->visibility = 0; _redraw_cursor(); SP->visibility = save_visibility; _make_xy(SP->curscol, SP->cursrow, &xpos, &ypos); ch = (chtype *)(Xcurscr + XCURSCR_Y_OFF(SP->cursrow) + (SP->curscol * sizeof(chtype))); _set_cursor_color(ch, &fore, &back); XSetForeground(XCURSESDISPLAY, rect_cursor_gc, colors[back]); XDrawRectangle(XCURSESDISPLAY, XCURSESWIN, rect_cursor_gc, xpos + 1, ypos - font_height + xc_app_data.normalFont->descent + 1, font_width - 2, font_height - 2); compose_state = STATE_COMPOSE; return; } switch (compose_state) { case STATE_COMPOSE: if (IsModifierKey(keysym)) return; if (event->xkey.state & compose_mask) { compose_state = STATE_NORMAL; _redraw_cursor(); break; } if (buffer[0] && count == 1) key = buffer[0]; compose_index = -1; for (i = 0; i < (int)strlen(compose_chars); i++) if (compose_chars[i] == key) { compose_index = i; break; } if (compose_index == -1) { compose_state = STATE_NORMAL; compose_index = 0; _redraw_cursor(); break; } compose_state = STATE_CHAR; return; case STATE_CHAR: if (IsModifierKey(keysym)) return; if (event->xkey.state & compose_mask) { compose_state = STATE_NORMAL; _redraw_cursor(); break; } if (buffer[0] && count == 1) key = buffer[0]; char_idx = -1; for (i = 0; i < MAX_COMPOSE_CHARS; i++) if (compose_lookups[compose_index][i] == key) { char_idx = i; break; } if (char_idx == -1) { compose_state = STATE_NORMAL; compose_index = 0; _redraw_cursor(); break; } _send_key_to_curses(compose_keys[compose_index][char_idx], NULL, FALSE); compose_state = STATE_NORMAL; compose_index = 0; _redraw_cursor(); return; } #endif /* PDC_XIM */ /* To get here we are procesing "normal" keys */ PDC_LOG(("%s:Keysym %x %d\n", XCLOGMSG, XKeycodeToKeysym(XCURSESDISPLAY, event->xkey.keycode, key), key)); if (SP->save_key_modifiers) { /* 0x10: usually, numlock modifier */ if (event->xkey.state & Mod2Mask) modifier |= PDC_KEY_MODIFIER_NUMLOCK; /* 0x01: shift modifier */ if (event->xkey.state & ShiftMask) modifier |= PDC_KEY_MODIFIER_SHIFT; /* 0x04: control modifier */ if (event->xkey.state & ControlMask) modifier |= PDC_KEY_MODIFIER_CONTROL; /* 0x08: usually, alt modifier */ if (event->xkey.state & Mod1Mask) modifier |= PDC_KEY_MODIFIER_ALT; } for (i = 0; key_table[i].keycode; i++) { if (key_table[i].keycode == keysym) { PDC_LOG(("%s:State %x\n", XCLOGMSG, event->xkey.state)); /* ControlMask: 0x04: control modifier Mod1Mask: 0x08: usually, alt modifier Mod2Mask: 0x10: usually, numlock modifier ShiftMask: 0x01: shift modifier */ if ((event->xkey.state & ShiftMask) || (key_table[i].numkeypad && (event->xkey.state & Mod2Mask))) { key = key_table[i].shifted; } else if (event->xkey.state & ControlMask) { key = key_table[i].control; } else if (event->xkey.state & Mod1Mask) { key = key_table[i].alt; } /* To get here, we ignore all other modifiers */ else key = key_table[i].normal; key_code = (key > 0x100); break; } } if (!key && buffer[0] && count == 1) key = buffer[0]; PDC_LOG(("%s:Key: %s pressed - %x Mod: %x\n", XCLOGMSG, XKeysymToString(keysym), key, event->xkey.state)); /* Handle ALT letters and numbers */ if (event->xkey.state == Mod1Mask) { if (key >= 'A' && key <= 'Z') { key += ALT_A - 'A'; key_code = TRUE; } if (key >= 'a' && key <= 'z') { key += ALT_A - 'a'; key_code = TRUE; } if (key >= '0' && key <= '9') { key += ALT_0 - '0'; key_code = TRUE; } } /* After all that, send the key back to the application if is NOT zero. */ if (key) { key |= (modifier << 24); _send_key_to_curses(key, NULL, key_code); } } static void XCursesHandleString(Widget w, XEvent *event, String *params, Cardinal *nparams) { unsigned char *ptr; if (*nparams != 1) return; ptr = (unsigned char *)*params; if (ptr[0] == '0' && ptr[1] == 'x' && ptr[2] != '\0') { unsigned char c; unsigned long total = 0; for (ptr += 2; (c = tolower(*ptr)); ptr++) { total <<= 4; if (c >= '0' && c <= '9') total += c - '0'; else if (c >= 'a' && c <= 'f') total += c - ('a' - 10); else break; } if (c == '\0') _send_key_to_curses(total, NULL, FALSE); } else for (; *ptr; ptr++) _send_key_to_curses((unsigned long)*ptr, NULL, FALSE); } static void _paste_string(Widget w, XtPointer data, Atom *selection, Atom *type, XtPointer value, unsigned long *length, int *format) { unsigned long i, key; unsigned char *string = value; XC_LOG(("_paste_string() - called\n")); if (!*type || !*length || !string) return; for (i = 0; string[i] && (i < (*length)); i++) { key = string[i]; if (key == 10) /* new line - convert to ^M */ key = 13; _send_key_to_curses(key, NULL, FALSE); } XtFree(value); } static void _paste_utf8(Widget w, XtPointer event, Atom *selection, Atom *type, XtPointer value, unsigned long *length, int *format) { wchar_t key; size_t i = 0, len; char *string = value; XC_LOG(("_paste_utf8() - called\n")); if (!*type || !*length) { XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, _paste_string, event, ((XButtonEvent *)event)->time); return; } len = *length; if (!string) return; while (string[i] && (i < len)) { int retval = _from_utf8(&key, string + i, len - i); if (retval < 1) return; if (key == 10) /* new line - convert to ^M */ key = 13; _send_key_to_curses(key, NULL, FALSE); i += retval; } XtFree(value); } static void XCursesPasteSelection(Widget w, XButtonEvent *button_event) { XC_LOG(("XCursesPasteSelection() - called\n")); XtGetSelectionValue(w, XA_PRIMARY, XA_UTF8_STRING(XtDisplay(w)), _paste_utf8, (XtPointer)button_event, button_event->time); } static Boolean _convert_proc(Widget w, Atom *selection, Atom *target, Atom *type_return, XtPointer *value_return, unsigned long *length_return, int *format_return) { XC_LOG(("_convert_proc() - called\n")); if (*target == XA_TARGETS(XtDisplay(topLevel))) { XSelectionRequestEvent *req = XtGetSelectionRequest(w, *selection, (XtRequestId)NULL); Atom *targetP; XPointer std_targets; unsigned long std_length; XmuConvertStandardSelection(topLevel, req->time, selection, target, type_return, &std_targets, &std_length, format_return); *length_return = std_length + 2; *value_return = XtMalloc(sizeof(Atom) * (*length_return)); targetP = *(Atom**)value_return; *targetP++ = XA_STRING; *targetP++ = XA_UTF8_STRING(XtDisplay(topLevel)); memmove((void *)targetP, (const void *)std_targets, sizeof(Atom) * std_length); XtFree((char *)std_targets); *type_return = XA_ATOM; *format_return = sizeof(Atom) * 8; return True; } else if (*target == XA_UTF8_STRING(XtDisplay(topLevel)) || *target == XA_STRING) { bool utf8 = !(*target == XA_STRING); char *data = XtMalloc(tmpsel_length * 3 + 1); chtype *tmp = tmpsel; int ret_length = 0; if (utf8) { while (*tmp) ret_length += _to_utf8(data + ret_length, *tmp++); } else while (*tmp) data[ret_length++] = *tmp++ & 0xff; data[ret_length++] = '\0'; *value_return = data; *length_return = ret_length; *format_return = 8; *type_return = *target; return True; } else return XmuConvertStandardSelection(topLevel, CurrentTime, selection, target, type_return, (XPointer*)value_return, length_return, format_return); } static void _lose_ownership(Widget w, Atom *type) { XC_LOG(("_lose_ownership() - called\n")); if (tmpsel) free(tmpsel); tmpsel = NULL; tmpsel_length = 0; _selection_off(); } static void _show_selection(int start_x, int start_y, int end_x, int end_y, bool highlight) { int i, num_cols, start_col, row; PDC_LOG(("%s:_show_selection() - called StartX: %d StartY: %d " "EndX: %d EndY: %d Highlight: %d\n", XCLOGMSG, start_x, start_y, end_x, end_y, highlight)); for (i = 0; i < end_y - start_y + 1; i++) { if (start_y == end_y) /* only one line */ { start_col = start_x; num_cols = end_x - start_x + 1; row = start_y; } else if (!i) /* first line */ { start_col = start_x; num_cols = COLS - start_x; row = start_y; } else if (start_y + i == end_y) /* last line */ { start_col = 0; num_cols = end_x + 1; row = end_y; } else /* full line */ { start_col = 0; num_cols = COLS; row = start_y + i; } XC_get_line_lock(row); _display_text((const chtype *)(Xcurscr + XCURSCR_Y_OFF(row) + (start_col * sizeof(chtype))), row, start_col, num_cols, highlight); XC_release_line_lock(row); } } static void _selection_off(void) { XC_LOG(("_selection_off() - called\n")); _display_screen(); selection_start_x = selection_start_y = selection_end_x = selection_end_y = 0; mouse_selection = FALSE; } static void _selection_on(int x, int y) { XC_LOG(("_selection_on() - called\n")); selection_start_x = selection_end_x = x; selection_start_y = selection_end_y = y; } static void _selection_extend(int x, int y) { int temp, current_start, current_end, current_start_x, current_end_x, current_start_y, current_end_y, new_start, new_end, new_start_x, new_end_x, new_start_y, new_end_y; XC_LOG(("_selection_extend() - called\n")); mouse_selection = TRUE; /* convert x/y coordinates into start/stop */ current_start = (selection_start_y * COLS) + selection_start_x; current_end = (selection_end_y * COLS) + selection_end_x; if (current_start > current_end) { current_start_x = selection_end_x; current_start_y = selection_end_y; current_end_x = selection_start_x; current_end_y = selection_start_y; temp = current_start; current_start = current_end; current_end = temp; } else { current_end_x = selection_end_x; current_end_y = selection_end_y; current_start_x = selection_start_x; current_start_y = selection_start_y; } /* Now we have the current selection as a linear expression. Convert the new position to a linear expression. */ selection_end_x = x; selection_end_y = y; /* convert x/y coordinates into start/stop */ new_start = (selection_start_y * COLS) + selection_start_x; new_end = (selection_end_y * COLS) + selection_end_x; if (new_start > new_end) { new_start_x = selection_end_x; new_start_y = selection_end_y; new_end_x = selection_start_x; new_end_y = selection_start_y; temp = new_start; new_start = new_end; new_end = temp; } else { new_end_x = selection_end_x; new_end_y = selection_end_y; new_start_x = selection_start_x; new_start_y = selection_start_y; } if (new_end > current_end) _show_selection(current_end_x, current_end_y, new_end_x, new_end_y, TRUE); else if (new_end < current_end) _show_selection(new_end_x, new_end_y, current_end_x, current_end_y, FALSE); else if (new_start < current_start) _show_selection(new_start_x, new_start_y, current_start_x, current_start_y, TRUE); else if (new_start > current_start) _show_selection(current_start_x, current_start_y, new_start_x, new_start_y, FALSE); else _show_selection(current_start_x, current_start_y, new_start_x, new_start_y, TRUE); } static void _selection_set(void) { int i, j, start, end, start_x, end_x, start_y, end_y, num_cols, start_col, row, num_chars, ch, last_nonblank, length, newlen; chtype *ptr = NULL; XC_LOG(("_selection_set() - called\n")); /* convert x/y coordinates into start/stop */ start = (selection_start_y * COLS) + selection_start_x; end = (selection_end_y * COLS) + selection_end_x; if (start == end) { if (tmpsel) free(tmpsel); tmpsel = NULL; tmpsel_length = 0; return; } if (start > end) { start_x = selection_end_x; start_y = selection_end_y; end_x = selection_start_x; end_y = selection_start_y; length = start - end + 1; } else { end_x = selection_end_x; end_y = selection_end_y; start_x = selection_start_x; start_y = selection_start_y; length = end - start + 1; } newlen = length + end_y - start_y + 2; if (length > (int)tmpsel_length) { if (!tmpsel_length) tmpsel = malloc(newlen * sizeof(chtype)); else tmpsel = realloc(tmpsel, newlen * sizeof(chtype)); } if (!tmpsel) { tmpsel_length = 0; return; } tmpsel_length = length; num_chars = 0; for (i = 0; i < end_y - start_y + 1; i++) { if (start_y == end_y) /* only one line */ { start_col = start_x; num_cols = end_x - start_x + 1; row = start_y; } else if (!i) /* first line */ { start_col = start_x; num_cols = COLS - start_x; row = start_y; } else if (start_y + i == end_y) /* last line */ { start_col = 0; num_cols = end_x + 1; row = end_y; } else /* full line */ { start_col = 0; num_cols = COLS; row = start_y + i; } XC_get_line_lock(row); ptr = (chtype *)(Xcurscr + XCURSCR_Y_OFF(row) + start_col * sizeof(chtype)); if (i < end_y - start_y) { last_nonblank = 0; for (j = 0; j < num_cols; j++) { ch = (int)(ptr[j] & A_CHARTEXT); if (ch != (int)' ') last_nonblank = j; } } else last_nonblank = num_cols - 1; for (j = 0; j <= last_nonblank; j++) tmpsel[num_chars++] = ptr[j]; XC_release_line_lock(row); if (i < end_y - start_y) tmpsel[num_chars++] = '\n'; } tmpsel[num_chars] = '\0'; tmpsel_length = num_chars; } static void _display_cursor(int old_row, int old_x, int new_row, int new_x) { int xpos, ypos, i; chtype *ch; short fore = 0, back = 0; PDC_LOG(("%s:_display_cursor() - draw char at row: %d col %d\n", XCLOGMSG, old_row, old_x)); /* if the cursor position is outside the boundary of the screen, ignore the request */ if (old_row >= XCursesLINES || old_x >= COLS || new_row >= XCursesLINES || new_x >= COLS) return; /* display the character at the current cursor position */ PDC_LOG(("%s:_display_cursor() - draw char at row: %d col %d\n", XCLOGMSG, old_row, old_x)); _display_text((const chtype *)(Xcurscr + (XCURSCR_Y_OFF(old_row) + (old_x * sizeof(chtype)))), old_row, old_x, 1, FALSE); /* display the cursor at the new cursor position */ if (!SP->visibility) return; /* cursor not displayed, no more to do */ _make_xy(new_x, new_row, &xpos, &ypos); ch = (chtype *)(Xcurscr + XCURSCR_Y_OFF(new_row) + new_x * sizeof(chtype)); _set_cursor_color(ch, &fore, &back); if (vertical_cursor) { XSetForeground(XCURSESDISPLAY, rect_cursor_gc, colors[back]); for (i = 1; i <= SP->visibility; i++) XDrawLine(XCURSESDISPLAY, XCURSESWIN, rect_cursor_gc, xpos + i, ypos - xc_app_data.normalFont->ascent, xpos + i, ypos - xc_app_data.normalFont->ascent + font_height - 1); } else { if (SP->visibility == 1) { /* cursor visibility normal */ XSetForeground(XCURSESDISPLAY, rect_cursor_gc, colors[back]); for (i = 0; i < xc_app_data.normalFont->descent + 2; i++) XDrawLine(XCURSESDISPLAY, XCURSESWIN, rect_cursor_gc, xpos, ypos - 2 + i, xpos + font_width, ypos - 2 + i); } else { /* cursor visibility high */ #ifdef PDC_WIDE XChar2b buf[2]; buf[0].byte1 = (*ch & 0xff00) >> 8; buf[0].byte2 = *ch & 0x00ff; buf[1].byte1 = buf[1].byte2 = 0; #else char buf[2]; buf[0] = *ch & 0xff; buf[1] = '\0'; #endif XSetForeground(XCURSESDISPLAY, block_cursor_gc, colors[fore]); XSetBackground(XCURSESDISPLAY, block_cursor_gc, colors[back]); #ifdef PDC_WIDE XDrawImageString16( #else XDrawImageString( #endif XCURSESDISPLAY, XCURSESWIN, block_cursor_gc, xpos, ypos, buf, 1); } } PDC_LOG(("%s:_display_cursor() - draw cursor at row %d col %d\n", XCLOGMSG, new_row, new_x)); } static void _redraw_cursor(void) { _display_cursor(SP->cursrow, SP->curscol, SP->cursrow, SP->curscol); } static void _handle_enter_leave(Widget w, XtPointer client_data, XEvent *event, Boolean *unused) { XC_LOG(("_handle_enter_leave called\n")); switch(event->type) { case EnterNotify: XC_LOG(("EnterNotify received\n")); window_entered = TRUE; break; case LeaveNotify: XC_LOG(("LeaveNotify received\n")); window_entered = FALSE; /* Display the cursor so it stays on while the window is not current */ _redraw_cursor(); break; default: PDC_LOG(("%s:_handle_enter_leave - unknown event %d\n", XCLOGMSG, event->type)); } } static void _send_key_to_curses(unsigned long key, MOUSE_STATUS *ms, bool key_code) { PDC_LOG(("%s:_send_key_to_curses() - called: sending %d\n", XCLOGMSG, key)); SP->key_code = key_code; if (XC_write_socket(xc_key_sock, &key, sizeof(unsigned long)) < 0) _exit_process(1, SIGKILL, "exiting from _send_key_to_curses"); if (ms) { MOUSE_LOG(("%s:writing mouse stuff\n", XCLOGMSG)); if (XC_write_socket(xc_key_sock, ms, sizeof(MOUSE_STATUS)) < 0) _exit_process(1, SIGKILL, "exiting from _send_key_to_curses"); } } static void _blink_cursor(XtPointer unused, XtIntervalId *id) { XC_LOG(("_blink_cursor() - called:\n")); if (window_entered) { if (visible_cursor) { /* Cursor currently ON, turn it off */ int save_visibility = SP->visibility; SP->visibility = 0; _redraw_cursor(); SP->visibility = save_visibility; visible_cursor = FALSE; } else { /* Cursor currently OFF, turn it on */ _redraw_cursor(); visible_cursor = TRUE; } } XtAppAddTimeOut(app_context, xc_app_data.cursorBlinkRate, _blink_cursor, NULL); } static void XCursesButton(Widget w, XEvent *event, String *params, Cardinal *nparams) { int button_no; static int last_button_no = 0; static Time last_button_press_time = 0; MOUSE_STATUS save_mouse_status; bool send_key = TRUE; static bool remove_release; static bool handle_real_release; XC_LOG(("XCursesButton() - called\n")); keysym = 0; /* suppress any modifier key return */ save_mouse_status = Mouse_status; button_no = event->xbutton.button; /* It appears that under X11R6 (at least on Linux), that an event_type of ButtonMotion does not include the mouse button in the event. The following code is designed to cater for this situation. */ if (!button_no) button_no = last_button_no; last_button_no = button_no; Mouse_status.changes = 0; switch(event->type) { case ButtonPress: /* Handle button 4 and 5, which are normally mapped to the wheel mouse scroll up and down */ if (button_no == 4 || button_no == 5) { /* Send the KEY_MOUSE to curses program */ memset(&Mouse_status, 0, sizeof(Mouse_status)); Mouse_status.changes = (button_no == 5) ? PDC_MOUSE_WHEEL_DOWN : PDC_MOUSE_WHEEL_UP; MOUSE_X_POS = MOUSE_Y_POS = -1; _send_key_to_curses(KEY_MOUSE, &Mouse_status, TRUE); remove_release = TRUE; return; } if (button_no == 2 && (!SP->_trap_mbe || (event->xbutton.state & ShiftMask))) { XCursesPasteSelection(drawing, (XButtonEvent *)event); remove_release = TRUE; return; } remove_release = False; handle_real_release = False; MOUSE_LOG(("\nButtonPress\n")); if ((event->xbutton.time - last_button_press_time) < xc_app_data.doubleClickPeriod) { MOUSE_X_POS = save_mouse_status.x; MOUSE_Y_POS = save_mouse_status.y; BUTTON_STATUS(button_no) = BUTTON_DOUBLE_CLICKED; _selection_off(); remove_release = True; } else { napms(SP->mouse_wait); event->type = ButtonRelease; XSendEvent(event->xbutton.display, event->xbutton.window, True, 0, event); last_button_press_time = event->xbutton.time; return; } last_button_press_time = event->xbutton.time; break; case MotionNotify: MOUSE_LOG(("\nMotionNotify: y: %d x: %d Width: %d " "Height: %d\n", event->xbutton.y, event->xbutton.x, font_width, font_height)); MOUSE_X_POS = (event->xbutton.x - xc_app_data.borderWidth) / font_width; MOUSE_Y_POS = (event->xbutton.y - xc_app_data.borderWidth) / font_height; if (button_no == 1 && (!SP->_trap_mbe || (event->xbutton.state & ShiftMask))) { _selection_extend(MOUSE_X_POS, MOUSE_Y_POS); send_key = FALSE; } else _selection_off(); /* Throw away mouse movements if they are in the same character position as the last mouse event, or if we are currently in the middle of a double click event. */ if ((MOUSE_X_POS == save_mouse_status.x && MOUSE_Y_POS == save_mouse_status.y) || save_mouse_status.button[button_no - 1] == BUTTON_DOUBLE_CLICKED) { send_key = FALSE; break; } Mouse_status.changes |= PDC_MOUSE_MOVED; break; case ButtonRelease: if (remove_release) { MOUSE_LOG(("Release at: %ld - removed\n", event->xbutton.time)); return; } else { MOUSE_X_POS = (event->xbutton.x - xc_app_data.borderWidth) / font_width; MOUSE_Y_POS = (event->xbutton.y - xc_app_data.borderWidth) / font_height; if (!handle_real_release) { if ((event->xbutton.time - last_button_press_time) < SP->mouse_wait && (event->xbutton.time != last_button_press_time)) { /* The "real" release was shorter than usleep() time; therefore generate a click event */ MOUSE_LOG(("Release at: %ld - click\n", event->xbutton.time)); BUTTON_STATUS(button_no) = BUTTON_CLICKED; if (button_no == 1 && mouse_selection && (!SP->_trap_mbe || (event->xbutton.state & ShiftMask))) { send_key = FALSE; if (XtOwnSelection(topLevel, XA_PRIMARY, event->xbutton.time, _convert_proc, _lose_ownership, NULL) == False) _selection_off(); } else _selection_off(); /* Ensure the "pseudo" release event is ignored */ remove_release = True; handle_real_release = False; break; } else { /* Button release longer than usleep() time; therefore generate a press and wait for the real release to occur later. */ MOUSE_LOG(("Generated Release at: %ld - " "press & release\n", event->xbutton.time)); BUTTON_STATUS(button_no) = BUTTON_PRESSED; if (button_no == 1 && (!SP->_trap_mbe || (event->xbutton.state & ShiftMask))) { _selection_off(); _selection_on(MOUSE_X_POS, MOUSE_Y_POS); } handle_real_release = True; break; } } else { MOUSE_LOG(("Release at: %ld - released\n", event->xbutton.time)); } } MOUSE_LOG(("\nButtonRelease\n")); BUTTON_STATUS(button_no) = BUTTON_RELEASED; if (button_no == 1 && mouse_selection && (!SP->_trap_mbe || (event->xbutton.state & ShiftMask))) { send_key = FALSE; if (XtOwnSelection(topLevel, XA_PRIMARY, event->xbutton.time, _convert_proc, _lose_ownership, NULL) == False) _selection_off(); _selection_set(); } else _selection_off(); break; } /* Set up the mouse status fields in preparation for sending */ Mouse_status.changes |= 1 << (button_no - 1); if (Mouse_status.changes & PDC_MOUSE_MOVED && BUTTON_STATUS(button_no) == BUTTON_PRESSED) BUTTON_STATUS(button_no) = BUTTON_MOVED; if (event->xbutton.state & ShiftMask) BUTTON_STATUS(button_no) |= BUTTON_SHIFT; if (event->xbutton.state & ControlMask) BUTTON_STATUS(button_no) |= BUTTON_CONTROL; if (event->xbutton.state & Mod1Mask) BUTTON_STATUS(button_no) |= BUTTON_ALT; /* If we are ignoring the event, or the mouse position is outside the bounds of the screen (because of the border), return here */ MOUSE_LOG(("Button: %d x: %d y: %d Button status: %x " "Mouse status: %x\n", button_no, MOUSE_X_POS, MOUSE_Y_POS, BUTTON_STATUS(button_no), Mouse_status.changes)); MOUSE_LOG(("Send: %d Button1: %x Button2: %x Button3: %x %d %d\n", send_key, BUTTON_STATUS(1), BUTTON_STATUS(2), BUTTON_STATUS(3), XCursesLINES, XCursesCOLS)); if (!send_key || MOUSE_X_POS < 0 || MOUSE_X_POS >= XCursesCOLS || MOUSE_Y_POS < 0 || MOUSE_Y_POS >= XCursesLINES) return; /* Send the KEY_MOUSE to curses program */ _send_key_to_curses(KEY_MOUSE, &Mouse_status, TRUE); } static void _scroll_up_down(Widget w, XtPointer client_data, XtPointer call_data) { int pixels = (long) call_data; int total_y = SP->sb_total_y * font_height; int viewport_y = SP->sb_viewport_y * font_height; int cur_y = SP->sb_cur_y * font_height; /* When pixels is negative, right button pressed, move data down, thumb moves up. Otherwise, left button pressed, pixels positive, move data up, thumb down. */ cur_y += pixels; /* limit panning to size of overall */ if (cur_y < 0) cur_y = 0; else if (cur_y > (total_y - viewport_y)) cur_y = total_y - viewport_y; SP->sb_cur_y = cur_y / font_height; XawScrollbarSetThumb(w, (double)((double)cur_y / (double)total_y), (double)((double)viewport_y / (double)total_y)); /* Send a key: if pixels negative, send KEY_SCROLL_DOWN */ _send_key_to_curses(KEY_SF, NULL, TRUE); } static void _scroll_left_right(Widget w, XtPointer client_data, XtPointer call_data) { int pixels = (long) call_data; int total_x = SP->sb_total_x * font_width; int viewport_x = SP->sb_viewport_x * font_width; int cur_x = SP->sb_cur_x * font_width; cur_x += pixels; /* limit panning to size of overall */ if (cur_x < 0) cur_x = 0; else if (cur_x > (total_x - viewport_x)) cur_x = total_x - viewport_x; SP->sb_cur_x = cur_x / font_width; XawScrollbarSetThumb(w, (double)((double)cur_x / (double)total_x), (double)((double)viewport_x / (double)total_x)); _send_key_to_curses(KEY_SR, NULL, TRUE); } static void _thumb_up_down(Widget w, XtPointer client_data, XtPointer call_data) { double percent = *(double *) call_data; double total_y = (double)SP->sb_total_y; double viewport_y = (double)SP->sb_viewport_y; int cur_y = SP->sb_cur_y; /* If the size of the viewport is > overall area simply return, as no scrolling is permitted. */ if (SP->sb_viewport_y >= SP->sb_total_y) return; if ((SP->sb_cur_y = (int)((double)total_y * percent)) >= (total_y - viewport_y)) SP->sb_cur_y = total_y - viewport_y; XawScrollbarSetThumb(w, (double)(cur_y / total_y), (double)(viewport_y / total_y)); _send_key_to_curses(KEY_SF, NULL, TRUE); } static void _thumb_left_right(Widget w, XtPointer client_data, XtPointer call_data) { double percent = *(double *) call_data; double total_x = (double)SP->sb_total_x; double viewport_x = (double)SP->sb_viewport_x; int cur_x = SP->sb_cur_x; if (SP->sb_viewport_x >= SP->sb_total_x) return; if ((SP->sb_cur_x = (int)((float)total_x * percent)) >= (total_x - viewport_x)) SP->sb_cur_x = total_x - viewport_x; XawScrollbarSetThumb(w, (double)(cur_x / total_x), (double)(viewport_x / total_x)); _send_key_to_curses(KEY_SR, NULL, TRUE); } static void _exit_process(int rc, int sig, char *msg) { if (rc || sig) fprintf(stderr, "%s:_exit_process() - called: rc:%d sig:%d <%s>\n", XCLOGMSG, rc, sig, msg); shmdt((char *)SP); shmdt((char *)Xcurscr); shmctl(shmidSP, IPC_RMID, 0); shmctl(shmid_Xcurscr, IPC_RMID, 0); if (bitmap_file) { XFreePixmap(XCURSESDISPLAY, icon_bitmap); free(bitmap_file); } #ifdef HAVE_XPM_H if (pixmap_file) { XFreePixmap(XCURSESDISPLAY, icon_pixmap); XFreePixmap(XCURSESDISPLAY, icon_pixmap_mask); free(pixmap_file); } #endif XFreeGC(XCURSESDISPLAY, normal_gc); XFreeGC(XCURSESDISPLAY, italic_gc); XFreeGC(XCURSESDISPLAY, block_cursor_gc); XFreeGC(XCURSESDISPLAY, rect_cursor_gc); XFreeGC(XCURSESDISPLAY, border_gc); #ifdef PDC_XIM XDestroyIC(Xic); #endif shutdown(xc_display_sock, 2); close(xc_display_sock); shutdown(xc_exit_sock, 2); close(xc_exit_sock); shutdown(xc_key_sock, 2); close(xc_key_sock); if (sig) kill(xc_otherpid, sig); /* to kill parent process */ _exit(rc); } static void _resize(void) { short save_atrtab[PDC_COLOR_PAIRS * 2]; after_first_curses_request = FALSE; SP->lines = XCursesLINES = ((resize_window_height - (2 * xc_app_data.borderWidth)) / font_height); LINES = XCursesLINES - SP->linesrippedoff - SP->slklines; SP->cols = COLS = XCursesCOLS = ((resize_window_width - (2 * xc_app_data.borderWidth)) / font_width); window_width = resize_window_width; window_height = resize_window_height; visible_cursor = TRUE; _draw_border(); /* Detach and drop the current shared memory segment and create and attach to a new segment */ memcpy(save_atrtab, xc_atrtab, sizeof(save_atrtab)); SP->XcurscrSize = XCURSCR_SIZE; shmdt((char *)Xcurscr); shmctl(shmid_Xcurscr, IPC_RMID, 0); if ((shmid_Xcurscr = shmget(shmkey_Xcurscr, SP->XcurscrSize + XCURSESSHMMIN, 0700 | IPC_CREAT)) < 0) { perror("Cannot allocate shared memory for curscr"); _exit_process(4, SIGKILL, "exiting from _process_curses_requests"); } Xcurscr = (unsigned char*)shmat(shmid_Xcurscr, 0, 0); memset(Xcurscr, 0, SP->XcurscrSize); xc_atrtab = (short *)(Xcurscr + XCURSCR_ATRTAB_OFF); memcpy(xc_atrtab, save_atrtab, sizeof(save_atrtab)); } /* For PDC_set_title() */ static void _set_title(void) { char title[1024]; /* big enough for window title */ int pos; if ((XC_read_socket(xc_display_sock, &pos, sizeof(int)) < 0) || (XC_read_socket(xc_display_sock, title, pos) < 0)) { _exit_process(5, SIGKILL, "exiting from _set_title"); } XtVaSetValues(topLevel, XtNtitle, title, NULL); } /* For color_content() */ static void _get_color(void) { XColor *tmp = (XColor *)(Xcurscr + XCURSCR_XCOLOR_OFF); int index = tmp->pixel; Colormap cmap = DefaultColormap(XCURSESDISPLAY, DefaultScreen(XCURSESDISPLAY)); if (index < 0 || index >= MAX_COLORS) _exit_process(4, SIGKILL, "exiting from _get_color"); tmp->pixel = colors[index]; XQueryColor(XCURSESDISPLAY, cmap, tmp); } /* For init_color() */ static void _set_color(void) { XColor *tmp = (XColor *)(Xcurscr + XCURSCR_XCOLOR_OFF); int index = tmp->pixel; Colormap cmap = DefaultColormap(XCURSESDISPLAY, DefaultScreen(XCURSESDISPLAY)); if (index < 0 || index >= MAX_COLORS) _exit_process(4, SIGKILL, "exiting from _set_color"); if (XAllocColor(XCURSESDISPLAY, cmap, tmp)) { XFreeColors(XCURSESDISPLAY, cmap, colors + index, 1, 0); colors[index] = tmp->pixel; _display_screen(); } } /* For PDC_getclipboard() */ static void _get_selection(Widget w, XtPointer data, Atom *selection, Atom *type, XtPointer value, unsigned long *length, int *format) { unsigned char *src = value; int pos, len = *length; XC_LOG(("_get_selection() - called\n")); if (!value && !len) { if (XC_write_display_socket_int(PDC_CLIP_EMPTY) < 0) _exit_process(4, SIGKILL, "exiting from _get_selection"); } else { /* Here all is OK, send PDC_CLIP_SUCCESS, then length, then contents */ if (XC_write_display_socket_int(PDC_CLIP_SUCCESS) < 0) _exit_process(4, SIGKILL, "exiting from _get_selection"); if (XC_write_display_socket_int(len) < 0) _exit_process(4, SIGKILL, "exiting from _get_selection"); for (pos = 0; pos < len; pos++) { #ifdef PDC_WIDE wchar_t c; #else unsigned char c; #endif c = *src++; if (XC_write_socket(xc_display_sock, &c, sizeof(c)) < 0) _exit_process(4, SIGKILL, "exiting from _get_selection"); } } } #ifdef PDC_WIDE static void _get_selection_utf8(Widget w, XtPointer data, Atom *selection, Atom *type, XtPointer value, unsigned long *length, int *format) { int len = *length; XC_LOG(("_get_selection_utf8() - called\n")); if (!*type || !*length) { XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, _get_selection, (XtPointer)NULL, 0); return; } if (!value && !len) { if (XC_write_display_socket_int(PDC_CLIP_EMPTY) >= 0) return; } else { wchar_t *wcontents = malloc((len + 1) * sizeof(wchar_t)); char *src = value; int i = 0; while (*src && i < (*length)) { int retval = _from_utf8(wcontents + i, src, len); src += retval; len -= retval; i++; } wcontents[i] = 0; len = i; /* Here all is OK, send PDC_CLIP_SUCCESS, then length, then contents */ if (XC_write_display_socket_int(PDC_CLIP_SUCCESS) >= 0) if (XC_write_display_socket_int(len) >= 0) if (XC_write_socket(xc_display_sock, wcontents, len * sizeof(wchar_t)) >= 0) { free(wcontents); return; } } _exit_process(4, SIGKILL, "exiting from _get_selection_utf8"); } #endif /* For PDC_setclipboard() */ static void _set_selection(void) { long length, pos; int status; if (XC_read_socket(xc_display_sock, &length, sizeof(long)) < 0) _exit_process(5, SIGKILL, "exiting from _set_selection"); if (length > (long)tmpsel_length) { if (!tmpsel_length) tmpsel = malloc((length + 1) * sizeof(chtype)); else tmpsel = realloc(tmpsel, (length + 1) * sizeof(chtype)); } if (!tmpsel) if (XC_write_display_socket_int(PDC_CLIP_MEMORY_ERROR) < 0) _exit_process(4, SIGKILL, "exiting from _set_selection"); for (pos = 0; pos < length; pos++) { #ifdef PDC_WIDE wchar_t c; #else unsigned char c; #endif if (XC_read_socket(xc_display_sock, &c, sizeof(c)) < 0) _exit_process(5, SIGKILL, "exiting from _set_selection"); tmpsel[pos] = c; } tmpsel_length = length; tmpsel[length] = 0; if (XtOwnSelection(topLevel, XA_PRIMARY, CurrentTime, _convert_proc, _lose_ownership, NULL) == False) { status = PDC_CLIP_ACCESS_ERROR; free(tmpsel); tmpsel = NULL; tmpsel_length = 0; } else status = PDC_CLIP_SUCCESS; _selection_off(); if (XC_write_display_socket_int(status) < 0) _exit_process(4, SIGKILL, "exiting from _set_selection"); } /* The curses process is waiting; tell it to continue */ static void _resume_curses(void) { if (XC_write_display_socket_int(CURSES_CONTINUE) < 0) _exit_process(4, SIGKILL, "exiting from _process_curses_requests"); } /* The curses process sent us a message */ static void _process_curses_requests(XtPointer client_data, int *fid, XtInputId *id) { struct timeval socket_timeout = {0}; int s; int old_row, new_row; int old_x, new_x; int pos, num_cols; char buf[12]; /* big enough for 2 integers */ XC_LOG(("_process_curses_requests() - called\n")); if (!received_map_notify) return; FD_ZERO(&xc_readfds); FD_SET(xc_display_sock, &xc_readfds); if ((s = select(FD_SETSIZE, (FD_SET_CAST)&xc_readfds, NULL, NULL, &socket_timeout)) < 0) _exit_process(2, SIGKILL, "exiting from _process_curses_requests" " - select failed"); if (!s) /* no requests pending - should never happen! */ return; if (FD_ISSET(xc_display_sock, &xc_readfds)) { /* read first integer to determine total message has been received */ XC_LOG(("_process_curses_requests() - before XC_read_socket()\n")); if (XC_read_socket(xc_display_sock, &num_cols, sizeof(int)) < 0) _exit_process(3, SIGKILL, "exiting from _process_curses_requests" " - first read"); XC_LOG(("_process_curses_requests() - after XC_read_socket()\n")); after_first_curses_request = TRUE; switch(num_cols) { case CURSES_EXIT: /* request from curses to stop */ XC_LOG(("CURSES_EXIT received from child\n")); _exit_process(0, 0, "XCursesProcess requested to exit by child"); break; case CURSES_BELL: XC_LOG(("CURSES_BELL received from child\n")); XBell(XCURSESDISPLAY, 50); break; /* request from curses to confirm completion of display */ case CURSES_REFRESH: XC_LOG(("CURSES_REFRESH received from child\n")); _refresh_screen(); _resume_curses(); break; case CURSES_REFRESH_SCROLLBAR: _refresh_scrollbar(); break; case CURSES_CURSOR: XC_LOG(("CURSES_CURSOR received from child\n")); if (XC_read_socket(xc_display_sock, buf, sizeof(int) * 2) < 0) _exit_process(5, SIGKILL, "exiting from CURSES_CURSOR " "_process_curses_requests"); memcpy(&pos, buf, sizeof(int)); old_row = pos & 0xFF; old_x = pos >> 8; memcpy(&pos, buf + sizeof(int), sizeof(int)); new_row = pos & 0xFF; new_x = pos >> 8; visible_cursor = TRUE; _display_cursor(old_row, old_x, new_row, new_x); break; case CURSES_DISPLAY_CURSOR: XC_LOG(("CURSES_DISPLAY_CURSOR received from child. Vis now: ")); XC_LOG((visible_cursor ? "1\n" : "0\n")); /* If the window is not active, ignore this command. The cursor will stay solid. */ if (window_entered) { if (visible_cursor) { /* Cursor currently ON, turn it off */ int save_visibility = SP->visibility; SP->visibility = 0; _redraw_cursor(); SP->visibility = save_visibility; visible_cursor = FALSE; } else { /* Cursor currently OFF, turn it on */ _redraw_cursor(); visible_cursor = TRUE; } } break; case CURSES_TITLE: XC_LOG(("CURSES_TITLE received from child\n")); _set_title(); break; case CURSES_RESIZE: XC_LOG(("CURSES_RESIZE received from child\n")); _resize(); _resume_curses(); break; case CURSES_GET_SELECTION: XC_LOG(("CURSES_GET_SELECTION received from child\n")); _resume_curses(); XtGetSelectionValue(topLevel, XA_PRIMARY, #ifdef PDC_WIDE XA_UTF8_STRING(XtDisplay(topLevel)), _get_selection_utf8, #else XA_STRING, _get_selection, #endif (XtPointer)NULL, 0); break; case CURSES_SET_SELECTION: XC_LOG(("CURSES_SET_SELECTION received from child\n")); _set_selection(); break; case CURSES_CLEAR_SELECTION: XC_LOG(("CURSES_CLEAR_SELECTION received from child\n")); _resume_curses(); _selection_off(); break; case CURSES_GET_COLOR: XC_LOG(("CURSES_GET_COLOR recieved from child\n")); _get_color(); _resume_curses(); break; case CURSES_SET_COLOR: XC_LOG(("CURSES_SET_COLOR recieved from child\n")); _set_color(); _resume_curses(); break; default: PDC_LOG(("%s:Unknown request %d\n", XCLOGMSG, num_cols)); } } } static void _handle_structure_notify(Widget w, XtPointer client_data, XEvent *event, Boolean *unused) { XC_LOG(("_handle_structure_notify() - called\n")); switch(event->type) { case ConfigureNotify: XC_LOG(("ConfigureNotify received\n")); /* Window has been resized, change width and height to send to place_text and place_graphics in next Expose. Also will need to kill (SIGWINCH) curses process if screen size changes. */ resize_window_width = event->xconfigure.width; resize_window_height = event->xconfigure.height; after_first_curses_request = FALSE; #ifdef SIGWINCH SP->resized = 1; kill(xc_otherpid, SIGWINCH); #endif _send_key_to_curses(KEY_RESIZE, NULL, TRUE); break; case MapNotify: XC_LOG(("MapNotify received\n")); received_map_notify = 1; _draw_border(); break; default: PDC_LOG(("%s:_handle_structure_notify - unknown event %d\n", XCLOGMSG, event->type)); } } static RETSIGTYPE _handle_signals(int signo) { int flag = CURSES_EXIT; PDC_LOG(("%s:_handle_signals() - called: %d\n", XCLOGMSG, signo)); /* Patch by: Georg Fuchs */ XCursesSetSignal(signo, _handle_signals); #ifdef SIGTSTP if (signo == SIGTSTP) { pause(); return; } #endif #ifdef SIGCONT if (signo == SIGCONT) return; #endif #ifdef SIGCLD if (signo == SIGCLD) return; #endif #ifdef SIGTTIN if (signo == SIGTTIN) return; #endif #ifdef SIGWINCH if (signo == SIGWINCH) return; #endif /* End of patch by: Georg Fuchs */ XCursesSetSignal(signo, SIG_IGN); /* Send a CURSES_EXIT to myself */ if (XC_write_socket(xc_exit_sock, &flag, sizeof(int)) < 0) _exit_process(7, signo, "exiting from _handle_signals"); } #ifdef PDC_XIM static void _dummy_handler(Widget w, XtPointer client_data, XEvent *event, Boolean *unused) { } #endif int XCursesSetupX(int argc, char *argv[]) { char *myargv[] = {"PDCurses", NULL}; extern bool sb_started; int italic_font_valid; XColor pointerforecolor, pointerbackcolor; XrmValue rmfrom, rmto; int i = 0; int minwidth, minheight; XC_LOG(("XCursesSetupX called\n")); if (!argv) { argv = myargv; argc = 1; } program_name = argv[0]; /* Keep open the 'write' end of the socket so the XCurses process can send a CURSES_EXIT to itself from within the signal handler */ xc_exit_sock = xc_display_sockets[0]; xc_display_sock = xc_display_sockets[1]; close(xc_key_sockets[0]); xc_key_sock = xc_key_sockets[1]; /* Trap all signals when XCurses is the child process, but only if they haven't already been ignored by the application. */ for (i = 0; i < PDC_MAX_SIGNALS; i++) if (XCursesSetSignal(i, _handle_signals) == SIG_IGN) XCursesSetSignal(i, SIG_IGN); /* Start defining X Toolkit things */ #if XtSpecificationRelease > 4 XtSetLanguageProc(NULL, (XtLanguageProc)NULL, NULL); #endif /* Exit if no DISPLAY variable set */ if (!getenv("DISPLAY")) { fprintf(stderr, "Error: no DISPLAY variable set\n"); kill(xc_otherpid, SIGKILL); return ERR; } /* Initialise the top level widget */ topLevel = XtVaAppInitialize(&app_context, class_name, options, XtNumber(options), &argc, argv, NULL, NULL); XtVaGetApplicationResources(topLevel, &xc_app_data, app_resources, XtNumber(app_resources), NULL); /* Check application resource values here */ font_width = xc_app_data.normalFont->max_bounds.rbearing - xc_app_data.normalFont->min_bounds.lbearing; font_height = xc_app_data.normalFont->max_bounds.ascent + xc_app_data.normalFont->max_bounds.descent; font_ascent = xc_app_data.normalFont->max_bounds.ascent; font_descent = xc_app_data.normalFont->max_bounds.descent; /* Check that the italic font and normal fonts are the same size */ /* This appears backwards */ italic_font_valid = font_width != xc_app_data.italicFont->max_bounds.rbearing - xc_app_data.italicFont->min_bounds.lbearing || font_height != xc_app_data.italicFont->max_bounds.ascent + xc_app_data.italicFont->max_bounds.descent; /* Calculate size of display window */ XCursesCOLS = xc_app_data.cols; XCursesLINES = xc_app_data.lines; window_width = font_width * XCursesCOLS + 2 * xc_app_data.borderWidth; window_height = font_height * XCursesLINES + 2 * xc_app_data.borderWidth; minwidth = font_width * 2 + xc_app_data.borderWidth * 2; minheight = font_height * 2 + xc_app_data.borderWidth * 2; /* Set up the icon for the application; the default is an internal one for PDCurses. Then set various application level resources. */ _get_icon(); #ifdef HAVE_XPM_H if (xc_app_data.pixmap && xc_app_data.pixmap[0]) XtVaSetValues(topLevel, XtNminWidth, minwidth, XtNminHeight, minheight, XtNbaseWidth, xc_app_data.borderWidth * 2, XtNbaseHeight, xc_app_data.borderWidth * 2, XtNiconPixmap, icon_pixmap, XtNiconMask, icon_pixmap_mask, NULL); else #endif XtVaSetValues(topLevel, XtNminWidth, minwidth, XtNminHeight, minheight, XtNbaseWidth, xc_app_data.borderWidth * 2, XtNbaseHeight, xc_app_data.borderWidth * 2, XtNiconPixmap, icon_bitmap, NULL); /* Create a BOX widget in which to draw */ if (xc_app_data.scrollbarWidth && sb_started) { scrollBox = XtVaCreateManagedWidget(program_name, scrollBoxWidgetClass, topLevel, XtNwidth, window_width + xc_app_data.scrollbarWidth, XtNheight, window_height + xc_app_data.scrollbarWidth, XtNwidthInc, font_width, XtNheightInc, font_height, NULL); drawing = XtVaCreateManagedWidget(program_name, boxWidgetClass, scrollBox, XtNwidth, window_width, XtNheight, window_height, XtNwidthInc, font_width, XtNheightInc, font_height, NULL); scrollVert = XtVaCreateManagedWidget("scrollVert", scrollbarWidgetClass, scrollBox, XtNorientation, XtorientVertical, XtNheight, window_height, XtNwidth, xc_app_data.scrollbarWidth, NULL); XtAddCallback(scrollVert, XtNscrollProc, _scroll_up_down, drawing); XtAddCallback(scrollVert, XtNjumpProc, _thumb_up_down, drawing); scrollHoriz = XtVaCreateManagedWidget("scrollHoriz", scrollbarWidgetClass, scrollBox, XtNorientation, XtorientHorizontal, XtNwidth, window_width, XtNheight, xc_app_data.scrollbarWidth, NULL); XtAddCallback(scrollHoriz, XtNscrollProc, _scroll_left_right, drawing); XtAddCallback(scrollHoriz, XtNjumpProc, _thumb_left_right, drawing); } else { drawing = XtVaCreateManagedWidget(program_name, boxWidgetClass, topLevel, XtNwidth, window_width, XtNheight, window_height, XtNwidthInc, font_width, XtNheightInc, font_height, NULL); XtVaSetValues(topLevel, XtNwidthInc, font_width, XtNheightInc, font_height, NULL); } /* Process any default translations */ XtAugmentTranslations(drawing, XtParseTranslationTable(default_translations)); XtAppAddActions(app_context, action_table, XtNumber(action_table)); /* Process the supplied colors */ _initialize_colors(); /* Determine text cursor alignment from resources */ if (!strcmp(xc_app_data.textCursor, "vertical")) vertical_cursor = TRUE; /* Now have LINES and COLS. Set these in the shared SP so the curses program can find them. */ LINES = XCursesLINES; COLS = XCursesCOLS; if ((shmidSP = shmget(shmkeySP, sizeof(SCREEN) + XCURSESSHMMIN, 0700 | IPC_CREAT)) < 0) { perror("Cannot allocate shared memory for SCREEN"); kill(xc_otherpid, SIGKILL); return ERR; } SP = (SCREEN*)shmat(shmidSP, 0, 0); memset(SP, 0, sizeof(SCREEN)); SP->XcurscrSize = XCURSCR_SIZE; SP->lines = XCursesLINES; SP->cols = XCursesCOLS; SP->mouse_wait = xc_app_data.clickPeriod; SP->audible = TRUE; PDC_LOG(("%s:SHM size for curscr %d\n", XCLOGMSG, SP->XcurscrSize)); if ((shmid_Xcurscr = shmget(shmkey_Xcurscr, SP->XcurscrSize + XCURSESSHMMIN, 0700 | IPC_CREAT)) < 0) { perror("Cannot allocate shared memory for curscr"); kill(xc_otherpid, SIGKILL); shmdt((char *)SP); shmctl(shmidSP, IPC_RMID, 0); return ERR; } Xcurscr = (unsigned char *)shmat(shmid_Xcurscr, 0, 0); memset(Xcurscr, 0, SP->XcurscrSize); xc_atrtab = (short *)(Xcurscr + XCURSCR_ATRTAB_OFF); PDC_LOG(("%s:shmid_Xcurscr %d shmkey_Xcurscr %d LINES %d COLS %d\n", XCLOGMSG, shmid_Xcurscr, shmkey_Xcurscr, LINES, COLS)); /* Add Event handlers to the drawing widget */ XtAddEventHandler(drawing, ExposureMask, False, _handle_expose, NULL); XtAddEventHandler(drawing, StructureNotifyMask, False, _handle_structure_notify, NULL); XtAddEventHandler(drawing, EnterWindowMask | LeaveWindowMask, False, _handle_enter_leave, NULL); XtAddEventHandler(topLevel, 0, True, _handle_nonmaskable, NULL); /* Add input handler from xc_display_sock (requests from curses program) */ XtAppAddInput(app_context, xc_display_sock, (XtPointer)XtInputReadMask, _process_curses_requests, NULL); /* If there is a cursorBlink resource, start the Timeout event */ if (xc_app_data.cursorBlinkRate) XtAppAddTimeOut(app_context, xc_app_data.cursorBlinkRate, _blink_cursor, NULL); /* Leave telling the curses process that it can start to here so that when the curses process makes a request, the Xcurses process can service the request. */ XC_write_display_socket_int(CURSES_CHILD); XtRealizeWidget(topLevel); /* Handle trapping of the WM_DELETE_WINDOW property */ wm_atom[0] = XInternAtom(XtDisplay(topLevel), "WM_DELETE_WINDOW", False); XSetWMProtocols(XtDisplay(topLevel), XtWindow(topLevel), wm_atom, 1); /* Create the Graphics Context for drawing. This MUST be done AFTER the associated widget has been realized. */ XC_LOG(("before _get_gc\n")); _get_gc(&normal_gc, xc_app_data.normalFont, COLOR_WHITE, COLOR_BLACK); _get_gc(&italic_gc, italic_font_valid ? xc_app_data.italicFont : xc_app_data.normalFont, COLOR_WHITE, COLOR_BLACK); _get_gc(&block_cursor_gc, xc_app_data.normalFont, COLOR_BLACK, COLOR_CURSOR); _get_gc(&rect_cursor_gc, xc_app_data.normalFont, COLOR_CURSOR, COLOR_BLACK); _get_gc(&border_gc, xc_app_data.normalFont, COLOR_BORDER, COLOR_BLACK); XSetLineAttributes(XCURSESDISPLAY, rect_cursor_gc, 2, LineSolid, CapButt, JoinMiter); XSetLineAttributes(XCURSESDISPLAY, border_gc, xc_app_data.borderWidth, LineSolid, CapButt, JoinMiter); /* Set the cursor for the application */ XDefineCursor(XCURSESDISPLAY, XCURSESWIN, xc_app_data.pointer); rmfrom.size = sizeof(Pixel); rmto.size = sizeof(XColor); rmto.addr = (XPointer)&pointerforecolor; rmfrom.addr = (XPointer)&(xc_app_data.pointerForeColor); XtConvertAndStore(drawing, XtRPixel, &rmfrom, XtRColor, &rmto); rmfrom.size = sizeof(Pixel); rmto.size = sizeof(XColor); rmfrom.addr = (XPointer)&(xc_app_data.pointerBackColor); rmto.addr = (XPointer)&pointerbackcolor; XtConvertAndStore(drawing, XtRPixel, &rmfrom, XtRColor, &rmto); XRecolorCursor(XCURSESDISPLAY, xc_app_data.pointer, &pointerforecolor, &pointerbackcolor); #ifndef PDC_XIM /* Convert the supplied compose key to a Keysym */ compose_key = XStringToKeysym(xc_app_data.composeKey); if (compose_key && IsModifierKey(compose_key)) { int i, j; KeyCode *kcp; XModifierKeymap *map; KeyCode compose_keycode = XKeysymToKeycode(XCURSESDISPLAY, compose_key); map = XGetModifierMapping(XCURSESDISPLAY); kcp = map->modifiermap; for (i = 0; i < 8; i++) { for (j = 0; j < map->max_keypermod; j++, kcp++) { if (!*kcp) continue; if (compose_keycode == *kcp) { compose_mask = state_mask[i]; break; } } if (compose_mask) break; } XFreeModifiermap(map); } #else Xim = XOpenIM(XCURSESDISPLAY, NULL, NULL, NULL); if (Xim) { Xic = XCreateIC(Xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, XCURSESWIN, NULL); } if (Xic) { long im_event_mask; XGetICValues(Xic, XNFilterEvents, &im_event_mask, NULL); if (im_event_mask) XtAddEventHandler(drawing, im_event_mask, False, _dummy_handler, NULL); XSetICFocus(Xic); } else { perror("ERROR: Cannot create input context"); kill(xc_otherpid, SIGKILL); shmdt((char *)SP); shmdt((char *)Xcurscr); shmctl(shmidSP, IPC_RMID, 0); shmctl(shmid_Xcurscr, IPC_RMID, 0); return ERR; } #endif /* Wait for events */ XtAppMainLoop(app_context); return OK; /* won't get here */ }