text: /* 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 <DECkeysym.h>
  #endif
  
  #ifdef HAVE_SUNKEYSYM_H
  # include <Sunkeysym.h>
  #endif
  
  #ifdef HAVE_XPM_H
  # include <xpm.h>
  #endif
  
  #if defined PDC_XIM
  # include <Xlocale.h>
  #endif
  
  #include <stdlib.h>
  #include <string.h>
  
  #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 =
  {
      "<Key>: XCursesKeyPress() \n" \
      "<KeyUp>: XCursesKeyPress() \n" \
      "<BtnDown>: XCursesButton() \n" \
      "<BtnUp>: XCursesButton() \n" \
      "<BtnMotion>: 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 */
      {
         XEvent event;
  
         for(;;) /* forever */
         {
            XtAppNextEvent(app_context, &event);
            XtDispatchEvent(&event);
         }
      }    
      return OK;          /* won't get here */
  }
  