/* * 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() */ #include #include /* for exit()... */ #include /* for read(), write() */ #include /* for pre-defined atom names */ #include /* for XtNforeground et.al. */ #include #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 */ #ifndef X_HAVE_UTF8_STRING #define Xutf8TextPropertyToTextList XmbTextPropertyToTextList #define Xutf8TextListToTextProperty XmbTextListToTextProperty #endif #define FONT_ELEMENT_SIZE 50 static const char * i_strstr (const char *str, const char *ptn) { const char *s2, *p2; for (; *str; str++) { for (s2 = str, p2 = ptn;; s2++, p2++) { if (!*p2) return str; if (toupper (*s2) != toupper (*p2)) break; } } return NULL; } static const char * Font_GetElement (const char *pattern, char *buf, int bufsiz, ...) { const char *p, *v; char *p2; va_list va; va_start (va, bufsiz); buf[bufsiz - 1] = 0; buf[bufsiz - 2] = '*'; while ((v = va_arg (va, char *)) != NULL) { p = i_strstr (pattern, v); if (p) { strncpy (buf, p + 1, bufsiz - 2); p2 = strchr (buf, '-'); if (p2) *p2 = 0; va_end (va); return p; } } va_end (va); strncpy (buf, "*", bufsiz); return NULL; } static const char * Font_GetSize (const char *pattern, int *size) { const char *p; const char *p2 = NULL; int n = 0; for (p = pattern; 1; p++) { if (!*p) { if (p2 != NULL && n > 1 && n < 72) { *size = n; return p2 + 1; } else { *size = 16; return NULL; } } else if (*p == '-') { if (n > 1 && n < 72 && p2 != NULL) { *size = n; return p2 + 1; } p2 = p; n = 0; } else if (*p >= '0' && *p <= '9' && p2 != NULL) { n *= 10; n += *p - '0'; } else { p2 = NULL; n = 0; } } } /* from http://www.debian.org/doc/manuals/intro-i18n/ */ static XFontSet XCreateFontSetWithGuess (Display * d, const char *pattern, char ***miss, int *n_miss, char **def) { XFontSet fs; char *pattern2; int pixel_size, bufsiz; char weight[FONT_ELEMENT_SIZE], slant[FONT_ELEMENT_SIZE]; /* No problem? or 'fs' for pattern analysis */ fs = XCreateFontSet (d, pattern, miss, n_miss, def); if (fs && !*n_miss) return fs; /* no need for font guessing */ /* for non-iso8859-1 language and iso8859-1 specification */ /* This 'fs' is only for pattern analysis. */ if (!fs) { if (*n_miss) XFreeStringList (*miss); setlocale (LC_CTYPE, "C"); fs = XCreateFontSet (d, pattern, miss, n_miss, def); setlocale (LC_CTYPE, ""); } /* make XLFD font name for pattern analysis */ if (fs) { XFontStruct **fontstructs; char **fontnames; XFontsOfFontSet (fs, &fontstructs, &fontnames); pattern = fontnames[0]; } /* read elements of font name */ Font_GetElement (pattern, weight, FONT_ELEMENT_SIZE, "-medium-", "-bold-", "-demibold-", "-regular-", NULL); Font_GetElement (pattern, slant, FONT_ELEMENT_SIZE, "-r-", "-i-", "-o-", "-ri-", "-ro-", NULL); Font_GetSize (pattern, &pixel_size); /* modify elements of font name to fit usual font names */ if (!strcmp (weight, "*")) strncpy (weight, "medium", FONT_ELEMENT_SIZE); if (!strcmp (slant, "*")) strncpy (slant, "r", FONT_ELEMENT_SIZE); if (pixel_size < 3) pixel_size = 3; else if (pixel_size > 97) pixel_size = 97; /* build font pattern for better matching for various charsets */ bufsiz = strlen (pattern) + FONT_ELEMENT_SIZE * 2 + 2 * 2 + 58; pattern2 = (char *) malloc (bufsiz); if (pattern2) { snprintf (pattern2, bufsiz - 1, "%s," "-*-*-%s-%s-*-*-%d-*-*-*-*-*-*-*," "-*-*-*-*-*-*-%d-*-*-*-*-*-*-*,*", pattern, weight, slant, pixel_size, pixel_size); pattern = pattern2; } if (*n_miss) XFreeStringList (*miss); if (fs) XFreeFontSet (d, fs); /* create fontset */ fs = XCreateFontSet (d, pattern, miss, n_miss, def); if (pattern2) free (pattern2); return fs; } static Boolean CvtStringToFontSet (dpy, args, num_args, fromVal, toVal, closure_ret) Display *dpy; XrmValuePtr args; Cardinal *num_args; XrmValuePtr fromVal; XrmValuePtr toVal; XtPointer *closure_ret; { XFontSet f; char **missing_charset_list; int missing_charset_count; char *def_string; f = XCreateFontSetWithGuess (dpy, (char *) fromVal->addr, &missing_charset_list, &missing_charset_count, &def_string); /* Free any returned missing charset list */ if (missing_charset_count) { fprintf (stderr, "Missing charsets in String to FontSet conversion (%s)\n", missing_charset_list[0]); XFreeStringList (missing_charset_list); } if (!f) { f = XCreateFontSetWithGuess (dpy, "-*-*-*-R-*-*-*-120-*-*-*-*", &missing_charset_list, &missing_charset_count, &def_string); } *(XFontSet *) (toVal->addr) = f; return f ? True : False; } /* * 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 && pt.format == 8) { (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); } } 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; 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; 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); } } 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; 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); } 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)); XtSetTypeConverter(XtRString, /* source type */ XtRFontSet, /* target type */ CvtStringToFontSet, /* converter routine */ (XtConvertArgList) NULL, /* args for converter routine */ 0, /*# args for converter routine */ XtCacheAll, /* caching instructions */ NULL); /* destructor function */ XtGetApplicationResources (top, &ares, res, XtNumber (res), 0, 0); nbuffs = max (ares.nbuffs, 1); 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; /* 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); 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; }