/* * xcb: Copyright (C) 1992,1993,1994 by Farrell McKay. * XView modifications provided by Danny Vanderryn. * mb/utf8 support by Marc Lehmann . * * Simple X interface to the cut buffers in an X server. * The program creates a window subdivided into a number of subwindows, * one per cut buffer. The user may copy cut buffer contents around * using mouse buttons 1 and 2, or rotate the buffers using mouse * button 3. Buffers may be cleared by using Shift-button 2. * * Note that this program assumes the cut buffers contain textual * information, and displays buffer contents using the XDrawString * function. It is not suitable for use in any other environment. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appears in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. This software is provided "as is" without express or * implied warranty. */ /* * The Xutf8*-API seems to just add complexity for each and every code author * without providing any benefits (such as better font handling). To the * contrary, xfree86 seems to go into the (wrong) direction of "unicode fonts", * thereby forcing the user to accept their glyph style. Why. Why. Why??? */ #if !defined(ATHENA) && !defined(MOTIF) Bong ! Start again. You must define either the 'ATHENA' or 'MOTIF' symbol when compiling. #endif #include #include #include /* for strcmp() */ #ifdef __STDC__ #include /* for exit()... */ #endif #ifndef VMS #include /* for read(), write() */ #endif #include /* for pre-defined atom names */ #include /* for XtNforeground et.al. */ #include #ifdef ATHENA #ifndef VMS #include #else #include #endif #endif #ifdef MOTIF #include #include #endif #include "cb.h" #include "patchlevel.h" #define eq(a,b) (strcmp((a),(b)) == 0) #define max(a,b) ((a) > (b)? (a): (b)) #define XtNbufferCount "bufferCount" /* Application resources */ #define XtCBufferCount "BufferCount" #define XtNlayout "layout" #define XtCLayout "Layout" #define PGM_NAME "xcb" #define PGM_CLASS "Xcb" #define BUFINC 2048 static Display *dpy; static Window root; static XtAppContext app; static Widget top, box, *wdg; static Atom xa_compound_text, xa_utf8_string, xa_text; static Atom delwin; static Atom *atom; static int natoms, nbuffs; static Window seln; static int use_utf8 = 0; static Atom convert_to; /* convert selection to this cut buffer */ #ifdef XVIEW static Atom caret, clipboard, yield; static Atom length, lengthc; #endif #ifndef X_HAVE_UTF8_STRING #define Xutf8TextPropertyToTextList XmbTextPropertyToTextList #define Xutf8TextListToTextProperty XmbTextListToTextProperty #endif /* * Fetch the contents of cut buffer n from the root window. */ static char * fetch_buffer (a, nb, force_mb) Atom a; int *nb; int force_mb; { unsigned long after; char **list; char *data; int count; XTextProperty pt; *nb = 0; if (XGetWindowProperty (dpy, root, a, 0L, 10000000L, False, AnyPropertyType, &pt.encoding, &pt.format, &pt.nitems, &after, &pt.value) != Success || !pt.nitems) return strdup (""); if (pt.nitems) { (force_mb ? XmbTextPropertyToTextList : Xutf8TextPropertyToTextList) (dpy, &pt, &list, &count); data = strdup (list[0]); *nb = strlen (data); XFreeStringList (list); XFree (pt.value); } else data = strdup (""); return data; } /* * Store the string p into cut buffer n on the root window. */ static void store_buffer (p, nb, atom, force_mb) char *p; int nb; Atom atom; int force_mb; { XTextProperty pt; (force_mb ? XmbTextListToTextProperty : Xutf8TextListToTextProperty) (dpy, &p, 1, XStdICCTextStyle, &pt); XChangeProperty (dpy, root, atom, pt.encoding, 8, PropModeReplace, pt.value, pt.nitems); XFree (pt.value); } /* * Add an atom to the program's atom cache. */ static Atom get_atom (n, ifexists) int n, ifexists; { char tmp[32]; if (n >= natoms) { atom = (Atom *) XtRealloc ((char *) atom, (n + 1) * sizeof (Atom)); while (natoms < n + 1) atom[natoms++] = 0; } if (!atom[n]) { sprintf (tmp, "CUT_BUFFER%d", n); atom[n] = XInternAtom (dpy, tmp, (Bool) ifexists); } return atom[n]; } static void initialize_properties (void) { int i; for (i = nbuffs - 1; i >= 0; i--) { Atom a; int nbytes; char *data; a = get_atom (i, False); data = fetch_buffer (a, &nbytes, 0); store_buffer (data, nbytes, a, 0); free (data); } } /* * Draw a string in the window with top-left corner justification. */ static void place_text (cb, str, len, y) CbWidget cb; char *str; int len, y; { int cols; GC gc; gc = (cb->core.window == seln) ? cb->gc_inv : cb->gc; if (y <= (int) cb->core.height) { /* we rely on clipping... */ /*cols = ((int) cb->core.width + cb->font_width - 1) / cb->font_width; */ /*len = min(len, cols); */ if (len > 0) { y -= cb->font_ascent; #if X_HAVE_UTF8_STRING Xutf8DrawImageString #else XmbDrawImageString #endif (dpy, cb->core.window, cb->fontset, gc, 0, y, str, len); } } } /* * ============================================================================ * The following collection of functions and data structures define * the cb widget. Each cb widget displays a single cut buffer value * in a window, and provides cut and paste access to that buffer. */ static void cb_initialize (req, wdg, args, nargs) /*ARGSUSED */ Widget req, wdg; ArgList args; Cardinal *nargs; { CbWidget cb = (CbWidget) wdg; XFontSetExtents *xfe = XExtentsOfFontSet (cb->fontset); cb->font_width = xfe->max_logical_extent.width; cb->font_height = xfe->max_logical_extent.height; cb->font_ascent = xfe->max_logical_extent.y; cb->gc = 0; cb->gc_inv = 0; } static void cb_realize (wdg, mask, attrs) Widget wdg; XtValueMask *mask; XSetWindowAttributes *attrs; { CbWidget cb = (CbWidget) wdg; XtGCMask v_mask = 0L; XGCValues values; XtCreateWindow (wdg, InputOutput, CopyFromParent, *mask, attrs); XStoreName (dpy, cb->core.window, cb->core.name); values.foreground = cb->fgnd; values.background = cb->core.background_pixel; values.plane_mask = AllPlanes; v_mask = GCForeground | GCBackground | GCPlaneMask; cb->gc = XtGetGC (wdg, v_mask, &values); values.foreground = cb->core.background_pixel; values.background = cb->fgnd; cb->gc_inv = XtGetGC (wdg, v_mask, &values); } /* * Redraw the contents of one of the subwindows. * The function assumes the cut buffer contains text data, and parses * it accordingly. The data is split into lines at each '\n' character. * Lines which extend beyond the subwindow's borders are clipped; no * wrap-around processing is done. * Keep it simple. */ static void cb_redisplay (wdg, event, region) /*ARGSUSED */ Widget wdg; XEvent *event; Region region; { CbWidget cb = (CbWidget) wdg; char *p, *pp, *base; int y, nbytes; y = 0; p = pp = base = fetch_buffer (cb->atom, &nbytes, 0); while (pp < base + nbytes) { if (*pp == '\n') { place_text (cb, p, pp - p, y); p = pp + 1; y += cb->font_height; } pp++; } place_text (cb, p, pp - p, y); XFree (base); } static void cb_destroy (wdg) Widget wdg; { CbWidget cb = (CbWidget) wdg; XtReleaseGC (wdg, cb->gc); XtReleaseGC (wdg, cb->gc_inv); } /* * Make this widget the owner of the PRIMARY selection. * The window contents are then redrawn with highlighting. * * It seems that if a XSetSelectionOwner is performed on a client's * window, no SelectionClear event is generated if another window * within the same client is already the selection owner. * This function originally attempted to avoid that problem and force * a SelectionClear event by firstly setting the selection ownership * to None and then setting it to this widget's window. Apparently * that didn't work for all X servers. * * Therefore the function must do the SelectionClear logic itself, * which means it must know when another xcb widget has selection * ownership, which brings about the need for the 'seln' global variable. * This breaks all the rules for object oriented widgets. Disgusting, no? */ static void cb_cut (wdg, event, parms, nparms) /*ARGSUSED */ Widget wdg; XEvent *event; String *parms; Cardinal *nparms; { CbWidget cb = (CbWidget) wdg; Window win = cb->core.window; Window w, tmp; if (win == seln) return; XSetSelectionOwner (dpy, XA_PRIMARY, win, event->xbutton.time); w = XGetSelectionOwner (dpy, XA_PRIMARY); if (seln && w != seln) { tmp = seln; seln = 0; XClearArea (dpy, tmp, 0, 0, 0, 0, False); cb_redisplay (XtWindowToWidget (dpy, tmp), (XEvent *) 0, (Region) 0); } if (w == win) { seln = win; XClearArea (dpy, win, 0, 0, 0, 0, False); cb_redisplay (wdg, (XEvent *) 0, (Region) 0); #ifdef XVIEW XSetSelectionOwner (dpy, caret, win, event->xbutton.time); XSetSelectionOwner (dpy, clipboard, win, event->xbutton.time); #endif } } static void cb_paste (wdg, event, parms, nparms) /*ARGSUSED */ Widget wdg; XEvent *event; String *parms; Cardinal *nparms; { CbWidget cb = (CbWidget) wdg; Window w; char *ptr; int n; w = XGetSelectionOwner (dpy, XA_PRIMARY); if (w == None) { ptr = fetch_buffer (atom[0], &n, 0); /* copy from cb0 */ store_buffer (ptr, n, cb->atom, 0); XFree (ptr); } else if (w != cb->core.window) XConvertSelection (dpy, XA_PRIMARY, xa_utf8_string, convert_to = cb->atom, XtWindow(wdg), event->xbutton.time); } static void cb_clear (wdg, event, parms, nparms) /*ARGSUSED */ Widget wdg; XEvent *event; String *parms; Cardinal *nparms; { CbWidget cb = (CbWidget) wdg; Window win = cb->core.window; store_buffer ("", 0, cb->atom, 0); if (win == seln) { seln = 0; XSetSelectionOwner (dpy, XA_PRIMARY, None, event->xbutton.time); } } static void cb_rotate (wdg, event, parms, nparms) /*ARGSUSED */ Widget wdg; XEvent *event; String *parms; Cardinal *nparms; { int n = 0; if (*nparms > 0) n = atoi (parms[0]); if (n != 0) XRotateWindowProperties (dpy, root, atom, nbuffs, n); } static void cb_quit (wdg, event, parms, nparms) /*ARGSUSED */ Widget wdg; XEvent *event; String *parms; Cardinal *nparms; { exit (0); } /* * Clear and redraw the widget's window. */ static void cb_refresh (wdg, event, parms, nparms) /*ARGSUSED */ Widget wdg; XEvent *event; String *parms; Cardinal *nparms; { XClearArea (dpy, wdg->core.window, 0, 0, 0, 0, False); cb_redisplay (wdg, (XEvent *) 0, (Region) 0); } /* * Someone or something wants a copy of the current PRIMARY selection. * Such a request is only satisfied if the target type is STRING. * (No conversion facilities are provided by this program). * The selection request is met by copying the current contents * of the cut buffer to the target window+atom. */ static void cb_selreq (wdg, event, parms, nparms) /*ARGSUSED */ Widget wdg; XEvent *event; String *parms; Cardinal *nparms; { int nbytes; char *ptr; XSelectionEvent notify; XSelectionRequestEvent *rq; CbWidget cb = (CbWidget) wdg; Window win = cb->core.window; #ifdef XVIEW unsigned long data; #endif rq = (XSelectionRequestEvent *) event; notify.type = SelectionNotify; notify.display = rq->display; notify.requestor = rq->requestor; notify.selection = rq->selection; notify.target = rq->target; notify.property = None; notify.time = rq->time; #ifdef XVIEW if (rq->selection == XA_PRIMARY) { if (rq->target == yield) { /* tell 'em we'll give it up */ data = 1; XChangeProperty (dpy, rq->requestor, rq->property, rq->target, 32, PropModeReplace, (unsigned char *) &data, 1); notify.property = rq->property; } else { #endif if (win == seln) { XICCEncodingStyle style; Atom target = None; if (rq->target == XA_STRING) { target = XA_STRING; style = XStringStyle; } else if (rq->target == xa_text) { target = xa_compound_text; style = XStdICCTextStyle; } else if (rq->target == xa_compound_text) { target = xa_compound_text; style = XCompoundTextStyle; } #ifdef X_HAVE_UTF8_STRING else if (rq->target == xa_utf8_string) { target = xa_utf8_string; style = XUTF8StringStyle; } #endif if (target != None) { XTextProperty pt; char *fl; fl = fetch_buffer (cb->atom, &nbytes, 0); Xutf8TextListToTextProperty (dpy, &fl, 1, style, &pt); XFree (fl); XChangeProperty (dpy, rq->requestor, rq->property, pt.encoding, 8, PropModeReplace, pt.value, pt.nitems); notify.property = rq->property; XFree (pt.value); } } #ifdef XVIEW } } else if (rq->selection == caret) { if (rq->target == yield) { /* * Give up the caret (which meant that we * own the clipboard) */ XSetSelectionOwner (dpy, caret, None, event->xselectionrequest.time); data = 1; XChangeProperty (dpy, rq->requestor, rq->property, rq->target, 32, PropModeReplace, (unsigned char *) &data, 1); notify.property = rq->property; } } else if (rq->selection == clipboard && win == seln) { ptr = fetch_buffer (cb->atom, &nbytes, 0); if (rq->target == lengthc || rq->target == length) { /* * Send the length of the selection. */ data = nbytes; XChangeProperty (dpy, rq->requestor, rq->property, rq->target, 32, PropModeReplace, (unsigned char *) &data, 1); notify.property = rq->property; } else if (rq->target == XA_STRING || rq->target == xa_text || rq->target == xa_compound_text) { /* * Send the selection itself. * All of our selections will be XA_STRING, * but if they ask for COMPOUND_TEXT, it's ok * to say that that's what we've got... */ XChangeProperty (dpy, rq->requestor, rq->property, (rq->target == xa_compound_text ? xa_compound_text : XA_STRING), 8, PropModeReplace, ptr, nbytes); notify.property = rq->property; } XFree (ptr); } #endif XSendEvent (dpy, rq->requestor, False, 0, (XEvent *) & notify); } /* * Boo hiss, someone has taken the PRIMARY selection ownership * away from this widget. The current window contents must * be redrawn without highlighting. */ static void cb_selclear (wdg, event, parms, nparms) /*ARGSUSED */ Widget wdg; XEvent *event; String *parms; Cardinal *nparms; { CbWidget cb = (CbWidget) wdg; #ifdef XVIEW Window w; Time t; #endif if (event->xproperty.atom != XA_PRIMARY) return; seln = 0; XClearArea (dpy, cb->core.window, 0, 0, 0, 0, False); cb_redisplay (wdg, (XEvent *) 0, (Region) 0); #ifdef XVIEW /* * Since we don't have ownership of PRIMARY anymore, * we'd better get rid of CLIPBOARD and _SUN_SELN_CARET, * if they're still ours. */ t = event->xselectionclear.time; w = XGetSelectionOwner (dpy, caret); if (w == cb->core.window) XSetSelectionOwner (dpy, caret, None, t); w = XGetSelectionOwner (dpy, clipboard); if (w == cb->core.window) XSetSelectionOwner (dpy, clipboard, None, t); #endif } static XtResource resources[] = { #define offset(field) XtOffset(CbWidget, field) /* {name, class, type, size, offset, default_type, default_addr}, */ {XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel), offset (fgnd), XtRString, (XtPointer) "XtDefaultForeground"}, {XtNfontSet, XtCFontSet, XtRFontSet, sizeof (XFontSet), offset (fontset), XtRString, (XtPointer) "XtDefaultFontSet"}, {XtNatom, XtCAtom, XtRAtom, sizeof (Atom), /* internal use */ offset (atom), XtRImmediate, (XtPointer) 0}, #undef offset }; static XtActionsRec actions[] = { {"cut", cb_cut}, {"paste", cb_paste}, {"clear", cb_clear}, {"rotate", cb_rotate}, {"quit", cb_quit}, {"refresh", cb_refresh}, {"selreq", cb_selreq}, {"selclear", cb_selclear}, }; static char cb_transl[] = "\ : cut() \n\ Shift : clear() \n\ : paste() \n\ Shift : rotate(-1) \n\ : rotate(1) \n\ Left: rotate(-1) \n\ Right: rotate(1) \n\ Up: rotate(-1) \n\ Down: rotate(1) \n\ q: quit() \n\ : selreq() \n\ : selclear() \n\ "; CbClassRec cbClassRec = { { (WidgetClass) & widgetClassRec, /* superclass */ "Buffer", /* class_name */ sizeof (CbRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize */ FALSE, /* class_inited */ cb_initialize, /* initialize */ NULL, /* initialize_hook */ cb_realize, /* realize */ actions, /* actions */ XtNumber (actions), /* num_actions */ resources, /* resources */ XtNumber (resources), /* num_resources */ NULLQUARK, /* xrm_class */ TRUE, /* compress_motion */ TRUE, /* compress_exposure */ TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ cb_destroy, /* destroy */ NULL, /* resize */ cb_redisplay, /* expose */ NULL, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ cb_transl, /* tm_table */ XtInheritQueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, }; WidgetClass cbWidgetClass = (WidgetClass) & cbClassRec; /* * Here endeth the section concerned with the cb widget. * Normal viewing shall now be resumed. * ============================================================================ */ static void usage () { fprintf (stderr, "Usage: %s [Xt option] [-l layout] [-n count] [-p|-s|-S list] [-r count]\n", PGM_NAME); exit (1); } /* * Gracefully exit after an XIO error, or after a "delete window" * directive from the window manager. * xioerror() avoids messy error messages sometimes seen from xterm * or in the xdm-errors file when forcibly destroying the client program. */ static int xioerror (d) /*ARGSUSED */ Display *d; { exit (1); /*NOTREACHED */ } static void wmdel (wdg, ptr, ep, cont) /*ARGSUSED */ Widget wdg; XtPointer ptr; XEvent *ep; Boolean *cont; { if (ep->type == ClientMessage && ep->xclient.data.l[0] == delwin) exit (0); } /* * Print the contents of a cut buffer on stdout. */ static void doprint (n, ptr, nb) int n; char *ptr; int nb; { Atom a; a = get_atom (n, True); if (a) { ptr = fetch_buffer (a, &nb, !use_utf8); if (write (1, ptr, nb) != nb) { fprintf (stderr, "Write error\n"); exit (1); } XFree (ptr); } } /* * Load a new value into one of the cut buffers. */ static void doset (n, ptr, nb) int n; char *ptr; int nb; { char *str = malloc (nb + 1); memcpy (str, ptr, nb); str[nb] = 0; store_buffer (str, nb, get_atom (n, False), !use_utf8); free (str); } static void timeout (arg, id) char *arg; XtIntervalId *id; { exit (2); } /* * Copy the PRIMARY selection into a cut buffer. */ static void dogetseln (n, ptr, nb) int n; char *ptr; int nb; { char *data; int nbytes; Atom atm; XEvent event; atm = get_atom (n, False); if (XGetSelectionOwner (dpy, XA_PRIMARY) == None) { if (n != 0) { data = fetch_buffer (atom[0], &nbytes, 0); store_buffer (data, nbytes, atm, 0); XFree (data); } return; } XSelectInput (dpy, root, PropertyChangeMask); XConvertSelection (dpy, XA_PRIMARY, xa_utf8_string, atm, root, CurrentTime); XtAppAddTimeOut (app, 3000, timeout, (char *) 0); for (;;) { XtAppNextEvent (app, &event); if (event.type == PropertyNotify && event.xproperty.window == root && event.xproperty.atom == atm && event.xproperty.state == PropertyNewValue) { return; } XtDispatchEvent (&event); /* This cannot happen. !!?? */ } } /* * Process an ASCII list of cut buffer numbers. * Lists must obey the form "buffno[,buffno...]" * where buffno is a non-negative integer or a range * of the form M-N. A processing function is called * for each buffer number in the list. Duplicates and * list ordering is significant. */ static void dolist (list, fn, data, nbytes) char *list; void (*fn) (); char *data; int nbytes; { int m, n, x; while (*list) { if (!isdigit (*list)) usage (); for (m = 0; isdigit (*list); list++) m = m * 10 + *list - '0'; (*fn) (m, data, nbytes); if (*list == '-') { list++; if (!isdigit (*list)) usage (); for (n = 0; isdigit (*list); list++) n = n * 10 + *list - '0'; x = (m > n) ? -1 : 1; while (m != n) { m += x; (*fn) (m, data, nbytes); } } if (*list == ',') list++; else if (*list) usage (); } } /* * Perform a task mode command, i.e. * do something to the cut buffers immediately, * without the need to create any X windows first. */ static void dotask (cmd, arg) int cmd; char *arg; { char *ptr; int i, n, nb; ptr = (char *) 0; n = nb = 0; switch (cmd) { case 'p': /* print one or more buffers */ dolist (arg, doprint, (char *) 0, 0); break; case 'r': /* rotate the buffer contents */ n = atoi (arg); if (n == 0) break; initialize_properties (); XRotateWindowProperties (dpy, root, atom, nbuffs, n); break; case 's': /* store data in one or more buffers */ do { ptr = XtRealloc (ptr, nb + BUFINC); i = BUFINC; do { n = read (0, ptr + nb, i); nb += n; i -= n; } while (n > 0 && i > 0); } while (n > 0); if (n == -1) { fprintf (stderr, "Read error\n"); exit (1); } dolist (arg, doset, ptr, nb); XtFree (ptr); break; case 'S': dolist (arg, dogetseln, (char *) 0, 0); break; } } typedef struct { int nbuffs; char *layout; } ares_t, *ares_ptr; static ares_t ares; static XtResource res[] = { #define offset(field) XtOffset(ares_ptr, field) {XtNbufferCount, XtCBufferCount, XtRInt, sizeof (int), offset (nbuffs), XtRImmediate, (XtPointer) 8}, {XtNlayout, XtCLayout, XtRString, sizeof (char *), offset (layout), XtRImmediate, "horiz"}, #undef offset }; static char *def[] = { /* default resource values */ ".bufferCount: 8", ".layout: horizontal", "*fontSet: XtDefaultFontSet", "*Buffer.width: 60", "*Buffer.height: 60", 0, }; static XrmOptionDescRec opt[] = { {"-n", ".bufferCount", XrmoptionSepArg, (caddr_t) 8}, {"-l", ".layout", XrmoptionSepArg, (caddr_t) "horiz"}, }; /* * Parse the command line options, and * perform all the windows initializations. */ static void init (argc, argv) int argc; char **argv; { int i, n; char **p; char name[16]; Arg args[3]; /* * Set up the atoms that we already know about. */ natoms = 8; atom = (Atom *) XtMalloc (natoms * sizeof (Atom)); atom[0] = XA_CUT_BUFFER0; atom[1] = XA_CUT_BUFFER1; atom[2] = XA_CUT_BUFFER2; atom[3] = XA_CUT_BUFFER3; atom[4] = XA_CUT_BUFFER4; atom[5] = XA_CUT_BUFFER5; atom[6] = XA_CUT_BUFFER6; atom[7] = XA_CUT_BUFFER7; /* * Initialize the toolkit, parse the command line, * initialize the resources database, and find out * how many buffers to deal with. */ XtSetLanguageProc (0, 0, 0); top = XtAppInitialize (&app, PGM_CLASS, opt, 2, &argc, argv, def, 0, 0); dpy = XtDisplay (top); root = RootWindow (dpy, DefaultScreen (dpy)); XtGetApplicationResources (top, &ares, res, XtNumber (res), 0, 0); nbuffs = max (ares.nbuffs, 1); /* search for the -u and -v switches first */ for (p = argv + 1; p < argv + argc; p++) { if (eq (*p, "-u")) use_utf8 = 1; else if (eq (*p, "-V")) { printf ("xcb version %s\n", version); exit (0); } } /* * If the command line contains one of the task mode * switches (print, set, rotate), it is processed here. * If there are multiple task mode args, only the first * one is processed. */ for (p = argv + 1; p < argv + argc; p++) { if (eq (*p, "-p") || eq (*p, "-r") || eq (*p, "-s") || eq (*p, "-S")) { if (p == argv + argc - 1) usage (); dotask (p[0][1], p[1]); XCloseDisplay (dpy); exit (0); } } /* * If no task mode request has been made of us, this code * proceeds with the rest of the windows initialization. * The container widget is created, the sub-widgets are * created and then everything is realized. */ if (argc > 1) usage (); wdg = (Widget *) XtMalloc (nbuffs * sizeof (Widget)); #ifdef ATHENA box = XtCreateWidget ("container", formWidgetClass, top, 0, 0); XtManageChild (box); for (i = 0; i < nbuffs; i++) { XtSetArg (args[0], XtNatom, get_atom (i, False)); n = 1; if (i > 0) { if (ares.layout[0] == 'h') { XtSetArg (args[1], XtNfromHoriz, wdg[i - 1]); XtSetArg (args[2], XtNfromVert, 0); n = 3; } if (ares.layout[0] == 'v') { XtSetArg (args[1], XtNfromVert, wdg[i - 1]); XtSetArg (args[2], XtNfromHoriz, 0); n = 3; } } sprintf (name, "buffer%d", i); wdg[i] = XtCreateWidget (name, cbWidgetClass, box, args, n); XtManageChild (wdg[i]); } #endif #ifdef MOTIF n = 0; if (ares.layout[0] == 'h') { XtSetArg (args[0], XmNorientation, XmHORIZONTAL); n = 1; } if (ares.layout[0] == 'v') { XtSetArg (args[0], XmNorientation, XmVERTICAL); n = 1; } box = XtCreateWidget ("container", xmRowColumnWidgetClass, top, args, n); XtManageChild (box); for (i = 0; i < nbuffs; i++) { Widget frame; XtSetArg (args[0], XtNatom, get_atom (i, False)); n = 1; sprintf (name, "frame%d", i); frame = XmCreateFrame (box, name, 0, 0); XtManageChild (frame); sprintf (name, "buffer%d", i); wdg[i] = XtCreateWidget (name, cbWidgetClass, frame, args, n); XtManageChild (wdg[i]); } #endif XSelectInput (dpy, root, PropertyChangeMask); XSetIOErrorHandler (xioerror); XtRealizeWidget (top); delwin = XInternAtom (dpy, "WM_DELETE_WINDOW", False); XSetWMProtocols (dpy, XtWindow (top), &delwin, 1); XtAddEventHandler (top, 0, True, wmdel, (XtPointer) 0); xa_compound_text = XInternAtom (dpy, "COMPOUND_TEXT", False); xa_utf8_string = XInternAtom (dpy, "UTF8_STRING", False); xa_text = XInternAtom (dpy, "TEXT", False); if (!xa_utf8_string) xa_utf8_string = xa_compound_text; #ifdef XVIEW clipboard = XInternAtom (dpy, "CLIPBOARD", False); caret = XInternAtom (dpy, "_SUN_SELN_CARET", False); yield = XInternAtom (dpy, "_SUN_SELN_YIELD", False); length = XInternAtom (dpy, "LENGTH", False); lengthc = XInternAtom (dpy, "LENGTH_CHARS", False); #endif initialize_properties (); } /* * Process incoming events. * The program needs to know when a cut buffer value changes. * We achieve this by eavesdropping on PropertyNotify events arising * on the root window. If such an event is delivered to us, the * function immediately clears the window displaying that property * and causes an Expose event to be generated for the window. * This is horrible nasty stuff. */ static void xevents () { XEvent event; int i; for (;;) { XtAppNextEvent (app, &event); if (event.type == PropertyNotify) { for (i = 0; i < nbuffs; i++) if (event.xproperty.atom == atom[i]) XClearArea (dpy, XtWindow (wdg[i]), 0, 0, 0, 0, True); } else if (event.type == SelectionNotify) { if (event.xselection.property) { /* horribly inefficient... */ XConvertSelection (dpy, XA_PRIMARY, event.xselection.target, convert_to, root, event.xselection.time); } else { Atom target; if (event.xselection.target == xa_text) target = XA_STRING; else if (event.xselection.target == xa_compound_text) target = xa_text; else if (event.xselection.target == xa_utf8_string) target = xa_compound_text; else target = None; if (target != None) XConvertSelection (dpy, XA_PRIMARY, target, convert_to, event.xselection.requestor, event.xselection.time); } } else { (void) XtDispatchEvent (&event); } } } main (argc, argv) int argc; char **argv; { init (argc, argv); xevents (); return 0; }