This is guix11.c in view mode; [Download] [Up]
/* guix11.c */ /* Copyright 1995 by Steve Kirkendall */ char id_guix11[] = "$Id: guix11.c,v 2.87 1996/10/01 19:45:58 steve Exp $"; #include "elvis.h" #ifdef GUI_X11 #include <sys/types.h> #include <sys/time.h> #ifdef NEED_SELECT_H # include <sys/select.h> #endif #include <unistd.h> #include <fcntl.h> #include <setjmp.h> #include <signal.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #define XK_MISCELLANY #include <X11/keysymdef.h> #include <X11/Xatom.h> #include "lib/elvis.xbm" /* default values of options */ #define DEFAULT_TOOLBAR True #define DEFAULT_CONTROLFONT "variable" #define DEFAULT_NORMALFONT "fixed" #define DEFAULT_CURSORCOLOR "red" #define DEFAULT_OWNCOLOR "red" #define DEFAULT_SCROLLBARFG "gray75" #define DEFAULT_SCROLLBARBG "gray60" #define DEFAULT_GEOMETRY "80x34" #define DEFAULT_XCOLUMNS 80 #define DEFAULT_XROWS 34 #define DEFAULT_SCROLLBARWIDTH 14 #define DEFAULT_SCROLLBARTIME 4 #define DEFAULT_DBLCLICKTIME 3 #define DEFAULT_ICON True #define DEFAULT_BLINKTIME 3 #define DEFAULT_STOPSHELL "xterm &" /* scroll bar appearance */ #define SB_GAP 2 /* blank pixels between buttons */ #define SB_BEVEL 2 /* width of 3-D shading for buttons */ #define TB_HEIGHT (o_toolbar ? (2 + 2 * SB_BEVEL + loadedcontrol->height) : 0) /* Graphic characters in a typical LATIN-1 font */ #define GCH '\022' #define GCV '\031' #define GC1 '\016' #define GC2 '\027' #define GC3 '\013' #define GC4 '\025' #define GC5 '\017' #define GC6 '\026' #define GC7 '\015' #define GC8 '\030' #define GC9 '\014' typedef enum { SB_REDRAW, SB_NORMAL, SB_BLANK, SB_STOP } SBSTATE; typedef struct loadedfont_s { struct loadedfont_s *next; /* next font in linked list */ XFontStruct *fontinfo; /* X font structure */ char *name; /* name of the font */ int height; /* height of font (ascent + descent) */ int links; /* number of windows using this font */ } LOADEDFONT; typedef struct loadedcolor_s { struct loadedcolor_s *next; /* next color in linked list */ unsigned long pixel; /* the color code */ CHAR *name; /* name of the color */ int links; /* number of uses for this color */ } LOADEDCOLOR; typedef struct x11win_s { struct x11win_s *next; /* pointer to some other window */ Window window; /* top-level X window */ Window textw; /* subwindow for text & scrollbar */ Window toolw; /* subwindow for toolbar */ Pixmap undercurs; /* image of character under cursor */ GC gc; /* graphic context for this window */ unsigned long fg, bg; /* current foreground & background */ unsigned long bgnormal; /* normal background color */ unsigned long fgnormal; /* foreground color for normal text */ unsigned long fgfixed; /* foreground color of fixed text */ unsigned long fgbold; /* foreground color for bold text */ unsigned long fgemph; /* foreground color of emphasized text */ unsigned long fgitalic; /* foreground color for italic text */ unsigned long fgundln; /* foreground color for underlined text */ unsigned long fgcursor; /* foreground color for cursor */ unsigned long owncursor; /* foreground for cursor when elvis owns selection */ unsigned long bgscroll; /* background color of scrollbar */ unsigned long fgscroll; /* foreground color of scrollbar */ BOOLEAN grexpose; /* are graphic exposures allow now? */ char *title; /* name of the window */ int cursx, cursy; /* cursor position */ unsigned cellw, cellh; /* size of a character cell, in pixels */ int cellbase; /* baseline of glyphs, relative to top of cell */ unsigned rows, columns; /* size of the window, in cells */ ELVCURSOR cursor; /* current state of cursor */ ELVCURSOR nextcursor; /* next state of cursor */ BOOLEAN ismapped; /* is window visible? */ ELVISSTATE state; /* command state of window */ int sbheight; /* total height of scrollbar */ int sbtop; /* top of thumb */ int sbbottom; /* bottom of thumb */ SBSTATE sbstate; /* scrollbar state */ char toolstate[100]; /* states of buttons */ int x, y; /* position of window */ BOOLEAN nowarp; /* don't warp pointer into this window */ } X11WIN; /* This stores the definition of toolbar buttons */ typedef struct tool_s { struct tool_s *next; /* another toolbar button */ char *label; /* button legend */ char *excmd; /* command to execute */ CHAR *when; /* condition when button works */ CHAR *in; /* condition when button drawn as "in" */ int x; /* button's position within toolbar */ int width; /* button's width */ int textx, texty; /* where to start drawing text */ int id; /* index into xw->toolstate[] */ BOOLEAN safer; /* run with "safer" flag? */ } TOOL; #if USE_PROTOTYPES static BOOLEAN clipopen(BOOLEAN forwrite); static BOOLEAN clrtoeol(GUIWIN *gw); static BOOLEAN color(GUIWIN *gw, _char_ font, CHAR *fg, CHAR *bg); static BOOLEAN creategw(char *name,char * attributes); static BOOLEAN scroll(GUIWIN *gw, int qty, BOOLEAN notlast); static BOOLEAN shift(GUIWIN *gw, int qty, int rows); static BOOLEAN wpoll(BOOLEAN reset); static LOADEDFONT *loadfont(char *name); static RESULT stop(BOOLEAN alwaysfork); static int clipread(CHAR *text, int len); static int clipwrite(CHAR *text, int len); static int init(int argc, char **argv); static int keylabel(CHAR *given, int givenlen, CHAR **label, CHAR **rawin); static int test(void); static BOOLEAN clientaction(int argc, char **argv); static unsigned long loadcolor(CHAR *name, unsigned long def); static void beep(GUIWIN *gw); static void clipclose(void); static void destroygw(GUIWIN *gw, BOOLEAN force); static void draw(GUIWIN *gw, _char_ font, CHAR *text, int len); static void drawcursor(X11WIN *xw); static void erasecursor(X11WIN *xw); static void loop(void); static void moveto(GUIWIN *gw, int column, int row); static void retitle(GUIWIN *gw, char *name); static void scrollbar(GUIWIN *gw, long top, long bottom, long total); static int catchErrors(Display *disp, XErrorEvent *err); static void term(void); static void unloadcolor(unsigned long pixel); static void unloadfont(LOADEDFONT *font); static void usage(void); static void bevelrect(X11WIN *xw, Window win, int x, int y, unsigned w, unsigned h, BOOLEAN in); #endif static BOOLEAN focusgw P_((GUIWIN *gw)); static void setsbstate P_((X11WIN *xw, SBSTATE newstate)); static void draw1tool P_((X11WIN *xw, TOOL *tool, _char_ newstate)); static void drawtoolbar P_((X11WIN *xw, BOOLEAN fromscratch)); static int ignoreErrors P_((Display *disp, XErrorEvent *err)); static void flush P_((void)); static BOOLEAN guicmd P_((GUIWIN *gw, char *extra)); static jmp_buf xerror_handler; /* used to recover from protocol errors */ static Display *display; /* X11 display */ static int screen; /* screen number */ static Window root; /* root window */ static int rootheight, rootwidth; /* size of root window */ static int depth; /* bits per pixel */ static X11WIN *winlist; /* list of windows */ static TOOL *toollist; /* list of toolbar buttons */ static Colormap colormap; /* colormap shared by elvis windows */ static Pixmap elvis_icon; /* elvis' window icon */ static char *argv0; /* name of program */ static LOADEDFONT *fonts; /* list of allocated fonts */ static LOADEDCOLOR*colors; /* list of allocated colors */ static LOADEDFONT *defaultnormal; /* normal font */ static LOADEDFONT *defaultbold; /* bold font, or NULL to fake it */ static LOADEDFONT *defaultitalic; /* italic font, or NULL to fake it */ static LOADEDFONT *loadedcontrol; /* toolbar font */ static Atom wm_protocols; /* value for WM_PROTOCOLS atom */ static Atom wm_delete_window; /* value for WM_DELETE_WINDOW atom */ static Atom elvis_cutbuffer; /* value for ELVIS_CUTBUFFER atom */ static Atom elvis_server; /* value for ELVIS_SERVER atom */ static unsigned long white, black; /* default color values */ static Time now; /* timestamp of current event */ static Time firstclick; /* timestamp of previous click */ static BOOLEAN ownselection; /* does elvis own the X11 selection? */ static char *clipbuf; /* pointer to malloc'ed buffer of chars to/from X */ static long clipsize; /* total number of bytes in clipbuf */ static long clipused; /* if reading, total number of bytes read previously */ static BOOLEAN clipwriting; /* True if cutting to X, False if pasting from X */ static Window fromwin; /* window which invoked elvis */ static X11WIN *hasfocus; /* window with keyboard focus, or NULL */ #define WIN2XW(win,xw) for ((xw) = winlist;\ (xw) && (xw)->window != (win) && (xw)->textw != (win) && (xw)->toolw != (win);\ (xw) = (xw)->next)\ {\ }\ if (!(xw)) break; /* This table lists the keys which are mapped automatically */ struct { char *label; KeySym sym; char *cooked; MAPFLAGS flags; } keys[] = { { "<Up>", XK_Up, "k", MAP_ALL }, { "<Down>", XK_Down, "j", MAP_ALL }, { "<Left>", XK_Left, "h", MAP_ALL }, { "<Right>", XK_Right, "l", MAP_ALL }, { "<Prior>", XK_Prior, "\002", MAP_ALL_VISUAL }, { "<Next>", XK_Next, "\006", MAP_ALL_VISUAL }, { "<Home>", XK_Home, "^", MAP_ALL }, { "<Begin>", XK_Begin, "^", MAP_ALL }, { "<End>", XK_End, "$", MAP_ALL }, { "<Insert>", XK_Insert, "i", MAP_ALL }, { "<Delete>", XK_Delete, "x", MAP_ALL }, { "<Undo>", XK_Undo, "u", MAP_ALL_VISUAL }, { "<Help>", XK_Help, ":help\r",MAP_ALL_VISUAL }, { "<Multi_key>",XK_Multi_key, "\013", MAP_INPUT|MAP_OPEN } }; static OPTDESC x11desc[] = { {"normalfont", "xfn", optsstring, optisstring }, {"boldfont", "xfb", optsstring, optisstring }, {"italicfont", "xfi", optsstring, optisstring }, {"controlfont", "xfc", optsstring, optisstring }, {"toolbar", "xtb", NULL, NULL }, {"scrollbarwidth","xsw",optnstring, optisnumber, "5:40"}, {"scrollbartime", "xst",optnstring, optisnumber, "0:20"}, {"dblclicktime", "xdct",optnstring, optisnumber, "1:10"}, {"blinktime", "xbt", optnstring, optisnumber, "0:10"}, {"xrows", "xlines", optnstring, optisnumber, "3:200"}, {"xcolumns", "xcols", optnstring, optisnumber, "30:200"}, {"firstx", "xpos", optnstring, optisnumber, "-2000:2000"}, {"firsty", "ypos", optnstring, optisnumber, "-2000:2000"}, {"icon", "icon", NULL, NULL }, {"iconic", "iconic", NULL, NULL }, {"stopshell", "ssh", optsstring, optisstring }, {"autoiconify", "aic", NULL, NULL }, {"altkey", "metakey", opt1string, optisoneof, "control-O setbit ignore"}, {"stagger", "step", optnstring, optisnumber, "0:200"}, {"warpback", "wb", NULL, NULL }, {"warpto", "wt", opt1string, optisoneof, "don't scrollbar origin corners"}, {"focusnew", "fn", NULL, NULL } }; static struct { OPTVAL normalfont, boldfont, italicfont, controlfont, toolbar, scrollbarwidth, scrollbartime, dblclicktime, blinktime, xrows, xcolumns, firstx, firsty, icon, iconic, stopshell, autoiconify, altkey, stagger, warpback, warpto, focusnew; } x11opt; #define o_normalfont x11opt.normalfont.value.string #define o_boldfont x11opt.boldfont.value.string #define o_italicfont x11opt.italicfont.value.string #define o_controlfont x11opt.controlfont.value.string #define o_toolbar x11opt.toolbar.value.boolean #define o_scrollbarwidth x11opt.scrollbarwidth.value.number #define o_scrollbartime x11opt.scrollbartime.value.number #define o_dblclicktime x11opt.dblclicktime.value.number #define o_blinktime x11opt.blinktime.value.number #define o_xrows x11opt.xrows.value.number #define o_xcolumns x11opt.xcolumns.value.number #define o_firstx x11opt.firstx.value.number #define o_firsty x11opt.firsty.value.number #define o_icon x11opt.icon.value.boolean #define o_iconic x11opt.iconic.value.boolean #define o_stopshell x11opt.stopshell.value.string #define o_autoiconify x11opt.autoiconify.value.boolean #define o_altkey x11opt.altkey.value.character #define o_stagger x11opt.stagger.value.number #define o_warpback x11opt.warpback.value.boolean #define o_warpto x11opt.warpto.value.character #define o_focusnew x11opt.focusnew.value.boolean /* The following store names of colors */ static CHAR background[50]; static CHAR foreground[50]; static CHAR boldcolor[50]; static CHAR emphcolor[50]; static CHAR fixedcolor[50]; static CHAR italiccolor[50]; static CHAR underlinecolor[50]; static CHAR cursorcolor[50]; static CHAR owncolor[50]; static CHAR scrollbarfg[50]; static CHAR scrollbarbg[50]; /* This function loads a font if it isn't already loaded. */ static LOADEDFONT *loadfont(name) char *name; /* name of the font */ { LOADEDFONT *font; XFontStruct *info; /* see if it is already loaded */ for (font = fonts; font && strcmp(name, font->name); font = font->next) { } /* if already loaded, then just increment its count */ if (font) { font->links++; return font; } /* else load the font into a new stucture */ info = XLoadQueryFont(display, name); if (!info) { msg(MSG_ERROR, "[s]can't load font $1", name); return NULL; } font = (LOADEDFONT *)safealloc(1, sizeof(LOADEDFONT)); font->fontinfo = info; font->name = safedup(name); font->height = info->descent + info->ascent; font->links = 1; /* link the new structure into the list, and return it */ font->next = fonts; fonts = font; return font; } /* This function unloads a font. If no window is using the font after that, * then the storage space is freed. */ static void unloadfont(font) LOADEDFONT *font; /* font to be freed */ { LOADEDFONT *scan; assert(fonts != NULL); /* If no font was given, do nothing */ if (!font) { return; } /* decrement the count. If other windows are still using this * font, then that's all we should for now. */ if (--font->links > 0) { return; } /* delete the font from the list of fonts */ if (fonts == font) { fonts = font->next; } else { for (scan = fonts; scan->next != fonts; scan = scan->next) { assert(scan->next); } scan->next = font->next; } /* free its resources */ safefree(font->name); XFreeFont(display, font->fontinfo); safefree(font); } /* This function allocates a color */ static unsigned long loadcolor(name, def) CHAR *name; /* name of color to load */ unsigned long def; /* default color, if can't load named color */ { XColor exact, color; LOADEDCOLOR *scan; /* if no name, then just use the default */ if (!name || !*name) goto UseDefault; /* was this color name used before? */ for (scan = colors; scan; scan = scan->next) { if (!CHARcmp(name, scan->name)) { scan->links++; return scan->pixel; } } /* try to load the color */ if (!XAllocNamedColor(display, colormap, tochar8(name), &exact, &color)) { msg(MSG_WARNING, "[S]could not allocate color $1", name); goto UseDefault; } /* was it rounded to a previous color? */ for (scan = colors; scan; scan = scan->next) { if (scan->pixel == color.pixel) { scan->links++; return scan->pixel; } } /* new color -- remember it */ scan = safealloc(1, sizeof(*scan)); scan->name = CHARdup(name); scan->pixel = color.pixel; scan->links = 1; scan->next = colors; colors = scan; return color.pixel; UseDefault: /* look for the default pixel code in the list */ for (scan = colors; scan; scan = scan->next) { if (scan->pixel == def) { scan->links++; return def; } } /* not in list -- add it */ scan = safealloc(1, sizeof *scan); scan->name = toCHAR(def == black ? "black" : "white"); scan->pixel = def; scan->links = 1; scan->next = colors; colors = scan; return def; } static void unloadcolor(pixel) unsigned long pixel; /* a color to free */ { LOADEDCOLOR *scan, *lag; /* never load/unload the default white & black values */ if (pixel == white || pixel == black) { return; } /* find the color */ for (lag = NULL, scan = colors; scan->pixel != pixel; lag = scan, scan = scan->next) { assert(scan->next); } /* decrement the link counter. If other windows are using it, * then leave it. */ if (--scan->links > 0) { return; } /* free it */ XFreeColors(display, colormap, &pixel, 1, 0); safefree(scan->name); if (lag) lag->next = scan->next; else colors = scan->next; safefree(scan); } /* Test whether this GUI is available in this environment. * Returns 0 if the GUI is unavailable, or 1 if available. * This should not have any visible side-effects. If the * GUI can't be tested without side-effects, then this * function should return 2 to indicate "maybe available". */ static int test() { char *tmp; /* I've had some problems in which DISPLAY gets set to the valid name * of a functioning X server, even though I'm personally not the person * who'se using that X terminal. To prevent my elvis window from * appearing on his X screen, I'll assume x11 is unavailable if the * TERM environment variable is set, and is initialized to anything * other than "xterm". */ tmp = getenv("TERM"); if (tmp && strcmp(tmp, "xterm")) return 0; /* Try to contact the server. If we can contact it, great! */ if (getenv("DISPLAY") && (display = XOpenDisplay("")) != (Display *)0) return 1; return 0; } static int catchErrors(disp, err) Display *disp; XErrorEvent *err; { longjmp(xerror_handler, 1); } /* This function transmits the filenames to another elvis processes */ static BOOLEAN clientaction(argc, argv) int argc; /* number of command-line arguments */ char **argv; /* values of command-line arguments */ { Window srvwin; /* a window of the server elvis */ Atom type; unsigned long ul, dummy; int format; unsigned char *data; char prop[6000]; char *cwd; int i; char *tagname = NULL; char *excommand = NULL; /* try to find the server window. If we can't find it, then we * can't do the client thing. */ XGetWindowProperty(display, root, elvis_server, 0L, 1L, False, XA_WINDOW, &type, &format, &ul, &dummy, &data); if (ul != 1 || type != XA_WINDOW || format != 32) return False; srvwin = *(Window *)data; XFree(data); /* stuff arguments into a property, as ex command strings */ cwd = dircwd(); memset(prop, 0, QTY(prop)); for (i = 1; i < argc && prop[QTY(prop) - 300] == '\0'; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'c': if (argv[i][2]) excommand = &argv[i][2]; else if (i + 1 < argc) excommand = argv[++i]; break; case 't': if (argv[i][2]) tagname = &argv[i][2]; else if (i + 1 < argc) tagname = argv[++i]; break; default: fprintf(stderr, "%s: %s not supported with -client\n", argv[0], argv[i]); return -1; } } else if (argv[i][0] == '+') { excommand = (argv[i][1] ? &argv[i][1] : "$"); } else { strcat(prop, "split "); if (argv[i][0] == '/' /* assumes unix-style filenames */ || (argv[i][0] == '~' && argv[i][1] == '/')) strcat(prop, argv[i]); else strcat(prop, dirpath(cwd, argv[i])); strcat(prop, "\n"); } } if (excommand) { strcat(prop, excommand); strcat(prop, "\n"); } if (tagname) { strcat(prop, "stag "); strcat(prop, tagname); strcat(prop, "\n"); } /* if the following causes an error, then don't do the client thing */ if (setjmp(xerror_handler)) { /* we'd better reinitialize the connection */ XCloseDisplay(display); display = XOpenDisplay(""); screen = DefaultScreen(display); root = RootWindow(display, screen); return False; } XSetErrorHandler(catchErrors); /* hang the property on the server elvis window */ XChangeProperty(display, srvwin, elvis_server, XA_STRING, 8, PropModeAppend, (unsigned char *)prop, strlen(prop)); XFlush(display); /* shut down the client connection to the X11 display, and cause * an eventual exit. */ XCloseDisplay(display); return True; } /* Start the GUI. * * argc and argv are the command line arguments. The GUI * may scan the arguments for GUI-specific options; if it * finds any, then they should be deleted from the argv list. * The resulting value of argc should be returned normally. * If the GUI couldn't initialize itself, it should emit an * error message and return -1. * * Other than "name" and "test", no other fields of the GUI * structure are accessed before this function has been called. */ static int init(argc, argv) int argc; /* number of command-line arguments */ char **argv; /* values of command-line arguments */ { int i, j, ndel; int x, y, flags; int h, w; char raw[50]; BOOLEAN client = False; BOOLEAN mustfork = False; char *geomstr = NULL; /* initialization */ if (!display) display = XOpenDisplay(""); if (!display) msg(MSG_FATAL, "could not contact X server"); screen = DefaultScreen(display); root = RootWindow(display, screen); /* parse the command-line flags */ argv0 = argv[0]; for (i = 1, ndel = 0; i < argc; i = (ndel==0 ? i+1 : i), ndel = 0) { if (!strcmp(argv[i], "-font") || !strcmp(argv[i], "-fn")) { optpreset(o_normalfont, toCHAR(argv[i + 1]), OPT_LOCK); ndel = 2; } else if (!strcmp(argv[i], "-fb")) { optpreset(o_boldfont, toCHAR(argv[i + 1]), OPT_LOCK); ndel = 2; } else if (!strcmp(argv[i], "-fi")) { optpreset(o_italicfont, toCHAR(argv[i + 1]), OPT_LOCK); ndel = 2; } else if (!strcmp(argv[i], "-courier")) { if (i + 1 >= argc) { msg(MSG_ERROR, "-courier requires a font size"); return -1; } sprintf(raw, "*-courier-medium-r-*-%s-*", argv[i + 1]); optpreset(o_normalfont, CHARdup(toCHAR(raw)), OPT_LOCK|OPT_FREE); sprintf(raw, "*-courier-bold-r-*-%s-*", argv[i + 1]); optpreset(o_boldfont, CHARdup(toCHAR(raw)), OPT_LOCK|OPT_FREE); sprintf(raw, "*-courier-medium-o-*-%s-*", argv[i + 1]); optpreset(o_italicfont, CHARdup(toCHAR(raw)), OPT_LOCK|OPT_FREE); ndel = 2; } else if (!strcmp(argv[i], "-fc")) { optpreset(o_controlfont, toCHAR(argv[i + 1]), OPT_LOCK); ndel = 2; } else if (!strcmp(argv[i], "-fg")) { CHARcpy(foreground, toCHAR(argv[i + 1])); ndel = 2; } else if (!strcmp(argv[i], "-bg")) { CHARcpy(background, toCHAR(argv[i + 1])); ndel = 2; } else if (!strcmp(argv[i], "-geometry") || !strcmp(argv[i], "-g")) { if (i + 1 >= argc) { msg(MSG_ERROR, "-geometry requires a size and/or position"); return -1; } geomstr = argv[i + 1]; ndel = 2; } else if (!strcmp(argv[i], "-noicon")) { o_icon = False; ndel = 1; } else if (!strcmp(argv[i], "-iconic")) { o_iconic = True; ndel = 1; } else if (!strcmp(argv[i], "-sync")) { XSynchronize(display, 1); ndel = 1; } else if (!strcmp(argv[i], "-client")) { mustfork = client = True; ndel = 1; } else if (!strcmp(argv[i], "-fork")) { mustfork = True; ndel = 1; } /* if we used some arguments, then delete them */ if (i + ndel > argc) { msg(MSG_FATAL, "[s]$1 requires an argument", argv[i]); } if (ndel > 0) { for (j = i; j < argc - ndel; j++) { argv[j] = argv[j + ndel]; } argc -= ndel; } } /* Define some atoms */ wm_protocols = XInternAtom(display, "WM_PROTOCOLS", False); wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); elvis_cutbuffer = XInternAtom(display, "ELVIS_CUTBUFFER", False); elvis_server = XInternAtom(display, "ELVIS_SERVER", False); /* if we're supposed to be a client of an existing elvis, then * do the client thing. If that fails, then ignore -client. */ if (client && clientaction(argc, argv)) { return -1; } /* if we're supposed to fork, then do that */ if (mustfork) { XCloseDisplay(display); switch (fork()) { case -1: /* ERROR */ perror("elvis: could not fork"); return -1; case 0: /* CHILD */ /* become immune to terminal signals */ #ifdef NEED_SETPGID setpgrp(); #else setpgid(0, 0); #endif /* reinitialize the connection */ display = XOpenDisplay(""); screen = DefaultScreen(display); root = RootWindow(display, screen); /* and then continue execution... */ break; default: /* PARENT */ return -1; } } /* more initialization */ rootheight = DisplayHeight(display, screen); rootwidth = DisplayWidth(display, screen); depth = DefaultDepth(display, screen); colormap = DefaultColormap(display, screen); elvis_icon = XCreateBitmapFromData(display, DefaultRootWindow(display), (char *)elvis_bits, elvis_width, elvis_height); XGetInputFocus(display, &fromwin, &i); if (fromwin == None || fromwin == PointerRoot) { fromwin = root; } /* ignore any protocol errors. We expect errors from XSetInputFocus() * in some situations, and no errors from anything else. */ XSetErrorHandler(ignoreErrors); /* initialize the options */ optflags(o_normalfont) |= OPT_HIDE; optflags(o_boldfont) |= OPT_HIDE; optflags(o_italicfont) |= OPT_HIDE; optflags(o_controlfont) |= OPT_HIDE; optpreset(o_toolbar, DEFAULT_TOOLBAR, OPT_HIDE); optpreset(o_scrollbarwidth, DEFAULT_SCROLLBARWIDTH, OPT_HIDE); optpreset(o_scrollbartime, DEFAULT_SCROLLBARTIME, OPT_HIDE); optpreset(o_dblclicktime, DEFAULT_DBLCLICKTIME, OPT_HIDE); optpreset(o_blinktime, DEFAULT_BLINKTIME, OPT_HIDE); optpreset(o_xrows, DEFAULT_XROWS, OPT_HIDE); optpreset(o_xcolumns, DEFAULT_XCOLUMNS, OPT_HIDE); optpreset(o_icon, True, OPT_HIDE); optpreset(o_stopshell, toCHAR(DEFAULT_STOPSHELL), OPT_HIDE); optpreset(o_altkey, 's', OPT_HIDE); optpreset(o_warpto, 'd', OPT_HIDE); optpreset(o_focusnew, True, OPT_HIDE); optinsert("x11", QTY(x11desc), x11desc, (OPTVAL *)&x11opt); /* initialize the colors */ CHARcpy(cursorcolor, toCHAR(DEFAULT_CURSORCOLOR)); CHARcpy(owncolor, toCHAR(DEFAULT_OWNCOLOR)); CHARcpy(scrollbarfg, toCHAR(DEFAULT_SCROLLBARFG)); CHARcpy(scrollbarbg, toCHAR(DEFAULT_SCROLLBARBG)); /* convert geometry string, if given */ if (geomstr) { /* Note: We're doing something weird here. Since we * don't know yet how large the character cells will be, * we can't compute the window size and position * correctly. We'll compute it incorrectly here, and * remember the details so we can make corrections * later. */ flags = XGeometry(display, screen, geomstr, DEFAULT_GEOMETRY, 0, 1, 1, 0, 0, &x, &y, &w, &h); if ((flags & (WidthValue|HeightValue)) == (WidthValue|HeightValue)) { o_xcolumns = w; o_xrows = h; } if ((flags & (XValue|YValue)) == (XValue|YValue)) { if (flags & XNegative) optpreset(o_firstx, x + o_xcolumns - rootwidth, OPT_SET); else optpreset(o_firstx, x, OPT_SET); if (flags & YNegative) optpreset(o_firsty, y + o_xrows - rootheight, OPT_SET); else optpreset(o_firsty, y, OPT_SET); } /* now, if size was really given, then mark xrows and * xcolumns as having been explicitly "set". */ flags = XGeometry(display, screen, geomstr, NULL, 0, 1, 1, 0, 0, &x, &y, &w, &h); if ((flags & (WidthValue|HeightValue)) == (WidthValue|HeightValue)) { optflags(o_xrows) |= OPT_SET; optflags(o_xcolumns) |= OPT_SET; } } /* map the cursor keypad keys */ for (i = 0; i < QTY(keys); i++) { if (keys[i].sym == XK_Delete) sprintf(raw, "%c", ELVCTRL('?')); else sprintf(raw, "%c%04lx",ELVCTRL('K'), (long)keys[i].sym); mapinsert(toCHAR(raw), (int)strlen(raw), toCHAR(keys[i].cooked), (int)strlen(keys[i].cooked), toCHAR(keys[i].label), keys[i].flags); } /* Redirect stdin to come from /dev/null. This will only affect * filter programs, and programs started via the ! command. Without * this, programs which attempted to read keystrokes would wait * forever because the keystrokes would have to come from the ASCII * terminal where elvis was invoked, NOT from elvis' own window. */ if (close(0) == 0) open("/dev/null", O_RDONLY); return argc; } /* output gui-dependent options */ static void usage() { msg(MSG_INFO, " -font normalfont Use \"normalfont\" as normal font"); msg(MSG_INFO, " -fn normalfont Same as -font normalfont"); msg(MSG_INFO, " -fb boldfont Use \"boldfont\" \\(else derive from normal font\\)"); msg(MSG_INFO, " -fi italicfont Use \"italicfont\" \\(else derive from normal font\\)"); msg(MSG_INFO, " -courier size Use three courier fonts of given size"); msg(MSG_INFO, " -fc controlfont Use \"controlfont\" in the toolbar"); msg(MSG_INFO, " -fg color Use \"color\" for the foreground \\(default black\\)"); msg(MSG_INFO, " -bg color Use \"color\" for the background \\(default white\\)"); msg(MSG_INFO, "[s] -geometry WxH+X+Y Set the window's size and/or position \\(default $1\\)", DEFAULT_GEOMETRY); msg(MSG_INFO, " -noicon Don't use built-in bitmap icon"); msg(MSG_INFO, " -iconic First window should start iconified"); msg(MSG_INFO, " -sync Disable X11 buffering, for debugging"); msg(MSG_INFO, " -fork Run in background"); msg(MSG_INFO, " -client Edit files via an existing elvis process"); } static void erasecursor(xw) X11WIN *xw; /* window whose cursor should be hidden */ { /* hide the cursor (if shown) */ if (xw->cursor != CURSOR_NONE) { if (xw->ismapped) { if (xw->grexpose) { XSetGraphicsExposures(display, xw->gc, False); xw->grexpose = False; } XCopyArea(display, xw->undercurs, xw->textw, xw->gc, 0, 0, xw->cellw, xw->cellh, (int)(xw->cursx * xw->cellw), (int)(xw->cursy * xw->cellh)); } xw->cursor = CURSOR_NONE; } } static void drawcursor(xw) X11WIN *xw; /* window whose cursor should be drawn */ { unsigned long color; /* a NULL "xw" forces all cursors to be redrawn */ if (!xw) { for (xw = winlist; xw; xw = xw->next) { xw->nextcursor = xw->cursor; erasecursor(xw); drawcursor(xw); } return; } /* if not mapped, then no cursor should be drawn */ if (!xw->ismapped) { xw->cursor = CURSOR_NONE; return; } /* if same as before, do nothing */ if (xw->nextcursor == xw->cursor) { return; } /* choose a color */ color = (ownselection ? xw->owncursor : xw->fgcursor); /* if some other cursor shape is already drawn there, then erase it */ if (xw->cursor != CURSOR_NONE) { erasecursor(xw); } else /* save the image of the cursor where we'll draw the cursor */ { if (xw->grexpose) { XSetGraphicsExposures(display, xw->gc, False); xw->grexpose = False; } XCopyArea(display, xw->textw, xw->undercurs, xw->gc, (int)(xw->cursx * xw->cellw), (int)(xw->cursy * xw->cellh), xw->cellw, xw->cellh, 0, 0); } xw->cursor = xw->nextcursor; /* draw the cursor, using the cursor color */ if (xw->grexpose) { XSetGraphicsExposures(display, xw->gc, False); xw->grexpose = False; } if (xw->fg != color) { XSetForeground(display, xw->gc, color); xw->fg = color; } switch (xw->cursor) { case CURSOR_INSERT: XFillRectangle(display, xw->textw, xw->gc, (int)(xw->cursx * xw->cellw), (int)(xw->cursy * xw->cellh), 2, xw->cellh); break; case CURSOR_REPLACE: case CURSOR_QUOTE: XFillRectangle(display, xw->textw, xw->gc, (int)(xw->cursx * xw->cellw), (int)((xw->cursy + 1) * xw->cellh - 2), xw->cellw, 2); break; case CURSOR_COMMAND: XDrawRectangle(display, xw->textw, xw->gc, (int)(xw->cursx * xw->cellw), (int)(xw->cursy * xw->cellh), xw->cellw - 1, xw->cellh - 1); break; case CURSOR_NONE: break; } } /* Simulate a "destroy" event for the window, or do the cleanup work after * a real destroy notify event. */ static void destroygw(gw, force) GUIWIN *gw; /* the window to be destroyed */ BOOLEAN force; /* if True, try harder */ { X11WIN *xw, *prev; /* find the doomed window */ for (xw = winlist, prev = NULL; xw != (X11WIN *)gw; prev = xw, xw = xw->next) { assert(xw->next != NULL); } eventdestroy((GUIWIN *)xw); /* delete the window from the list of existing windows */ if (prev) { prev->next = xw->next; } else { winlist = xw->next; } /* switch keyboard focus to another elvis window */ if (winlist != NULL) { /* nishi */ /* Choose one which isn't iconified */ for (prev = winlist; prev && !prev->ismapped; prev = prev->next) { } if (prev && o_focusnew) { focusgw((GUIWIN *)winlist); } /* Also make the other window be an elvis server window. This * is only significant if the doomed window used to be the * server, but it is just as easy to change it every time. */ XChangeProperty(display, root, elvis_server, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&winlist->window, 1); } /* free the window's resources */ XFreePixmap(display, xw->undercurs); XFreeGC(display, xw->gc); if (xw->window) { XDestroySubwindows(display, xw->window); XDestroyWindow(display, xw->window); } unloadcolor(xw->fgnormal); unloadcolor(xw->bgnormal); unloadcolor(xw->fgfixed); unloadcolor(xw->fgbold); unloadcolor(xw->fgemph); unloadcolor(xw->fgitalic); unloadcolor(xw->fgundln); unloadcolor(xw->fgcursor); unloadcolor(xw->bgscroll); unloadcolor(xw->fgscroll); safefree(xw->title); safefree(xw); } /* This function changes the keyboard focus to a specific window */ static BOOLEAN focusgw(gw) GUIWIN *gw; /* the window to receive keyboard focus */ { X11WIN *xw = (X11WIN *)gw; int x1, y1, x2, y2; /* If the window is unmapped (iconfied) then map it. Also, if the * autoiconify option is set then unmap the previous window. */ if (!xw->ismapped) { XMapWindow(display, xw->window); if (o_autoiconify && windefault && windefault->gw != gw) { XIconifyWindow(display, ((X11WIN *)windefault->gw)->window, screen); } /* the rest of the focus change must wait until it is mapped */ return True; } /* Raise the window. (I.e., make it fully visible.) */ XRaiseWindow(display, xw->window); /* Move the pointer to some point in the window, so that if * keyboard focus follows the mouse, this will switch focus. */ if (!xw->nowarp) { switch (o_warpto) { case 'o': /* "origin" */ XWarpPointer(display, None, xw->window, 0,0,0,0, 0,0); break; case 's': /* scrollbar */ XWarpPointer(display, None, xw->window, 0,0,0,0, (int)(xw->cellw * xw->columns + o_scrollbarwidth/2), (xw->sbtop + xw->sbbottom)/2); break; case 'c': /* corners */ /* set x1,y1 to the furthest corner, x2,y2 to nearest */ if (xw->cursx < xw->columns / 2) x1 = xw->cellw * xw->columns + o_scrollbarwidth - 1, x2 = 0; else x1 = 0, x2 = xw->cellw * xw->columns + o_scrollbarwidth - 1; if (xw->cursy < xw->rows / 2) y1 = xw->cellh * xw->rows - 1, y2 = 0; else y1 = 0, y2 = xw->cellh * xw->rows - 1; /* warp to furthest corner unless it's off the screen */ if (y1 + xw->y < rootheight && x1 + xw->x < rootwidth) XWarpPointer(display, None, xw->window, 0,0,0,0, x1,y1); XFlush(display); /* warp to nearest corner */ XWarpPointer(display, None, xw->window, 0,0,0,0, x2,y2); break; /* case 'd': don't -- requires no action */ } } xw->nowarp = False; /* Explicitly change the focus */ XSetInputFocus(display, xw->window, RevertToParent, now); hasfocus = xw; return True; } /* In a loop, receive events from the GUI and call elvis * functions which will act on the event. When this function * returns, elvis will call the GUI's term() function and then exit. * (This function should return only when the number of windows becomes 0.) */ static void loop() { XEvent event; /* an X event to process */ XEvent notify; /* used in selections */ XEvent rptevent; /* event to repeat */ long rpttime = -1; /* amount of time to allow before repeat */ KeySym mykey; Window owner; int i, j; char text[20]; X11WIN *xw; int top, bottom, left, right; int ptrx, ptry, prevptrx, prevptry; BOOLEAN didcmd; BOOLEAN marking; /* dragging mouse in text area */ BOOLEAN thumbing; /* dragging scrollbar's "thumb" */ BOOLEAN paging; /* holding mouse button on scollbar's buttons */ ELVISSTATE state = 0; long offset; CHAR modifier; fd_set rfds, wfds, efds; struct timeval timeout; char *excmd; unsigned long exlen, ldummy; Atom gottype; TOOL *tool; /* loop until we don't have any windows left */ didcmd = True; prevptrx = prevptry = -1; marking = thumbing = paging = False; while (winlist) { /* draw new window images, if they may have changed */ if ((rpttime >= 0 || XEventsQueued(display, QueuedAfterFlush) == 0) && didcmd) { /* for each window... */ for (xw = winlist; xw; xw = xw->next) { if (xw->ismapped) { xw->nextcursor = eventdraw((GUIWIN *)xw); drawcursor(xw); setsbstate(xw, xw->cursy == xw->rows - 1 ? SB_BLANK : SB_NORMAL); drawtoolbar(xw, False); } } didcmd = False; } /* Read the next event. This is complicated by the need to * autorepeat some scrollbar events. */ if (rpttime >= 0 && XPending(display) <= 0) { /* wait for either rpttime to expire, or for an event * other than MotionNotify to be received. If rpttime * expires first, then pretend the rptevent was received * again. */ XFlush(display); do { FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); FD_SET(ConnectionNumber(display), &rfds); timeout.tv_sec = rpttime / 10; timeout.tv_usec = (rpttime % 10) * 100000 + 33333; i = select(ConnectionNumber(display) + 1, &rfds, &wfds, &efds, &timeout); if (i > 0) { do { XNextEvent(display, &event); } while (event.type == MotionNotify && XPending(display)); } } while (i > 0 && event.type == MotionNotify); if (i == 0) { event = rptevent; } } else { /* blink the cursor until an event is available */ if (hasfocus && XPending(display) == 0 && o_blinktime > 0) { do { FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); FD_SET(ConnectionNumber(display), &rfds); timeout.tv_sec = o_blinktime / 10; timeout.tv_usec = (o_blinktime % 10) * 100000; i = select(ConnectionNumber(display) + 1, &rfds, &wfds, &efds, &timeout); if (i == 0) { if (hasfocus->cursor == CURSOR_NONE) drawcursor(hasfocus); else erasecursor(hasfocus); XFlush(display); } } while (i == 0); /* make cursor visible while processing event */ if (hasfocus->cursor == CURSOR_NONE) drawcursor(hasfocus); } /* read the next event */ XNextEvent(display, &event); /* compress MotionNotify events */ if (event.type == MotionNotify) { while (XEventsQueued(display, QueuedAfterReading) > 0) { XPeekEvent(display, ¬ify); if (notify.type != MotionNotify) break; XNextEvent(display, &event); } } } /* process the event */ switch (event.type) { case Expose: /* repaint window on Expose events */ /* find the window */ WIN2XW(event.xexpose.window, xw); /* toolbar is easy */ if (event.xexpose.window == xw->toolw) { drawtoolbar(xw, True); break; } /* draw the image */ xw->ismapped = True; xw->sbheight = 0; erasecursor(xw); top = event.xexpose.y / xw->cellh; left = event.xexpose.x / xw->cellw; bottom = (event.xexpose.y + event.xexpose.height) / xw->cellh; right = (event.xexpose.x + event.xexpose.width) / xw->cellw; if (bottom < 0) /* just toolbar exposed? */ bottom = 0; /* must redraw something */ if (left >= xw->columns) /* just scrollbar exposed? */ left = xw->columns - 1; /* must redraw something */ eventexpose((GUIWIN *)xw, top, left, bottom, right); xw->nextcursor = eventfocus((GUIWIN *)xw); drawcursor(xw); break; case MapNotify: WIN2XW(event.xexpose.window, xw); xw->ismapped = True; if (o_focusnew) (void)focusgw((GUIWIN *)xw); break; case UnmapNotify: WIN2XW(event.xexpose.window, xw); xw->ismapped = False; break; case GraphicsExpose: WIN2XW(event.xexpose.window, xw); top = event.xexpose.y / xw->cellh; left = event.xexpose.x / xw->cellw; bottom = (event.xexpose.y + event.xexpose.height) / xw->cellh; right = (event.xexpose.x + event.xexpose.width) / xw->cellw; eventexpose((GUIWIN *)xw, top, left, bottom, right); break; case MappingNotify: /* process keyboard mapping changes */ XRefreshKeyboardMapping(&event.xmapping); break; case FocusIn: WIN2XW(event.xfocus.window, xw); hasfocus = xw; break; case FocusOut: WIN2XW(event.xfocus.window, xw); if (xw == hasfocus) hasfocus = NULL; break; case ButtonPress: /* process mouse-button presses */ WIN2XW(event.xbutton.window, xw); now = event.xbutton.time; /* check for toolbar event */ if (event.xbutton.window == xw->toolw) { /* determine which tool button was pressed */ for (tool = toollist; tool && (event.xbutton.x < tool->x || tool->x + tool->width < event.xbutton.x); tool = tool->next) { } /* if a non-disabled button was pressed... */ if (tool && tool->excmd && xw->toolstate[tool->id] != 'f') { /* temporarily draw it "pushed in" */ draw1tool(xw, tool, 'i'); flush(); /* execute its ex command */ eventex((GUIWIN *)xw, tool->excmd, tool->safer); didcmd = True; } break; } /* If this is a text-area button press, then we'll * need to know which character cell was clicked. * It's convenient to compute it here. */ ptry = event.xbutton.y / xw->cellh; ptrx = event.xbutton.x / xw->cellw; /* check for scrollbar event */ xw->x = event.xbutton.x_root - event.xbutton.x; xw->y = event.xbutton.y_root - event.xbutton.y; if (ptrx >= xw->columns) { /* scrollbar event */ rptevent = event; if (event.xbutton.y < o_scrollbarwidth + SB_GAP) { /* clicked on the up arrow */ (void)eventscroll((GUIWIN *)xw, SCROLL_BACKLN, 1L, 0L); rpttime = (rpttime < 0 ? o_scrollbartime : 0); paging = True; } else if (event.xbutton.y >= xw->sbheight - (o_scrollbarwidth + SB_GAP)) { /* clicked on the down arrow */ (void)eventscroll((GUIWIN *)xw, SCROLL_FWDLN, 1L, 0L); rpttime = (rpttime < 0 ? o_scrollbartime : 0); paging = True; } else if (event.xbutton.y < xw->sbtop) { /* clicked before the thumb */ (void)eventscroll((GUIWIN *)xw, SCROLL_BACKSCR, 1L, 0L); rpttime = (rpttime < 0 ? o_scrollbartime : 0); paging = True; } else if (event.xbutton.y > xw->sbbottom) { /* clicked after the thumb */ (void)eventscroll((GUIWIN *)xw, SCROLL_FWDSCR, 1L, 0L); rpttime = (rpttime < 0 ? o_scrollbartime : 0); paging = True; } else { /* clicked on the thumb */ prevptry = event.xbutton.y - xw->sbtop + (o_scrollbarwidth + SB_GAP); prevptrx = -1; thumbing = True; rpttime = -1; } didcmd = True; } else if (now - firstclick > o_dblclicktime * 100 || event.xbutton.button == Button2 || prevptry != ptry || prevptrx != ptrx) { /* single-click */ if (event.xbutton.button != Button3) { (void)eventclick((GUIWIN *)xw, -1, -1, CLICK_CANCEL); } if (event.xbutton.button != Button2) { if (firstclick != 1 || prevptry != ptry || prevptrx != ptrx) { offset = eventclick((GUIWIN *)xw, ptry, ptrx, CLICK_MOVE); didcmd |= (BOOLEAN)(offset >= 0); } firstclick = now; } if (event.xbutton.button == Button3) { (void)eventclick((GUIWIN *)xw, -1, -1, CLICK_YANK); } prevptry = ptry; prevptrx = ptrx; didcmd = True; } else { /* double-click */ (void)eventclick((GUIWIN *)xw, -1, -1, event.xbutton.button == Button1 ? CLICK_TAG : CLICK_UNTAG); firstclick = 1; didcmd = True; } break; case MotionNotify: /* ignore if wiggly click on a scrollbar button */ if (paging) break; /* process mouse movement while button held down */ WIN2XW(event.xbutton.window, xw); now = event.xmotion.time; xw->x = event.xbutton.x_root - event.xbutton.x; xw->y = event.xbutton.y_root - event.xbutton.y; /* moving the scrollbar's thumb? */ if (thumbing) { /* compute the percentage */ ptry = event.xbutton.y - prevptry + 1; i = xw->sbheight - 2 * (o_scrollbarwidth + SB_GAP); if (i <= 0) break; if (ptry < 0) ptry = 0; else if (ptry > i) ptry = i; if (ptry != prevptrx) { (void)eventscroll((GUIWIN *)xw, SCROLL_PERCENT, (long)ptry, (long)i); didcmd = True; prevptrx = ptry; } break; } /* convert to character cell coordinates */ ptry = event.xbutton.y / xw->cellh; ptrx = event.xbutton.x / xw->cellw; /* ignore wigglies when clicking on the scrollbar */ if (ptrx >= xw->columns) { break; } /* if not the same cell as last time... */ if (ptry != prevptry || ptrx != prevptrx) { offset = eventclick((GUIWIN *)xw, ptry, ptrx, CLICK_MOVE); if (offset >= 0) { /* If moved off original character, start marking */ if (!marking && 0 <= eventclick((GUIWIN *)xw, prevptry, prevptrx, (event.xmotion.state & Button2Mask) ? CLICK_SELRECT : (event.xmotion.state & Button3Mask) ? CLICK_SELLINE : CLICK_SELCHAR)) { (void)eventclick((GUIWIN *)xw, ptry, ptrx, CLICK_MOVE); marking = True; } prevptry = ptry; prevptrx = ptrx; didcmd = True; } } break; case ButtonRelease: /* find the guix11 window structure */ WIN2XW(event.xbutton.window, xw); now = event.xbutton.time; xw->x = event.xbutton.x_root - event.xbutton.x; xw->y = event.xbutton.y_root - event.xbutton.y; /* Is this the end of a mark, or end of a click? */ if (thumbing) { /* nothing to do here */ } else if (marking) { /* end of a mark - yank the selected text and * then leave it marked. */ eventclick((GUIWIN *)xw, -1, -1, CLICK_YANK); } else if (rpttime >= 0) { /* stop repeating a scroll button press */ rpttime = -1; } else { /* end of a click - depends on button */ if (event.xbutton.state & Button2Mask) { /* paste from "< buffer */ eventclick((GUIWIN *)xw, -1, -1, CLICK_PASTE); didcmd = True; } } #if 0 prevptrx = prevptry = -1; #endif thumbing = marking = paging = False; break; case KeyPress: /* get some standard info from the event */ WIN2XW(event.xkey.window, xw); now = event.xkey.time; xw->x = event.xkey.x_root - event.xkey.x; xw->y = event.xkey.y_root - event.xkey.y; /* check for modifier keys */ if (event.xkey.state & Mod1Mask) modifier = o_altkey; else modifier = '\0'; /* convert the keypress to a KeySym and string */ i = XLookupString(&event.xkey, text, sizeof text, &mykey, 0); #if 1 /* THIS IS A HACK! Some versions of XFree86 come with * a default map which causes the backspace keycode to * be translated to XK_Delete instead of XK_BackSpace. * This causes big problems for elvis, since elvis would * like to make the <backspace> and <delete> keys do * different things. If the current keystroke appears * to be a backspace keycode which has been mapped to * XK_Delete, then we force it to be mapped to * XK_BackSpace instead. * * This problem could also be solved by the user running * `xmodmap -e "keycode 22 = BackSpace"` after starting * the X server. */ if (mykey == XK_Delete && event.xkey.keycode == 22) { mykey = XK_BackSpace; text[0] = '\b'; text[1] = '\0'; } #endif if (i == 0) { if (!IsModifierKey(mykey) && mykey != XK_Mode_switch) { /* function keys become a control sequence */ sprintf(text, "%c%04x", ELVCTRL('K'), (int)mykey); i = strlen(text); } } if (i > 0) { switch (modifier) { case 'c': (void)eventkeys((GUIWIN *)xw, toCHAR("\017"), 1); break; case 's': if (i == 1) text[0] |= 0x80; break; default: ;/* do nothing */ } state = eventkeys((GUIWIN *)xw, toCHAR(text), i); /* The "xw" window may have been deleted during * the eventkeys() call above. If the window * still exists then we have some work to do. */ #if 1 WIN2XW(event.xkey.window, xw); #else for (xw = winlist; xw && xw->window != event.xkey.window && ; xw = xw->next) { } if (xw) #endif { /* user maps never time out (Darn!) but * key maps timeout immediately. */ xw->state = state; if (xw->state == MAP_KEY) { xw->state = eventkeys((GUIWIN *)xw, toCHAR(text), 0); } didcmd = True; } } break; case ConfigureNotify: WIN2XW(event.xconfigure.window, xw); i = (event.xconfigure.width - o_scrollbarwidth) / xw->cellw; j = (event.xconfigure.height - TB_HEIGHT) / xw->cellh; if (i != xw->columns || j != xw->rows) { xw->columns = i; xw->rows = j; XResizeWindow(display, xw->textw, (unsigned)event.xconfigure.width, (unsigned)(event.xconfigure.height - TB_HEIGHT)); if (o_toolbar) { XResizeWindow(display, xw->toolw, (unsigned)event.xconfigure.width, (unsigned)TB_HEIGHT); } eventresize((GUIWIN *)xw, (int)xw->rows, (int)xw->columns); didcmd = True; } break; case ClientMessage: /* if WM_DELETE_WINDOW from the window manager, then * destroy the X11 window. The server will then send * us a DestroyNotify message so we can finish * cleaning up. */ if (event.xclient.message_type == wm_protocols && event.xclient.format == 32 && event.xclient.data.l[0] == wm_delete_window && winlist->next) { XDestroyWindow(display, event.xclient.window); } break; case DestroyNotify: /* fetch id of window to destroy */ offset = (event.type == DestroyNotify ? event.xdestroywindow.window : event.xclient.window); /* locate the corresponding X11WIN structure */ WIN2XW(offset, xw); /* The X window itself is already gone */ xw->window = 0; /* other stuff still needs to be taken care of */ destroygw((GUIWIN *)xw, True); break; case PropertyNotify: WIN2XW(event.xproperty.window, xw); if (event.xproperty.atom == elvis_server && state == PropertyNewValue) { XGetWindowProperty(display, xw->window, elvis_server, 0L, 8192, True, XA_STRING, &gottype, &i, (unsigned long *)&exlen, (unsigned long *)&ldummy, (unsigned char **)&excmd); if (exlen > 0) { if (gottype == XA_STRING && i == 8) { excmd[exlen] = '\0'; eventex((GUIWIN *)xw, excmd, True); didcmd = True; } XFree(excmd); } /* never leave old data in the property */ if (ldummy > 0) { XDeleteProperty(display, xw->window, elvis_server); } } break; case SelectionClear: /* We may have lost our old selection yet still be * responsible for the new selection. Check to see * if we still own the current selection. */ owner = XGetSelectionOwner(display, XA_PRIMARY); for (xw = winlist; xw && xw->window != owner; xw = xw->next) { } if (ownselection && !xw) { /* free the selection */ if (clipbuf) { safefree(clipbuf); clipbuf = NULL; } ownselection = False; drawcursor(NULL); } break; case SelectionRequest: /* create a SelectionNotify event for requestor */ notify.type = SelectionNotify; notify.xselection.requestor = event.xselectionrequest.requestor; notify.xselection.selection = event.xselectionrequest.selection; notify.xselection.target = event.xselectionrequest.target; notify.xselection.time = event.xselectionrequest.time; /* try to convert the selection */ if (event.xselectionrequest.selection == XA_PRIMARY && event.xselectionrequest.target == XA_STRING) { /* store the selection's value into the property */ XChangeProperty(display, event.xselectionrequest.requestor, event.xselectionrequest.property, event.xselectionrequest.target, 8, PropModeReplace, (unsigned char *)(clipbuf ? clipbuf : ""), clipsize); notify.xselection.property = event.xselectionrequest.property; } else { /* can't convert */ notify.xselection.property = None; } /* notify the requestor */ XSendEvent(display, notify.xselection.requestor, False, 0L, ¬ify); } } } /* Test for signs of boredom from the user, so we can cancel long operations. * Here, we check to see if user has clicked on the window. */ static BOOLEAN wpoll(reset) BOOLEAN reset; { XEvent event; X11WIN *scan; /* ignore if simply trying to reset */ if (reset) return False; /* Check every window to see if it has a ButtonPress event pending */ if (XPending(display) > 0 && XCheckTypedEvent(display, ButtonPress, &event)) { return True; } /* Since we're polling, redraw all scrollbars to show stop signs so * the user knows that a click will abort. */ for (scan = winlist; scan; scan = scan->next) { setsbstate(scan, SB_STOP); } return False; } /* dummy X11 error handler */ static int ignoreErrors(disp, err) Display *disp; XErrorEvent *err; { return 0; } /* End the GUI. For "termcap" this means switching the * the terminal back into "cooked" mode. For "x11", the * window should be deleted and any other X resources freed. * * This function is called after all windows have been deleted * by delwin(), when elvis is about to terminate. */ static void term() { Window curfocus; int dummy; /* unload any fonts */ while (fonts) { unloadfont(fonts); } /* forget any tools */ guicmd(NULL, "newtoolbar"); /* warp the cursor back to the original (non-Elvis) window */ if (fromwin != root && (!XGetInputFocus(display, &curfocus, &dummy) || curfocus != fromwin)) { if (o_warpback) XWarpPointer(display, None, fromwin, 0, 0, 0, 0, 0, 0); if (o_warpback || o_focusnew) XSetInputFocus(display, fromwin, RevertToParent, now); } /* delete the server property from the root window */ XDeleteProperty(display, root, elvis_server); /* close the connection to the display */ XCloseDisplay(display); } /* Create a new window for the buffer named name. If successful, * return TRUE and then simulate a "create" event later. Return * FALSE if the GUIWIN can't be created, e.g., because the GUI doesn't * support multiple windows. The msg() function should be called to * describe the reason for the failure. */ static BOOLEAN creategw(name, attributes) char *name; /* name of buffer for the new window */ char *attributes; /* other window parameters, if any */ { XSizeHints hint; XWMHints wmhint; XClassHint class; XTextProperty textprops[2]; X11WIN *xw; XGCValues gcvalues; char *argv[5]; int argc; /* is this the first time? */ if (!winlist) { /* allocate the fonts named in options. */ defaultnormal = loadfont(o_normalfont ? tochar8(o_normalfont) : DEFAULT_NORMALFONT); if (!defaultnormal) return False; if (o_boldfont && !(defaultbold = loadfont(tochar8(o_boldfont)))) return False; if (o_italicfont && !(defaultitalic = loadfont(tochar8(o_italicfont)))) return False; loadedcontrol = loadfont(o_controlfont ? tochar8(o_controlfont) : DEFAULT_CONTROLFONT); if (!loadedcontrol) return False; /* lock the options that can only be changed during initialization */ optflags(o_normalfont) |= OPT_LOCK; optflags(o_boldfont) |= OPT_LOCK; optflags(o_italicfont) |= OPT_LOCK; optflags(o_scrollbarwidth) |= OPT_LOCK; optflags(o_icon) |= OPT_LOCK; optflags(o_toolbar) |= OPT_LOCK; } /* allocate storage space */ xw = (X11WIN *)safealloc(1, sizeof(*xw)); xw->title = safedup(name); xw->next = winlist; winlist = xw; /* default pixel values */ white = xw->bg = WhitePixel(display, screen); black = xw->bg = BlackPixel(display, screen); xw->fgnormal = loadcolor(foreground, black); xw->fgfixed = loadcolor(fixedcolor, black); xw->fgbold = loadcolor(boldcolor, xw->fgnormal); xw->fgemph = loadcolor(emphcolor, xw->fgnormal); xw->fgitalic = loadcolor(italiccolor, xw->fgnormal); xw->fgundln = loadcolor(underlinecolor, xw->fgnormal); xw->fgcursor = loadcolor(cursorcolor, xw->fgnormal); xw->owncursor = loadcolor(owncolor, xw->fgnormal); xw->bgscroll = loadcolor(scrollbarbg, xw->fgnormal); xw->bgnormal = loadcolor(background, white); xw->fgscroll = loadcolor(scrollbarfg, xw->bgnormal); xw->grexpose = False; /* remember font metrics */ xw->cellbase = defaultnormal->fontinfo->ascent; xw->cellh = defaultnormal->height; xw->cellw = defaultnormal->fontinfo->max_bounds.width; /* default window geometry */ xw->rows = o_xrows; xw->columns = o_xcolumns; xw->cursx = xw->cursy = 0; /* initial scrollbar appearance */ xw->sbheight = xw->rows * xw->cellh; xw->sbtop = o_scrollbarwidth + SB_GAP; xw->sbbottom = xw->sbheight - (o_scrollbarwidth + SB_GAP); xw->sbstate = SB_BLANK; /* default window size */ hint.x = hint.y = 0; hint.width = xw->columns * xw->cellw + o_scrollbarwidth; hint.height = xw->rows * xw->cellh + TB_HEIGHT; hint.base_width = o_scrollbarwidth; hint.base_height = TB_HEIGHT; hint.width_inc = xw->cellw; hint.height_inc = xw->cellh; hint.min_width = 20 * xw->cellw + o_scrollbarwidth; hint.min_height = 4 * xw->cellh; hint.max_width = 200 * xw->cellw + o_scrollbarwidth; hint.max_height = 200 * xw->cellh; hint.flags = PSize | PBaseSize | PMinSize | PResizeInc; /* maybe set the window position, too */ if (!xw->next && (optflags(o_firstx) & OPT_SET) != 0) { /* first window -- use firstx & firsty */ xw->x = hint.x = (o_firstx >= 0) ? o_firstx : rootwidth + o_firstx - hint.width - 6; xw->y = hint.y = (o_firsty >= 0) ? o_firsty : rootheight + o_firsty - hint.height - 12; hint.flags |= PPosition | USPosition; } else if (o_stagger > 0 && xw->next) { /* not first window -- use stagger */ xw->x = hint.x = xw->next->x + o_stagger; if (hint.x + hint.width >= rootwidth) xw->x = hint.x = 0; xw->y = hint.y = xw->next->y + o_stagger; if (hint.y + hint.height >= rootheight) xw->y = hint.y = 0; hint.flags |= PPosition | USPosition; } else { /* probably have to position it manually -- don't warp pointer*/ xw->nowarp = True; } /* window creation */ xw->window = XCreateSimpleWindow(display, root, hint.x, hint.y, (unsigned)hint.width, (unsigned)hint.height, 5, xw->fgnormal, xw->fgscroll); xw->textw = XCreateSimpleWindow(display, xw->window, 0, TB_HEIGHT, (unsigned)hint.width, (unsigned)(xw->rows * xw->cellh), 0, xw->fgnormal, xw->bgnormal); if (o_toolbar) { xw->toolw = XCreateSimpleWindow(display, xw->window, 0, 0, (unsigned)hint.width, (unsigned)TB_HEIGHT, 0, xw->fgnormal, xw->bgscroll); } /* Set the standard properties */ argv[0] = xw->title; XStringListToTextProperty(argv, 1, &textprops[0]); argv[0] = dirfile(xw->title); XStringListToTextProperty(argv, 1, &textprops[1]); argc = 0; argv[argc++] = argv0; if (o_session) { argv[argc++] = "-s"; argv[argc++] = tochar8(o_session); } wmhint.input = True; wmhint.initial_state = (o_iconic && !xw->next) ? IconicState : NormalState; if (o_icon) { wmhint.icon_pixmap = elvis_icon; wmhint.flags = InputHint | StateHint | IconPixmapHint; } else { wmhint.flags = InputHint | StateHint; } XSetWMProperties(display, xw->window, &textprops[0], &textprops[1], argv, argc, &hint, &wmhint, NULL); XFree(textprops[0].value); XFree(textprops[1].value); /* set class hints */ class.res_name = "elvis"; class.res_class = "Elvis"; XSetClassHint(display, xw->window, &class); /* allow window manager's "Delete" menu item to work */ XSetWMProtocols(display, xw->window, &wm_delete_window, 1); /* GC creation and initialization */ gcvalues.foreground = xw->fg; gcvalues.background = xw->bg; gcvalues.font = defaultnormal->fontinfo->fid; gcvalues.graphics_exposures = xw->grexpose; xw->gc = XCreateGC(display, xw->window, GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcvalues); /* pixmap creation, for storing image of character under cursor */ xw->undercurs = XCreatePixmap(display, xw->window, xw->cellw, xw->cellh, (unsigned)depth); xw->cursor = CURSOR_NONE; xw->nextcursor = CURSOR_QUOTE; /* make it work as an elvis server window */ XChangeProperty(display, root, elvis_server, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&xw->window, 1); /* input event selection */ XSelectInput(display, xw->window, KeyPressMask|KeyReleaseMask|FocusChangeMask|StructureNotifyMask|PropertyChangeMask); XSelectInput(display, xw->textw, ButtonPressMask|ButtonMotionMask|ButtonReleaseMask|ExposureMask); if (o_toolbar) { XSelectInput(display, xw->toolw, ButtonPressMask|ExposureMask); } /* window mapping */ XMapSubwindows(display, xw->window); XMapRaised(display, xw->window); xw->ismapped = (BOOLEAN)(wmhint.initial_state == NormalState); /* simulate a "window create" event */ eventcreate((GUIWIN *)xw, NULL, name, (int)xw->rows, (int)xw->columns); return True; } /* Change the title of the window. This function is called when a * buffer's name changes, or different buffer becomes associated with * a window. The name argument is the new buffer name. */ static void retitle(gw, name) GUIWIN *gw; /* the window to be retitled */ char *name; /* the new title of the window */ { X11WIN *xw = (X11WIN *)gw; XTextProperty textprop; /* free the old title, remember the new title */ safefree(xw->title); xw->title = safedup(name); /* inform the window manager of the new name */ XStringListToTextProperty(&name, 1, &textprop); XSetWMName(display, xw->window, &textprop); XFree(textprop.value); /* also change the icon name */ name = dirfile(name); XStringListToTextProperty(&name, 1, &textprop); XSetWMIconName(display, xw->window, &textprop); XFree(textprop.value); } /* Flush all changes out to the screen */ static void flush() { XFlush(display); } /* Move the cursor to a given character cell. The upper left * character cell is designated column 0, row 0. */ static void moveto(gw, column, row) GUIWIN *gw; /* the window whose cursor is to be moved */ int column; /* the column to move to */ int row; /* the row to move to */ { X11WIN *xw = (X11WIN *)gw; if (xw->cursx != column || xw->cursy != row) { erasecursor(xw); xw->cursx = column; xw->cursy = row; } } /* Displays text on the screen, starting at the cursor's * current position, in the given font. The text string is * guaranteed to contain only printable characters. * * The font is indicated by a single letter. The letter will * be lowercase normally, or uppercase to indicate that the * text should be visibly marked for the <v> and <V> commands. * The letters are: * n/N normal characters * b/B bold characters * i/I italic characters * u/U underlined characters * g/G graphic characters * s standout (used for messages on bottom row) * p popup menu * * This function should move the text cursor to the end of * the output text. */ static void draw(gw, font, text, len) GUIWIN *gw; /* the window where the text should be drawn */ _char_ font; /* the font code to use for drawing */ CHAR *text; /* the text to draw */ int len; /* number of characters in text */ { X11WIN *xw; long swapper; LOADEDFONT *loaded; CHAR *tmp = NULL; XGCValues gcvalues; int i; xw = (X11WIN *)gw; xw->cursor = CURSOR_NONE; /* set the font & colors */ switch (font) { case 'b': case 'B': gcvalues.foreground = xw->fgbold; loaded = defaultbold; break; case 'e': case 'E': gcvalues.foreground = xw->fgemph; loaded = defaultbold; break; case 'i': case 'I': gcvalues.foreground = xw->fgitalic; loaded = defaultitalic; break; case 'g': case 'G': gcvalues.foreground = xw->fgnormal; loaded = defaultnormal; tmp = safealloc(len, sizeof(CHAR)); for (i = 0; i < len; i++) { if (loaded->fontinfo->min_char_or_byte2 <= '\013') { switch (text[i]) { case '-': tmp[i] = GCH; break; case '|': tmp[i] = GCV; break; case '1': tmp[i] = GC1; break; case '2': tmp[i] = GC2; break; case '3': tmp[i] = GC3; break; case '4': tmp[i] = GC4; break; case '5': tmp[i] = GC5; break; case '6': tmp[i] = GC6; break; case '7': tmp[i] = GC7; break; case '8': tmp[i] = GC8; break; case '9': tmp[i] = GC9; break; default: tmp[i] = text[i]; } } else if (isdigit(text[i])) { tmp[i] = '+'; } else { tmp[i] = text[i]; } } text = tmp; break; case 'u': case 'U': gcvalues.foreground = xw->fgundln; loaded = defaultnormal; break; case 'f': case 'F': gcvalues.foreground = xw->fgfixed; loaded = defaultnormal; break; default: gcvalues.foreground = xw->fgnormal; loaded = defaultnormal; } gcvalues.background = xw->bgnormal; /* if font letter is uppercase, then swap foreground & background */ if (isupper(font)) { swapper = gcvalues.foreground; gcvalues.foreground = gcvalues.background; gcvalues.background = swapper; } /* set the GC values */ gcvalues.font = (loaded ? loaded : defaultnormal)->fontinfo->fid; gcvalues.graphics_exposures = xw->grexpose = False; XChangeGC(display, xw->gc, GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcvalues); xw->fg = gcvalues.foreground; xw->bg = gcvalues.background; /* draw the text */ XDrawImageString(display, xw->textw, xw->gc, (int)(xw->cursx * xw->cellw), (int)(xw->cursy * xw->cellh + xw->cellbase), tochar8(text), len); if ((font == 'b' || font == 'B' || font == 'e' || font == 'E') && !defaultbold) { XDrawString(display, xw->textw, xw->gc, (int)(xw->cursx * xw->cellw + 1), (int)(xw->cursy * xw->cellh + xw->cellbase), tochar8(text), len); } if (font == 'u' || font == 'U') { XFillRectangle(display, xw->textw, xw->gc, (int)(xw->cursx * xw->cellw), (int)((xw->cursy + 1) * xw->cellh - 1), len * xw->cellw, 1); } if ((font == 'i' || font == 'I') && !defaultitalic) { XCopyArea(display, xw->textw, xw->textw, xw->gc, (int)(xw->cursx * xw->cellw), (int)(xw->cursy * xw->cellh), len * xw->cellw - 1, (xw->cellh + 1) / 2, (int)(xw->cursx * xw->cellw + 1), (int)(xw->cursy * xw->cellh)); } /* free the temp string (if any) */ if (tmp) { safefree(tmp); } /* leave the cursor after the text */ xw->cursx += len; } /* Insert "qty" characters into the current row, starting at * the current cursor position. A negative "qty" value means * that characters should be deleted. * * This function is optional. If omitted, elvis will rewrite * the text that would have been shifted. */ static BOOLEAN shift(gw, qty, rows) GUIWIN *gw; /* window to be shifted */ int qty; /* amount to shift by */ int rows; /* number of rows affected */ { X11WIN *xw = (X11WIN *)gw; #if 1 /* erase the cursor */ erasecursor(xw); #endif /* make sure we have the right background */ if (!xw->grexpose) { XSetGraphicsExposures(display, xw->gc, True); xw->grexpose = True; } if (xw->bg != xw->bgnormal) { XSetBackground(display, xw->gc, xw->bgnormal); xw->bg = xw->bgnormal; } if (qty > 0) { /* we'll be inserting */ /* shift the characters */ XCopyArea(display, xw->textw, xw->textw, xw->gc, (int)(xw->cursx * xw->cellw), (int)(xw->cursy * xw->cellh), xw->cellw * (xw->columns - xw->cursx - qty), xw->cellh * rows, (int)((xw->cursx + qty) * xw->cellw), (int)(xw->cursy * xw->cellh)); } else { /* we'll be deleting. Convert qty to absolute value. */ qty = -qty; /* shift the characters */ XCopyArea(display, xw->textw, xw->textw, xw->gc, (int)((xw->cursx + qty) * xw->cellw), (int)(xw->cursy * xw->cellh), xw->cellw * (xw->columns - xw->cursx - qty), xw->cellh * rows, (int)(xw->cursx * xw->cellw), (int)(xw->cursy * xw->cellh)); } return True; } static BOOLEAN scroll(gw, qty, notlast) GUIWIN *gw; /* window to be scrolled */ int qty; /* amount to scroll by (pos=downward, neg=upward) */ BOOLEAN notlast;/* if True, last row should not be affected */ { X11WIN *xw = (X11WIN *)gw; int rows; #if 1 /* erase the cursor */ erasecursor(xw); #endif /* decide how many rows to scroll */ rows = xw->rows; if (notlast) { rows--; } /* make sure we have the right background */ if (!xw->grexpose) { XSetGraphicsExposures(display, xw->gc, True); xw->grexpose = True; } if (xw->bg != xw->bgnormal) { XSetBackground(display, xw->gc, xw->bgnormal); xw->bg = xw->bgnormal; } if (qty > 0) { /* we'll be inserting */ /* shift the rows */ XCopyArea(display, xw->textw, xw->textw, xw->gc, 0, (int)(xw->cursy * xw->cellh), xw->cellw * xw->columns, xw->cellh * (rows - xw->cursy - qty), 0, (int)((xw->cursy + qty) * xw->cellh)); } else { /* we'll be deleting. Convert qty to absolute value. */ qty = -qty; /* shift the rows */ XCopyArea(display, xw->textw, xw->textw, xw->gc, 0, (int)((xw->cursy + qty) * xw->cellh), xw->cellw * xw->columns, xw->cellh * (rows - xw->cursy - qty), 0, (int)(xw->cursy * xw->cellh)); } return True; } static BOOLEAN clrtoeol(gw) GUIWIN *gw; /* window whose row is to be cleared */ { X11WIN *xw = (X11WIN *)gw; /* make sure we have the right background */ if (xw->fg != xw->bgnormal) { XSetForeground(display, xw->gc, xw->bgnormal); xw->fg = xw->bgnormal; } if (xw->grexpose) { XSetGraphicsExposures(display, xw->gc, False); xw->grexpose = False; } /* whether or not the cursor was visible before, it'll be invisible * after we erase the line. */ xw->cursor = CURSOR_NONE; /* erase the line, from the cursor to the right edge */ XFillRectangle(display, xw->textw, xw->gc, (int)(xw->cursx * xw->cellw), (int)(xw->cursy * xw->cellh), (xw->columns - xw->cursx) * xw->cellw, xw->cellh); return True; } /* Ring the bell */ static void beep(gw) GUIWIN *gw; /* window that generated a beep */ { XBell(display, 0); } /* draw a bevelled rectangle */ static void bevelrect(xw, win, x, y, w, h, in) X11WIN *xw; /* top-level app window to draw in */ Window win; /* widget where drawing should occur */ int x, y; /* upper-left corner of the rectangle */ unsigned w, h; /* width and height of the rectangle */ BOOLEAN in; /* swap edge colors so it appears pushed in? */ { XGCValues gcv; #if SB_BEVEL >= 1 int b, r; /* bottom & right edges */ unsigned long topleft; unsigned long bottomright; int i; #endif /* draw the button's face */ gcv.foreground = xw->fgscroll; gcv.fill_style = FillSolid; XChangeGC(display, xw->gc, GCForeground|GCFillStyle, &gcv); xw->fg = xw->fgscroll; XFillRectangle(display, win, xw->gc, x, y, w, h); #if SB_BEVEL >= 1 /* choose the edge colors */ if (in) { topleft = xw->fgnormal; bottomright = xw->bgnormal; } else { topleft = xw->bgnormal; bottomright = xw->fgnormal; } /* locate the bottom & right edges */ b = y + h - 1; r = x + w - 1; /* draw the outermost bevel sides */ for (i = SB_BEVEL; i > 0; i--) { /* draw the top & left bevel edges */ XSetForeground(display, xw->gc, topleft); XDrawLine(display, win, xw->gc, x, y, r, y); XDrawLine(display, win, xw->gc, x, y, x, b); /* draw the bottom & right bevel edges */ XSetForeground(display, xw->gc, bottomright); xw->fg = bottomright; XDrawLine(display, win, xw->gc, x, b, r, b); XDrawLine(display, win, xw->gc, r, y, r, b); /* move the edges in slightly, to continue the bevel */ x++; y++; r--; b--; } #endif /* SB_BEVEL >= 2 */ } /* draw the scrollbar */ static void scrollbar(gw, top, bottom, total) GUIWIN *gw; /* window whose scrollbar should be updated */ long top; /* offset of char at top of screen */ long bottom; /* offset of char at bottom of screen */ long total; /* total number of characters in buffer */ { X11WIN *xw = (X11WIN *)gw; int newtop; /* top of thumb */ int newbottom; /* bottom of thumb */ int newheight; /* total height of window */ int newthumb; /* height of thumb scrolling region */ XGCValues gcv; /* GC Values */ XPoint vertex[3]; /* points of arrowhead */ /* compute new scrollbar regions */ newheight = xw->cellh * xw->rows; if (total <= 0) { newtop = xw->sbtop; newbottom = xw->sbbottom; xw->sbstate = SB_REDRAW; } else { if (newheight < 1 + 2 * (o_scrollbarwidth + SB_GAP)) newheight = 1 + 2 * (o_scrollbarwidth + SB_GAP); newthumb = newheight - 2 * (o_scrollbarwidth + SB_GAP); newtop = o_scrollbarwidth + SB_GAP + top * newthumb / total; newbottom = o_scrollbarwidth + SB_GAP + bottom * newthumb / total; /* make sure the thumb doesn't disappear completely */ if (newbottom - newtop < 2) newbottom = newtop + 2; /* make sure the thumb doesn't go outside the scroll area */ if (newbottom >= newheight - (o_scrollbarwidth + SB_GAP)) { newtop -= (newbottom - (newheight - (o_scrollbarwidth + SB_GAP))); newbottom = newheight - (o_scrollbarwidth + SB_GAP); } if (newtop < o_scrollbarwidth + SB_GAP) newtop = o_scrollbarwidth + SB_GAP; } /* if geometries haven't changed, then don't bother redrawing */ if (newheight == xw->sbheight && newtop == xw->sbtop && newbottom == xw->sbbottom && xw->sbstate == SB_NORMAL) { return; } #if 1 /* if not supposed to change, then don't -- but remember shape anyway */ if (xw->sbstate != SB_NORMAL && xw->sbstate != SB_REDRAW) { xw->sbheight = newheight; xw->sbtop = newtop; xw->sbbottom = newbottom; return; } #endif /* choose the erase color */ if (xw->fgscroll == xw->bgscroll) { /* foreground & background are the same - use a fill * pattern. */ gcv.foreground = xw->fgnormal; gcv.background = xw->bgnormal; gcv.fill_style = FillOpaqueStippled; gcv.stipple = elvis_icon; XChangeGC(display, xw->gc, GCForeground|GCBackground|GCFillStyle|GCStipple, &gcv); } else { /* foreground & background are different */ XSetForeground(display, xw->gc, xw->bgscroll); } if (newheight == xw->sbheight && xw->sbstate == SB_NORMAL) { /* erase the old thumb */ XFillRectangle(display, xw->textw, xw->gc, (int)(xw->cellw * xw->columns + SB_GAP), xw->sbtop, (unsigned)(o_scrollbarwidth - 2 * SB_GAP + 1), (unsigned)(xw->sbbottom - xw->sbtop + 1)); } else { /* erase the old scrollbar */ XFillRectangle(display, xw->textw, xw->gc, (int)(xw->cellw * xw->columns), 0, (unsigned)o_scrollbarwidth, xw->cellh * xw->rows); /* redraw the borders */ gcv.foreground = xw->fgnormal; gcv.fill_style = FillSolid; XChangeGC(display, xw->gc, GCForeground|GCFillStyle, &gcv); XDrawLine(display, xw->textw, xw->gc, (int)(xw->cellw * xw->columns), 0, (int)(xw->cellw * xw->columns), newheight - 1); XDrawLine(display, xw->textw, xw->gc, (int)(xw->cellw * xw->columns), 0, (int)(xw->cellw * xw->columns + o_scrollbarwidth - 1), 0); XSetForeground(display, xw->gc, xw->bgnormal); XDrawLine(display, xw->textw, xw->gc, (int)(xw->cellw * xw->columns + o_scrollbarwidth - 1), 0, (int)(xw->cellw * xw->columns + o_scrollbarwidth - 1), newheight - 1); XDrawLine(display, xw->textw, xw->gc, (int)(xw->cellw * xw->columns), newheight - 1, (int)(xw->cellw * xw->columns + o_scrollbarwidth - 1), newheight - 1); /* draw the "up" arrowhead */ vertex[0].x = xw->cellw * xw->columns + SB_GAP; vertex[0].y = o_scrollbarwidth; /* lower left */ vertex[1].x = vertex[0].x + o_scrollbarwidth / 2 - SB_GAP; vertex[1].y = SB_GAP; /* top */ vertex[2].x = vertex[0].x + o_scrollbarwidth - 2 * SB_GAP; vertex[2].y = vertex[0].y; /* lower right */ XSetForeground(display, xw->gc, xw->fgscroll); XFillPolygon(display, xw->textw, xw->gc, vertex, QTY(vertex), Convex, CoordModeOrigin); #if SB_BEVEL >= 2 XSetForeground(display, xw->gc, xw->fgnormal); XDrawLine(display, xw->textw, xw->gc, vertex[1].x, vertex[1].y+1, vertex[2].x-1, vertex[2].y-1); XDrawLine(display, xw->textw, xw->gc, vertex[2].x-1, vertex[2].y-1, vertex[0].x+1, vertex[0].y-1); XSetForeground(display, xw->gc, xw->bgnormal); XDrawLine(display, xw->textw, xw->gc, vertex[0].x+1, vertex[0].y-1, vertex[1].x, vertex[1].y+1); #endif /* SB_BEVEL >= 2 */ #if SB_BEVEL >= 1 XSetForeground(display, xw->gc, xw->fgnormal); XDrawLine(display, xw->textw, xw->gc, vertex[1].x, vertex[1].y, vertex[2].x, vertex[2].y); XDrawLine(display, xw->textw, xw->gc, vertex[2].x, vertex[2].y, vertex[0].x, vertex[0].y); XSetForeground(display, xw->gc, xw->bgnormal); XDrawLine(display, xw->textw, xw->gc, vertex[0].x, vertex[0].y, vertex[1].x, vertex[1].y); #endif /* SB_BEVEL >= 1 */ /* draw the "down" arrowhead */ vertex[0].x = xw->cellw * xw->columns + SB_GAP; /* top left */ vertex[0].y = xw->cellh * xw->rows - o_scrollbarwidth; vertex[1].x = vertex[0].x + o_scrollbarwidth - 2 * SB_GAP; vertex[1].y = vertex[0].y; /* top right */ vertex[2].x = vertex[0].x + o_scrollbarwidth / 2 - SB_GAP; vertex[2].y = xw->cellh * xw->rows - SB_GAP; /* bottom */ XSetForeground(display, xw->gc, xw->fgscroll); XFillPolygon(display, xw->textw, xw->gc, vertex, QTY(vertex), Convex, CoordModeOrigin); #if SB_BEVEL >= 2 XSetForeground(display, xw->gc, xw->fgnormal); XDrawLine(display, xw->textw, xw->gc, vertex[1].x-1, vertex[1].y+1, vertex[2].x, vertex[2].y-1); XSetForeground(display, xw->gc, xw->bgnormal); XDrawLine(display, xw->textw, xw->gc, vertex[0].x+1, vertex[0].y+1, vertex[1].x-1, vertex[1].y+1); XDrawLine(display, xw->textw, xw->gc, vertex[2].x, vertex[2].y-1, vertex[0].x+1, vertex[0].y+1); #endif /* SB_BEVEL >= 2 */ #if SB_BEVEL >= 1 XSetForeground(display, xw->gc, xw->fgnormal); XDrawLine(display, xw->textw, xw->gc, vertex[1].x, vertex[1].y, vertex[2].x, vertex[2].y); XSetForeground(display, xw->gc, xw->bgnormal); XDrawLine(display, xw->textw, xw->gc, vertex[2].x, vertex[2].y, vertex[0].x, vertex[0].y); XDrawLine(display, xw->textw, xw->gc, vertex[0].x, vertex[0].y, vertex[1].x, vertex[1].y); #endif /* SB_BEVEL >= 1 */ } /* draw the thumb */ bevelrect(xw, xw->textw, (int)(xw->cellw * xw->columns + SB_GAP), newtop, (unsigned)(o_scrollbarwidth - 2 * SB_GAP), (unsigned)(newbottom - newtop), False) ; /* store info about the scrollbar */ xw->sbheight = newheight; xw->sbtop = newtop; xw->sbbottom = newbottom; xw->sbstate = SB_NORMAL; xw->fg = xw->fgnormal; } /* change scrollbar state. This is used to blank out the scrollbar or make * it show a stop-sign. */ static void setsbstate(xw, newstate) X11WIN *xw; /* the window whose state should be changed */ SBSTATE newstate; /* the state is should be changed to */ { XPoint vertex[9]; /* corners of a stop sign */ int i; /* if no change needed, then do nothing */ if (xw->sbstate == newstate) return; /* else switch to the requested state */ if (newstate == SB_NORMAL) { scrollbar((GUIWIN *)xw, 0, 0, 0); } else { /* blank the scrollbar area */ XSetForeground(display, xw->gc, xw->bgscroll); xw->fg = xw->bgscroll; XFillRectangle(display, xw->textw, xw->gc, (int)(xw->cellw * xw->columns + 1), 1, (unsigned)o_scrollbarwidth - 2, (unsigned)(xw->cellh * xw->rows) - 2); /* if supposed to draw a little Stop sign, then do that. */ if (newstate == SB_STOP) { /* compute vertices of stop sign */ vertex[0].x = -(o_scrollbarwidth - SB_GAP) * 5/24; vertex[0].y = (o_scrollbarwidth - SB_GAP) / 2 - 1; vertex[1].x = -vertex[0].y; vertex[1].y = -vertex[0].x; vertex[2].x = -vertex[0].y; vertex[2].y = vertex[0].x; vertex[3].x = vertex[0].x; vertex[3].y = -vertex[0].y; for (i = 4; i < 9; i++) { vertex[i].x = -vertex[i - 4].x; vertex[i].y = -vertex[i - 4].y; } for (i = 0; i < 9; i++) { vertex[i].x += o_scrollbarwidth / 2 + xw->cellw * xw->columns; vertex[i].y += (xw->cellh * xw->rows) / 2; } /* draw the stop sign */ XSetForeground(display, xw->gc, xw->fgcursor); XFillPolygon(display, xw->textw, xw->gc, vertex, 8, Convex, CoordModeOrigin); XSetForeground(display, xw->gc, xw->bgnormal); XDrawLines(display, xw->textw, xw->gc, vertex, 5, CoordModeOrigin); XSetForeground(display, xw->gc, xw->fgnormal); XDrawLines(display, xw->textw, xw->gc, vertex + 4, 5, CoordModeOrigin); xw->fg = xw->fgnormal; /* We only draw the stop sign when we expect elvis to * be busy for a long time, not receiving new events. * We should flush the X queue so the stop sign will * be drawn immediately. */ XFlush(display); } } /* we've changed the state */ xw->sbstate = newstate; } /* draw a single toolbar button */ static void draw1tool(xw, tool, newstate) X11WIN *xw; /* window where tool will be drawn */ TOOL *tool; /* the tool to draw */ _char_ newstate;/* appearance the button should have */ { int h; /* if new state is same as old state, do nothing */ if (newstate == xw->toolstate[tool->id]) return; /* compute the button height */ h = loadedcontrol->height + 2 * SB_GAP; /* draw the button's background and bevel */ if (newstate == 'f') { /* draw the button in the window's background color */ XClearArea(display, xw->toolw, tool->x, 1, (unsigned)tool->width, (unsigned)h, False); XSetBackground(display, xw->gc, xw->bgscroll); xw->bg = xw->bgscroll; } else { /* draw the button's face in the scrollbar foreground * color, with bevelled edges. */ bevelrect(xw, xw->toolw, tool->x, 1, (unsigned)tool->width, (unsigned)h, (BOOLEAN)(newstate == 'i')); } /* draw the button's label */ XSetForeground(display, xw->gc, xw->fgnormal); XSetFont(display, xw->gc, loadedcontrol->fontinfo->fid); XDrawString(display, xw->toolw, xw->gc, tool->x + tool->textx, tool->texty, tool->label, strlen(tool->label)); xw->fg = xw->fgnormal; /* remember this button's drawn state */ xw->toolstate[tool->id] = newstate; } /* draw a window's toolbar */ static void drawtoolbar(xw, fromscratch) X11WIN *xw; /* window whose toolbar should be drawn */ BOOLEAN fromscratch; /* redraw background too? */ { TOOL *tool, *lag; char newstate; CHAR *str; int dummy; XCharStruct size; /* if no toolbar is visible, then don't bother */ if (!o_toolbar) { return; } /* supposed to draw from scratch? */ if (fromscratch) { /* draw the background */ XClearWindow(display, xw->toolw); /* reset the states for all buttons */ memset(xw->toolstate, 0, sizeof xw->toolstate); } /* make sure this window is the "current" window. Otherwise the * calculate() calls below will be looking at the wrong window's * options. */ (void)eventfocus((GUIWIN *)xw); /* for each button... */ for (lag = NULL, tool = toollist; tool; lag = tool, tool = tool->next) { /* if new button, then assign it a position & size */ if (tool->width == 0) { XTextExtents(loadedcontrol->fontinfo, tool->label, strlen(tool->label), &dummy, &dummy, &dummy, &size); tool->width = size.rbearing - size.lbearing + 2 * SB_BEVEL + 2; tool->textx = -size.lbearing + SB_BEVEL + 1; tool->texty = loadedcontrol->fontinfo->ascent + SB_BEVEL + 1; if (lag) tool->x += lag->x + lag->width + SB_GAP; else tool->x = SB_GAP / 2; } /* compute the button's new state */ newstate = '\0'; if (xw->sbstate != SB_NORMAL || tool->excmd == NULL) { newstate = 'f'; /* flat, inactive */ } else if (tool->when) { str = calculate(tool->when, NULL, False); if (!str) { /* error - disable the button semi-permanently */ safefree(tool->when); str = toCHAR("0"); tool->when = CHARdup(str); } if (!calctrue(str)) newstate = 'f'; /* flat, inactive */ } if (newstate != 'f' && !tool->in) { newstate = 'o'; /* out, active */ } else if (newstate != 'f') { str = calculate(tool->in, NULL, False); if (!str) { /* error - forget condition, always display out */ safefree(tool->in); tool->in = NULL; newstate = 'o'; /* out, active */ } else if (calctrue(str)) newstate = 'i'; /* in, inactive */ else newstate = 'o'; /* out, active */ } /* update the button's appearance */ draw1tool(xw, tool, newstate); } } /* maintain the list of toolbar buttons */ static BOOLEAN guicmd(gw, extra) GUIWIN *gw; /* window where command was typed (ignored) */ char *extra; /* label, operator, and command/condition */ { TOOL *scan, *lag; char *label, *end; char operator; int i; X11WIN *xw; static gap; /* Parse the label and operator. Leave extra pointing to command/condition */ for (label = extra; *extra && (*extra == *label || isalnum(*extra)); extra++) { } end = extra; while (isspace(*extra)) { extra++; } if (*extra != ':' && *extra != '=' && *extra != '?') { /* maybe a special word? */ if (!strcmp(label, "gap")) { gap += 6; return True; } else if (!strcmp(label, "newtoolbar")) { /* delete all tools */ while (toollist) { lag = toollist->next; safefree(toollist->label); if (toollist->excmd) safefree(toollist->excmd); if (toollist->when) safefree(toollist->when); if (toollist->in) safefree(toollist->in); safefree(toollist); toollist = lag; } /* redraw toolbar from scratch, on all windows */ for (xw = winlist; xw; xw = xw->next) { drawtoolbar(xw, True); } return True; } return False; } operator = *extra++; *end = '\0'; while (isspace(*extra)) { extra++; } /* search for the button. If not found, then create it. */ for (lag = NULL, scan = toollist, i = 0; scan && strcmp(scan->label, label); lag = scan, scan = scan->next, i++) { } if (!scan) { scan = (TOOL *)safealloc(1, sizeof(TOOL)); scan->label = safedup(label); scan->safer = o_safer; scan->id = i; scan->x = gap; gap = 0; if (lag) lag->next = scan; else toollist = scan; } /* process the operator */ switch (operator) { case ':': if (scan->excmd) safefree(scan->excmd); scan->excmd = (*extra ? safedup(extra) : NULL); break; case '?': if (scan->when) safefree(scan->when); scan->when = (*extra ? CHARdup(toCHAR(extra)) : NULL); break; case '=': if (scan->in) safefree(scan->in); scan->in = (*extra ? CHARdup(toCHAR(extra)) : NULL); break; } return True; } /* Translate keylabels into raw codes, or vice versa. Returns length of raw * codes if successful, or 0 if unrecognized text. */ static int keylabel(given, givenlen, label, rawin) CHAR *given; /* the string typed in by user */ int givenlen; /* length of the user's string */ CHAR **label; /* pointer to (CHAR *) to set to key label */ CHAR **rawin; /* pointer to (CHAR *) to set to raw codes */ { static CHAR rawbuf[10]; /* buffer, holds raw byte string */ CHAR lblbuf[20]; /* buffer, holds label string */ char *name; KeySym key; int i; /* no single-character string can be a key label */ if (givenlen < 2) return 0; /* could this be the raw codes of a key? */ if (givenlen == 5 && *given == ELVCTRL('K')) { /* convert key value into a KeySym */ for (i = 1, key = 0; i <= 4; i++) { key <<= 4; if (isdigit(given[i])) { key += given[i] - '0'; } else if (isxdigit(given[i])) { key += (given[i] & 0xf) + 9; } else { return 0; } } /* See if the KeySym has a name */ name = XKeysymToString(key); if (!name) { return 0; } goto Found; } /* Maybe it is a label in foo or <foo> format? */ if (given[0] == '<' && given[givenlen - 1] == '>' && givenlen < QTY(lblbuf)-1) { /* Convert <foo> name to foo */ CHARncpy(lblbuf, given, (size_t)givenlen); givenlen -= 2; } else if (given[0] == '#' && givenlen < QTY(lblbuf) - 2) { /* standardize the format of the #nn string */ lblbuf[1] = 'F'; CHARncpy(lblbuf + 2, given + 1, (size_t)givenlen); } else if (givenlen < QTY(lblbuf)-3) { /* standardize the format of the foo string */ CHARncpy(lblbuf + 1, given, (size_t)givenlen); } else { /* too long to be a key label */ return 0; } /* convert label to KeySym */ lblbuf[givenlen + 1] = '\0'; name = tochar8(lblbuf + 1); key = XStringToKeysym(name); if (key == NoSymbol) { return 0; } Found: /* We have a key! At this point, "key" and "name" are the only * variables we can trust. */ /* if function key, then convert label to #n format (else <foo>) */ if (key >= XK_F1 && key <= XK_F10) { sprintf((char *)lblbuf, "#%ld", (long)(key - XK_F1 + 1)); } else { lblbuf[0] = '<'; CHARcpy(&lblbuf[1], name); CHARcat(lblbuf, toCHAR(">")); } /* convert the KeySym into raw code, and return it. */ sprintf((char *)rawbuf, "%c%04lx", ELVCTRL('K'), (long)key); *label = CHARdup(lblbuf); *rawin = rawbuf; return CHARlen(rawbuf); } static BOOLEAN color(gw, font, fg, bg) GUIWIN *gw; /* window whose colors should be changed */ _char_ font; /* font letter of font to change */ CHAR *fg; /* name of new foreground color */ CHAR *bg; /* background color for whole window */ { X11WIN *xw = (X11WIN *)gw; unsigned long *fgp, *bgp; /* pointers to colors to change */ CHAR *fname, *bname; /* pointers to color names */ /* decide which colors we should change */ bgp = &xw->bgnormal; bname = background; switch (font) { case 'b': fgp = &xw->fgbold, fname = boldcolor; break; case 'e': fgp = &xw->fgemph, fname = emphcolor; break; case 'u': fgp = &xw->fgundln, fname = underlinecolor; break; case 'i': fgp = &xw->fgitalic, fname = italiccolor; break; case 'c': fgp = &xw->fgcursor, fname = cursorcolor; bgp = &xw->owncursor, bname = owncolor; break; case 's': fgp = &xw->fgscroll, fname = scrollbarfg; bgp = &xw->bgscroll, bname = scrollbarbg; break; case 'f': fgp = &xw->fgfixed, fname = fixedcolor; break; default: fgp = &xw->fgnormal, fname = foreground; break; } /* Change the foreground colors */ if (fg) { if (xw) { unloadcolor(*fgp); *fgp = loadcolor(fg, (font != 's') ? black : white); } CHARcpy(fname, fg); } /* Change the background colors */ if (bg) { if (xw) { unloadcolor(*bgp); if (font == 's') { /* scrollbar and toolbar */ *bgp = loadcolor(bg, black); XSetWindowBackground(display, xw->toolw, xw->bgscroll); } else { /* text */ *bgp = loadcolor(bg, white); XSetWindowBackground(display, xw->textw, xw->bgnormal); } } CHARcpy(bname, bg); } /* We'll probably need to redraw the scrollbar. Since any :color * command causes the screen to be redrawn, all we need to do is * bypass the scrollbar's optimization. * * Additionally, if the toolbar is shown then it should be redrawn */ if (xw) { xw->sbheight = 0; drawtoolbar(xw, True); } return True; } /*----------------------------------------------------------------------------*/ /* open an X cut buffer for reading or writing. Returns True if successful */ static BOOLEAN clipopen(forwrite) BOOLEAN forwrite; /* True for writing, False for reading */ { XEvent event; Atom gottype; long extra; int i; /* free the old clipbuf, if there was one (except when reading from * own own selection). */ if (clipbuf && (forwrite || !ownselection)) { safefree(clipbuf); clipbuf = NULL; } if (forwrite) { /* prepare to collect bytes as clipwrite() gets called */ clipwriting = True; clipsize = 0; } else { /* does elvis own the selection? */ if (ownselection) { /* yes -- clipbuf already contains it */ } else { /* no -- try to fetch text from X two ways... */ /* is there a selection owner? */ if (XGetSelectionOwner(display, XA_PRIMARY) == None) { /* don't bother to try getting selection */ event.type = ButtonPress; } else { /* Try to fetch the selection */ XConvertSelection(display, XA_PRIMARY, XA_STRING, elvis_cutbuffer, ((X11WIN *)windefault->gw)->window, now); /* Wait for the selection to arrive, or for a * button press. Eat any other events. (The * button press event gives the user a way to * abort the operation.) */ do { XNextEvent(display, &event); } while (event.type != SelectionNotify && event.type != ButtonPress); } /* did we succeed in fetching the selection? */ if (event.type == SelectionNotify && event.xselection.property != None) { /* Yes -- fetch bytes from X property */ XGetWindowProperty(display, event.xselection.requestor, event.xselection.property, 0L, 65536L, True, event.xselection.target, &gottype, &i, (unsigned long *)&clipsize, (unsigned long *)&extra, (unsigned char **)&clipbuf); } else { /* No -- fetch bytes from X cut buffer */ clipbuf = XFetchBytes(display, &i); clipsize = i; if (!clipbuf) { return False; } } } /* prepare to receive bytes as clipread() gets called */ clipwriting = False; clipused = 0; } return True; } /* add text to the buffer */ static int clipwrite(text, len) CHAR *text; /* pointer to buffer containing some bytes */ int len; /* number of bytes to add */ { char *newp; assert(clipwriting); /* combine old text (if any) with new text in a malloc'ed buffer */ newp = safealloc(len + clipsize, sizeof(char)); if (clipsize > 0) { memcpy(newp, clipbuf, (size_t)clipsize); safefree(clipbuf); } clipbuf = newp; memcpy(clipbuf + clipsize, text, (size_t)len); clipsize += len; return len; } /* extract text from the buffer */ static int clipread(text, len) CHAR *text; /* pointer to buffer where bytes should go */ int len; /* number of bytes to read this time */ { assert(!clipwriting); if (!clipbuf || clipused >= clipsize) { /* everything already sent; return 0 */ return 0; } else if (clipused + len >= clipsize) { /* if everything fits, return everything */ len = clipsize - clipused; } memcpy(text, clipbuf, (size_t)len); clipused += len; return len; } /* end the cut/paste operation */ static void clipclose() { if (clipwriting) { /* send bytes to the X server */ XStoreBytes(display, clipbuf ? clipbuf : "", clipsize); /* claim ownership of the selection */ XSetSelectionOwner(display, XA_PRIMARY, ((X11WIN *)windefault->gw)->window, now); /* was the claim successful? */ if (XGetSelectionOwner(display, XA_PRIMARY) == ((X11WIN *)windefault->gw)->window) { ownselection = True; drawcursor(NULL); } else { /* elvis doesn't own the selection */ ownselection = False; drawcursor(NULL); safefree(clipbuf); clipbuf = NULL; } } else { if (!ownselection) { /* free Xlib's copy of the cut buffer */ XFree(clipbuf); clipbuf = NULL; } } } /*----------------------------------------------------------------------------*/ /* This function starts an interactive shell. It is called with the argument * (True) for the :sh command, or (False) for a :stop or :suspend command. * If successful it returns RESULT_COMPLETE after the shell exits; if * unsuccessful it issues an error message and returns RESULT_ERROR. It * could also return RESULT_MORE to defer processing to the portable code * in ex_suspend(). */ static RESULT stop(alwaysfork) BOOLEAN alwaysfork; /* ignored; X11 always forks anyway */ { /* save the buffers, if we're supposed to */ eventsuspend(); /* start an xterm with a shell in it */ system(o_stopshell ? tochar8(o_stopshell) : "xterm &"); return RESULT_COMPLETE; } /*----------------------------------------------------------------------------*/ /* NOTE: The X11 headers #define True and False for their own purposes, but now * we need use elvis' enum versions of them. This probably isn't important, * really, since both sets of symbols use the same value, but some compilers * will complain if "False" becomes "0" in the initializer of the guix11 struct * and we want to keep compilers happy. */ #ifdef True # undef True # undef False #endif GUI guix11 = { "x11", /* name */ "Simple X11 graphic interface", False, /* exonly */ False, /* newblank */ True, /* minimizeclr */ False, /* scrolllast */ True, /* shiftrows */ 0, /* movecost */ 0, /* nopts */ NULL, /* optdescs */ test, init, usage, loop, wpoll, term, creategw, destroygw, focusgw, retitle, NULL, /* reset */ flush, moveto, draw, shift, scroll, clrtoeol, NULL, /* newline */ beep, NULL, /* msg */ scrollbar, NULL, /* status */ keylabel, clipopen, clipwrite, clipread, clipclose, color, /* color */ guicmd, /* guicmd */ NULL, /* tabcmd */ NULL, /* save */ NULL, /* wildcard */ NULL, /* prgopen */ NULL, /* prgclose */ stop }; #endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.