/* Public Domain Curses */ #include "pdcx11.h" #ifdef HAVE_DECKEYSYM_H # include #endif #ifdef HAVE_SUNKEYSYM_H # include #endif #ifdef HAVE_XPM_H # include #endif #if defined PDC_XIM # include #endif #ifdef HAVE_XF86KEYSYM_H # 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 256 /* 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" #define CURSOR_BLINK_RATE 500 /* Used to be set in xc_app_data.cursorBlinkRate */ 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*/ {';', FALSE, ';', ':', CTL_SEMICOLON, ALT_SEMICOLON }, {'=', FALSE, '=', '+', CTL_EQUAL, ALT_EQUAL }, {',', FALSE, ',', '<', CTL_COMMA, ALT_COMMA }, {'-', FALSE, '-', '_', CTL_MINUS, ALT_MINUS }, {'.', FALSE, '.', '>', CTL_STOP, ALT_STOP }, {'/', FALSE, '/', '?', CTL_FSLASH, ALT_FSLASH }, {'`', FALSE, '`', '~', CTL_BQUOTE, ALT_BQUOTE }, /* 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}, #if defined(XK_ISO_Left_Tab) {XK_ISO_Left_Tab, FALSE, 0x09, KEY_BTAB, CTL_TAB, ALT_TAB}, #endif {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}, {XK_Super_R, FALSE, KEY_COMMAND, KEY_SCOMMAND, KEY_COMMAND, KEY_COMMAND}, {XK_Super_L, FALSE, KEY_COMMAND, KEY_SCOMMAND, KEY_COMMAND, KEY_COMMAND}, #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 #ifdef XF86XK_Back {XF86XK_Back, FALSE, KEY_BROWSER_BACK, KEY_SBROWSER_BACK, KEY_CBROWSER_BACK, KEY_ABROWSER_BACK }, #endif #ifdef XF86XK_Forward {XF86XK_Forward, FALSE, KEY_BROWSER_FWD, KEY_SBROWSER_FWD, KEY_CBROWSER_FWD, KEY_ABROWSER_FWD }, #endif #ifdef XF86XK_Reload {XF86XK_Reload, FALSE, KEY_BROWSER_REF, KEY_SBROWSER_REF, KEY_CBROWSER_REF, KEY_ABROWSER_REF }, #endif #ifdef XF86XK_Search {XF86XK_Search, FALSE, KEY_SEARCH, KEY_SSEARCH, KEY_CSEARCH, KEY_ASEARCH }, #endif #ifdef XF86XK_Favorites {XF86XK_Favorites, FALSE, KEY_FAVORITES, KEY_SFAVORITES, KEY_CFAVORITES, KEY_AFAVORITES }, #endif #ifdef XF86XK_AudioPlay {XF86XK_AudioPlay, FALSE, KEY_PLAY_PAUSE, KEY_SPLAY_PAUSE, KEY_CPLAY_PAUSE, KEY_APLAY_PAUSE }, #endif #ifdef XF86XK_AudioStop {XF86XK_AudioStop, FALSE, KEY_MEDIA_STOP, KEY_SMEDIA_STOP, KEY_CMEDIA_STOP, KEY_AMEDIA_STOP }, #endif #ifdef XF86XK_AudioPrev {XF86XK_AudioPrev, FALSE, KEY_PREV_TRACK, KEY_SPREV_TRACK, KEY_CPREV_TRACK, KEY_APREV_TRACK }, #endif #ifdef XF86XK_AudioNext {XF86XK_AudioNext, FALSE, KEY_NEXT_TRACK, KEY_SNEXT_TRACK, KEY_CNEXT_TRACK, KEY_ANEXT_TRACK }, #endif #ifdef XF86XK_Tools {XF86XK_Tools, FALSE, KEY_MEDIA_SELECT, KEY_SMEDIA_SELECT, KEY_CMEDIA_SELECT, KEY_AMEDIA_SELECT }, #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, bold_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 PDC_blink_state = 1; static int PDC_really_blinking = FALSE; /* see 'pdcsetsc.c' */ #ifndef PDC_XIM static int state_mask[8] = { ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask }; #endif 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 window_entered = TRUE; static char *program_name; /* Macros just for app_resources */ #ifdef PDC_WIDE # define DEFFONT "-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso10646-1" # define DEFBOLDFONT "-misc-fixed-bold-r-normal--13-120-75-75-c-70-iso10646-1" # define DEFITALICFONT "-misc-fixed-medium-o-normal--13-120-75-75-c-70-iso10646-1" #else # define DEFFONT "-misc-fixed-medium-r-normal--13-120-75-75-c-70-iso8859-1" # define DEFBOLDFONT "-misc-fixed-bold-r-normal--13-120-75-75-c-70-iso8859-1" # define DEFITALICFONT "-misc-fixed-medium-o-normal--13-120-75-75-c-70-iso8859-1" #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, DEFITALICFONT), RFONT(boldFont, BoldFont, DEFBOLDFONT), 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 #undef DEFBOLDFONT #undef DEFITALICFONT /* 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), COPT(boldFont), #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]; #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 if( code < 0x10000) { outcode[0] = ((code & 0xf000) >> 12) | 0xe0; outcode[1] = ((code & 0x0fc0) >> 6) | 0x80; outcode[2] = (code & 0x003f) | 0x80; return 3; } else /* SMP: Unicode past 64K */ { outcode[0] = (code >> 18) | 0xf0; outcode[1] = ((code >> 12) & 0x3f) | 0x80; outcode[2] = ((code >> 6) & 0x3f) | 0x80; outcode[3] = ( code & 0x3f) | 0x80; return 4; } } 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 -- minimal validation */ if (key & 0x80) { if ((key & 0xe0) == 0xc0) { if (1 < n) { key = ((key & 0x1f) << 6) | (string[1] & 0x3f); i = 2; } } else if ((key & 0xf0) == 0xe0) /* Unicode from 0x800 to 0xffff */ { if (2 < n) { key = ((key & 0x0f) << 12) | ((string[1] & 0x3f) << 6) | (string[2] & 0x3f); i = 3; } } else if ((key & 0xf8) == 0xf0) /* SMP: Unicode past 64K */ { if (3 < n) { key = ((key & 0x07) << 18) | ((string[1] & 0x3f) << 12) | ((string[2] & 0x3f) << 6) | (string[3] & 0x3f); i = 4; } } else fprintf(stderr, "Invalid UTF8\n"); } 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; } /* This function 'intensifies' a color by shifting it toward white. */ /* It used to average the input color with white. Then it did a */ /* weighted average: 2/3 of the input color, 1/3 white, for a */ /* lower "intensification" level. */ /* Then Mark Hessling suggested that the output level should */ /* remap zero to 85 (= 255 / 3, so one-third intensity), and input */ /* of 192 or greater should be remapped to 255 (full intensity). */ /* Assuming we want a linear response between zero and 192, that */ /* leads to output = 85 + input * (255-85)/192. */ /* This should lead to proper handling of bold text in legacy */ /* apps, where "bold" means "high intensity". */ /* NOTE that this is basically a clone of code in wingui/pdcdisp.c. */ /* The same basic logic should eventually be used in SDL, I think. */ static Pixel intensified_color( Pixel ival) { int rgb, i; Pixel oval = 0; for( i = 0; i < 3; i++, ival >>= 8) { rgb = (int)( ival & 0xff); if( rgb >= 192) rgb = 255; else rgb = 85 + rgb * (255 - 85) / 192; oval |= ((Pixel)rgb << (i * 8)); } return( oval); } /* For use in adjusting colors for A_DIMmed characters. Just */ /* knocks down the intensity of R, G, and B by 1/3. */ static Pixel dimmed_color( Pixel ival) { unsigned i; Pixel oval = 0; for( i = 0; i < 3; i++, ival >>= 8) { unsigned rgb = (unsigned)( ival & 0xff); rgb -= (rgb / 3); oval |= ((Pixel)rgb << (i * 8)); } return( oval); } /* see 'addch.c' for an explanation of how combining chars are handled. */ /* Though note that right now, it doesn't work at all; we'll have to */ /* arrange shared memory or communication between the X process and the */ /* "host" process... to be done later. */ #if defined( CHTYPE_LONG) && CHTYPE_LONG >= 2 #ifdef PDC_WIDE #define USING_COMBINING_CHARACTER_SCHEME int PDC_expand_combined_characters( const cchar_t c, cchar_t *added); /* addch.c */ #endif /* PDC_get_rgb_values(), extract_packed_rgb(), intensified_component(), */ /* intensified_color(), and dimmed_color() each exist in x11/x11.c, */ /* wingui/pdcdisp.c, and sdl2/pdcdisp.c in forms slightly modified for */ /* each platform. But they all look pretty much alike. */ /* PDCurses stores RGBs in fifteen bits, five bits each */ /* for red, green, blue. A Pixel uses eight bits per */ /* channel. Hence the following. */ static Pixel extract_packed_rgb( const chtype color) { const int red = (int)( (color << 3) & 0xf8); const int green = (int)( (color >> 2) & 0xf8); const int blue = (int)( (color >> 7) & 0xf8); return( ((Pixel)red << 16) | ((Pixel)green << 8) | (Pixel)blue); } #endif void PDC_get_rgb_values( const chtype srcp, Pixel *foreground_rgb, Pixel *background_rgb) { bool reverse_colors = ((srcp & A_REVERSE) ? TRUE : FALSE); bool intensify_backgnd = FALSE; #if defined( CHTYPE_LONG) && CHTYPE_LONG >= 2 if( srcp & A_RGB_COLOR) { /* Extract RGB from 30 bits of the color field */ *background_rgb = extract_packed_rgb( srcp >> PDC_COLOR_SHIFT); *foreground_rgb = extract_packed_rgb( srcp >> (PDC_COLOR_SHIFT + 15)); } else #endif { short foreground_index, background_index; PDC_pair_content( PAIR_NUMBER( srcp), &foreground_index, &background_index); *foreground_rgb = colors[foreground_index]; *background_rgb = colors[background_index]; } if( srcp & A_BLINK) { if( !PDC_really_blinking) /* convert 'blinking' to 'bold' */ intensify_backgnd = TRUE; else if( PDC_blink_state) reverse_colors = !reverse_colors; } if( reverse_colors) { const Pixel temp = *foreground_rgb; *foreground_rgb = *background_rgb; *background_rgb = temp; } if( srcp & A_BOLD) *foreground_rgb = intensified_color( *foreground_rgb); if( intensify_backgnd) *background_rgb = intensified_color( *background_rgb); if( srcp & A_DIM) *foreground_rgb = dimmed_color( *foreground_rgb); if( srcp & A_DIM) *background_rgb = dimmed_color( *background_rgb); } /* Output a block of characters with common attributes */ static int _new_packet( const chtype attr, const bool rev, const int len, const int col, const int row, #ifdef PDC_WIDE XChar2b *text) #else char *text) #endif { XRectangle bounds; GC gc; int xpos, ypos; Pixel foreground_rgb, background_rgb; #ifdef PDC_WIDE text[len].byte1 = text[len].byte2 = 0; #else text[len] = '\0'; #endif /* Determine which GC to use - normal or italic */ if ( attr & A_ITALIC ) { gc = italic_gc; } else if ( attr & A_BOLD ) { gc = bold_gc; } else { gc = normal_gc; } /* Draw it */ PDC_get_rgb_values( attr, &foreground_rgb, &background_rgb); if( rev) { const Pixel swap_val = foreground_rgb; foreground_rgb = background_rgb; background_rgb = swap_val; } XSetForeground(XCURSESDISPLAY, gc, foreground_rgb); XSetBackground(XCURSESDISPLAY, gc, background_rgb); _make_xy(col, row, &xpos, &ypos); bounds.x = xpos; bounds.y = ypos - font_ascent; bounds.width = font_width * len; bounds.height = font_height; XSetClipRectangles(XCURSESDISPLAY, gc, 0, 0, &bounds, 1, Unsorted); #ifdef PDC_WIDE XDrawImageString16( #else XDrawImageString( #endif XCURSESDISPLAY, XCURSESWIN, gc, xpos, ypos, text, len); /* Underline, etc. */ if (attr & (A_LEFTLINE|A_RIGHTLINE|A_UNDERLINE|A_OVERLINE|A_STRIKEOUT)) { 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 + xc_app_data.normalFont->descent-1, xpos + font_width * len, ypos + xc_app_data.normalFont->descent-1); /* xpos, ypos + 1, xpos + font_width * len, ypos + 1);*/ if (attr & A_OVERLINE) XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc, xpos, ypos - font_ascent, xpos + font_width * len, ypos - font_ascent); if (attr & A_STRIKEOUT) XDrawLine(XCURSESDISPLAY, XCURSESWIN, gc, xpos, ypos + (font_descent - font_ascent) / 2, xpos + font_width * len, ypos + (font_descent - font_ascent) / 2); if (attr & A_LEFTLINE) /* LEFT */ for (k = 0; k < len; k++) { int x = xpos + font_width * k; 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, foreground_rgb, background_rgb, 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 || i > 100) { if (_new_packet(old_attr, highlight, i, col, row, text) == ERR) return ERR; old_attr = attr; col += i; i = 0; } #ifdef PDC_WIDE curr &= A_CHARTEXT; if( curr <= 0xffff) /* BMP Unicode */ { if( !curr) curr = ' '; text[i].byte1 = (curr & 0xff00) >> 8; text[i++].byte2 = curr & 0x00ff; } else /* SMP & combining chars */ { const chtype MAX_UNICODE = 0x110000; if( curr < MAX_UNICODE) /* Supplemental Multilingual Plane */ { /* (SMP); store w/surrogates */ const unsigned short part1 = (unsigned short) (0xd800 | ((curr - 0x10000) >> 10)); const unsigned short part2 = (unsigned short) (0xdc00 | (curr & 0x3ff)); text[i].byte1 = part1 >> 8; text[i++].byte2 = part1 & 0xff; text[i].byte1 = part2 >> 8; text[i++].byte2 = part2 & 0xff; } #ifdef USING_COMBINING_CHARACTER_SCHEME else if( curr > MAX_UNICODE) { #ifdef GOT_COMBINING_CHARS_IN_X cchar_t added[10]; int n_combined = 0; while( (curr = PDC_expand_combined_characters( curr, &added[n_combined])) > MAX_UNICODE) n_combined++; while( n_combined >= 0) { text[i].byte1 = added[n_combined] >> 8; text[i++].byte2 = added[n_combined] & 0xff; n_combined--; } #else text[i].byte1 = 0; text[i++].byte2 = '?'; #endif } #endif } #else /* non-wide case */ 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); #ifdef WHY_IS_THIS_HERE XSetForeground(XCURSESDISPLAY, *gc, colors[fore]); XSetBackground(XCURSESDISPLAY, *gc, colors[back]); #endif } #define RGB( R, G, B) (((unsigned long)(R)) << 16 \ | ((unsigned long)(G) << 8) | (unsigned long)(B)) static void _initialize_colors(void) { int i, r, g, b; 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; i = 16; /* 256-color xterm extended palette: 216 colors in a 6x6x6 color cube, plus 24 (not 50) shades of gray */ for( r = 0; r < 6; r++) for( g = 0; g < 6; g++) for( b = 0; b < 6; b++) colors[i++] = RGB( r ? r * 40 + 55 : 0, g ? g * 40 + 55 : 0, b ? b * 40 + 55 : 0); for( i = 0; i < 24; i++) colors[i + 232] = RGB( i * 10 + 8, i * 10 + 8, i * 10 + 8); 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); if( row == SP->cursrow && SP->visibility) _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( row == SP->cursrow && SP->visibility) _redraw_cursor(); } } _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]) { /* if we specified an exit key return it, otherwise exit the process */ if ( SP->exit_key ) _send_key_to_curses(SP->exit_key, NULL, TRUE); else _exit_process(0, SIGKILL, ""); } } } static int override_cursor = -1; #define CURSOR_UNBLINKING_RECTANGLE 0x303 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; static XEvent prev_event; static int repeat_count = 0; XC_LOG(("XCursesKeyPress() - called\n")); /* Handle modifier keys first; ignore other KeyReleases */ if( event->type == KeyPress && prev_event.type == KeyRelease && event->xkey.time == prev_event.xkey.time && event->xkey.keycode == prev_event.xkey.keycode) { repeat_count++; } else if( event->type == KeyPress) repeat_count = 0; prev_event = *event; 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) { /* Change the shape of the cursor to an outline rectangle to indicate we are in "compose" status */ override_cursor = CURSOR_UNBLINKING_RECTANGLE; 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; override_cursor = -1; 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; override_cursor = -1; break; } compose_state = STATE_CHAR; return; case STATE_CHAR: if (IsModifierKey(keysym)) return; if (event->xkey.state & compose_mask) { compose_state = STATE_NORMAL; override_cursor = -1; 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; override_cursor = -1; break; } _send_key_to_curses(compose_keys[compose_index][char_idx], NULL, FALSE); compose_state = STATE_NORMAL; compose_index = 0; override_cursor = -1; 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)); /* 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; if( repeat_count) modifier |= PDC_KEY_MODIFIER_REPEAT; 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 (%lx) pressed - %lx Mod: %x\n", XCLOGMSG, XKeysymToString(keysym), keysym, key, event->xkey.state)); /* Handle ALT letters and numbers */ /* enable Alt key without numlock on */ 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) { static long unicode_value = -1L; if( key == 21 && modifier == /* Ctrl-Shift-U hit: Unicode entry */ (PDC_KEY_MODIFIER_SHIFT | PDC_KEY_MODIFIER_CONTROL)) unicode_value = 0L; if( unicode_value >= 0L) { int offset = 0; override_cursor = CURSOR_UNBLINKING_RECTANGLE; if( key >= '0' && key <= '9') offset = '0'; if( key >= 'a' && key <= 'f') offset = 'a' - 10; if( key >= 'A' && key <= 'F') offset = 'A' - 10; if( offset) { unicode_value <<= 4; unicode_value |= (long)( key - offset); } if( key == 13 || key == PADENTER) { key = unicode_value; unicode_value = -1L; modifier = 0; key_code = FALSE; override_cursor = -1; } else /* still in unicode entry mode */ return; } if (SP->save_key_modifiers) 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")); if( mouse_selection) { _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; } #define CURSOR_INVISIBLE 0 #define CURSOR_NORMAL 1 #define CURSOR_BLOCK 2 #define CURSOR_RECTANGLE 3 #define CURSOR_VLINE 4 #define CURSOR_HALF_BLOCK 5 static void _display_cursor(int old_row, int old_x, int new_row, int new_x) { int xpos, ypos, i, cursor_to_show; 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 */ /* use lower 8 bits for 1/2 cycle (or if not in window) */ if( PDC_blink_state || !window_entered) cursor_to_show = (SP->visibility & 0xff); else /* ...& upper 8 bits on other 1/2 cycle */ cursor_to_show = (SP->visibility >> 8); if( override_cursor >= 0) cursor_to_show = (PDC_blink_state ? override_cursor & 0xff : override_cursor >> 8); if( !cursor_to_show) 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); XSetForeground(XCURSESDISPLAY, rect_cursor_gc, colors[back]); switch( cursor_to_show) { case CURSOR_VLINE: for (i = 1; i <= 2; 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); break; case CURSOR_NORMAL: case CURSOR_HALF_BLOCK: { int n_lines = xc_app_data.normalFont->descent; ypos += n_lines - 1; if( cursor_to_show == CURSOR_HALF_BLOCK) n_lines += xc_app_data.normalFont->ascent / 2; else n_lines += 2; for( i = 0; i < n_lines; i++, ypos--) XDrawLine(XCURSESDISPLAY, XCURSESWIN, rect_cursor_gc, xpos, ypos, xpos + font_width, ypos); break; } case CURSOR_RECTANGLE: XDrawRectangle(XCURSESDISPLAY, XCURSESWIN, rect_cursor_gc, xpos + 1, ypos - font_height + xc_app_data.normalFont->descent + 1, font_width - 2, font_height - 2); break; case CURSOR_BLOCK: default: { /* 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); } break; } 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"); } } #ifdef A_OVERLINE #define A_ALL_LINES (A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE | A_OVERLINE | A_STRIKEOUT) #else #define A_ALL_LINES (A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE) #endif /* Note that the logic used to avoid unnecessary drawing is heavily borrowed from the HandleTimer function in wingui/pdcscrn.c. The comments there may be helpful. */ static void _blink_cursor(XtPointer unused, XtIntervalId *id) { XC_LOG(("_blink_cursor() - called:\n")); int i; static int previously_really_blinking = 0; static int prev_line_color = -1; chtype attr_to_seek = 0; if( prev_line_color != SP->line_color) attr_to_seek = A_ALL_LINES; if( PDC_really_blinking || previously_really_blinking) attr_to_seek |= A_BLINK; prev_line_color = SP->line_color; previously_really_blinking = PDC_really_blinking; PDC_blink_state ^= 1; if( attr_to_seek) for( i = 0; i < SP->lines; i++) { const chtype *line; int j = 0, n_chars; XC_get_line_lock( i); line = (const chtype *)(Xcurscr + XCURSCR_Y_OFF( i)); /* skip over starting text that isn't blinking: */ while( j < SP->cols && !(*line & attr_to_seek)) { j++; line++; } n_chars = SP->cols - j; /* then skip over text at the end that's not blinking: */ while( n_chars && !(line[n_chars - 1] & attr_to_seek)) n_chars--; if( n_chars) _display_text( line, i, j, n_chars, 0); XC_release_line_lock( i); } _redraw_cursor(); XtAppAddTimeOut(app_context, CURSOR_BLINK_RATE, _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, and button 6 and 7, which are normally mapped to the wheel mouse scroll left and right */ if (button_no >= 4 && button_no <= 7) { /* Send the KEY_MOUSE to curses program */ memset(&Mouse_status, 0, sizeof(Mouse_status)); switch(button_no) { case 4: Mouse_status.changes = PDC_MOUSE_WHEEL_UP; break; case 5: Mouse_status.changes = PDC_MOUSE_WHEEL_DOWN; break; case 6: Mouse_status.changes = PDC_MOUSE_WHEEL_LEFT; break; case 7: Mouse_status.changes = PDC_MOUSE_WHEEL_RIGHT; } 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) { const short curr_status = BUTTON_STATUS( button_no); MOUSE_X_POS = save_mouse_status.x; MOUSE_Y_POS = save_mouse_status.y; if( curr_status == BUTTON_DOUBLE_CLICKED || curr_status == BUTTON_TRIPLE_CLICKED) BUTTON_STATUS(button_no) = BUTTON_TRIPLE_CLICKED; else 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, bold_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; _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)) { 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_BLINK_ON: PDC_really_blinking = TRUE; break; case CURSES_BLINK_OFF: PDC_really_blinking = FALSE; 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; /* Only redraw the cursor if it's actually moved. */ /* Otherwise, it'll get refreshed at the next cycle. */ if( old_row != new_row || old_x != new_x) _display_cursor(old_row, old_x, new_row, new_x); break; case CURSES_DISPLAY_CURSOR: if(PDC_blink_state) { XC_LOG(("CURSES_DISPLAY_CURSOR received from child. Vis. now: 1\n")); } else { XC_LOG(("CURSES_DISPLAY_CURSOR received from child. Vis. now: 0\n")); } 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}; char override_text[2][10]; char **new_argv = NULL; extern bool sb_started; int italic_font_valid, bold_font_valid; int italic_font_width, italic_font_height; int bold_font_width, bold_font_height; XColor pointerforecolor, pointerbackcolor; XrmValue rmfrom, rmto; int i; int minwidth, minheight; XC_LOG(("XCursesSetupX called\n")); if (!argv) { argv = myargv; argc = 1; } program_name = argv[0]; if( XCursesLINES != 24 || XCursesCOLS != 80) { /* a call to resize() was made before initscr() */ int pass; new_argv = (char **)calloc( argc + 5, sizeof( char *)); for( i = 0; i < argc; i++) new_argv[i] = argv[i]; argv = new_argv; for( pass = 0; pass < 2; pass++) { const char *override = (pass ? "-cols" : "-lines"); i = 0; while( i < argc && strcmp( argv[i], override)) i++; argv[i] = (char *)override; argv[i + 1] = override_text[pass]; sprintf( override_text[pass], "%d", (pass ? XCursesCOLS : XCursesLINES)); if( i == argc) /* this is new (usual case) */ argc += 2; } } /* 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); if( new_argv) free( new_argv); 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 */ /* italic fonts can have negative lbearings! */ if ( xc_app_data.italicFont->min_bounds.lbearing < 0 ) italic_font_width = xc_app_data.italicFont->max_bounds.rbearing + xc_app_data.italicFont->min_bounds.lbearing; else italic_font_width = xc_app_data.italicFont->max_bounds.rbearing - xc_app_data.italicFont->min_bounds.lbearing; italic_font_height = xc_app_data.italicFont->max_bounds.ascent + xc_app_data.italicFont->max_bounds.descent; italic_font_valid = (font_width == italic_font_width) && (font_height == italic_font_height); /* Check that the bold font and normal fonts are the same size */ if ( xc_app_data.boldFont->min_bounds.lbearing < 0 ) bold_font_width = xc_app_data.boldFont->max_bounds.rbearing + xc_app_data.boldFont->min_bounds.lbearing; else bold_font_width = xc_app_data.boldFont->max_bounds.rbearing - xc_app_data.boldFont->min_bounds.lbearing; bold_font_height = xc_app_data.boldFont->max_bounds.ascent + xc_app_data.boldFont->max_bounds.descent; bold_font_valid = (font_width == bold_font_width) && (font_height == bold_font_height); /* 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(); /* 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); /* start the Timeout event for blinking the cursor (and blinking text) */ XtAppAddTimeOut(app_context, CURSOR_BLINK_RATE, _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(&bold_gc, bold_font_valid ? xc_app_data.boldFont : 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 */ { XEvent event; for (;;) /* forever */ { XtAppNextEvent(app_context, &event); XtDispatchEvent(&event); } } return OK; /* won't get here */ }