ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/xcb/xcb.c
Revision: 1.1
Committed: Thu Oct 10 10:07:02 2002 UTC (21 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 /*
2     * xcb: Copyright (C) 1992,1993,1994 by Farrell McKay.
3     * XView modifications provided by Danny Vanderryn.
4     * mb/utf8 support by Marc Lehmann <pcg@goof.com>.
5     *
6     * Simple X interface to the cut buffers in an X server.
7     * The program creates a window subdivided into a number of subwindows,
8     * one per cut buffer. The user may copy cut buffer contents around
9     * using mouse buttons 1 and 2, or rotate the buffers using mouse
10     * button 3. Buffers may be cleared by using Shift-button 2.
11     *
12     * Note that this program assumes the cut buffers contain textual
13     * information, and displays buffer contents using the XDrawString
14     * function. It is not suitable for use in any other environment.
15     *
16     * Permission to use, copy, modify, and distribute this software and its
17     * documentation for any purpose and without fee is hereby granted, provided
18     * that the above copyright notice appears in all copies and that both that
19     * copyright notice and this permission notice appear in supporting
20     * documentation. This software is provided "as is" without express or
21     * implied warranty.
22     */
23    
24     /*
25     * The Xutf8*-API seems to just add complexity for each and every code author
26     * without providing any benefits (such as better font handling). To the
27     * contrary, xfree86 seems to go into the (wrong) direction of "unicode fonts",
28     * thereby forcing the user to accept their glyph style. Why. Why. Why???
29     */
30    
31     #if !defined(ATHENA) && !defined(MOTIF)
32     Bong ! Start again.
33     You must define either the 'ATHENA' or 'MOTIF' symbol when compiling.
34     #endif
35    
36     #include <ctype.h>
37     #include <stdio.h>
38     #include <string.h> /* for strcmp() */
39     #ifdef __STDC__
40     #include <stdlib.h> /* for exit()... */
41     #endif
42     #ifndef VMS
43     #include <unistd.h> /* for read(), write() */
44     #endif
45     #include <X11/Xatom.h> /* for pre-defined atom names */
46     #include <X11/StringDefs.h> /* for XtNforeground et.al. */
47     #include <X11/Intrinsic.h>
48    
49     #ifdef ATHENA
50     #ifndef VMS
51     #include <X11/Xaw/Form.h>
52     #else
53     #include <Xaw/Form.h>
54     #endif
55     #endif
56    
57     #ifdef MOTIF
58     #include <Xm/Frame.h>
59     #include <Xm/RowColumn.h>
60     #endif
61    
62     #include "cb.h"
63     #include "patchlevel.h"
64    
65     #define eq(a,b) (strcmp((a),(b)) == 0)
66     #define max(a,b) ((a) > (b)? (a): (b))
67    
68     #define XtNbufferCount "bufferCount" /* Application resources */
69     #define XtCBufferCount "BufferCount"
70     #define XtNlayout "layout"
71     #define XtCLayout "Layout"
72    
73     #define PGM_NAME "xcb"
74     #define PGM_CLASS "Xcb"
75     #define BUFINC 2048
76    
77     static Display *dpy;
78     static Window root;
79     static XtAppContext app;
80     static Widget top, box, *wdg;
81     static Atom xa_compound_text, xa_utf8_string, xa_text;
82     static Atom delwin;
83     static Atom *atom;
84     static int natoms, nbuffs;
85     static Window seln;
86     static int use_utf8 = 0;
87    
88     static Atom convert_to; /* convert selection to this cut buffer */
89    
90     #ifdef XVIEW
91     static Atom caret, clipboard, yield;
92     static Atom length, lengthc;
93     #endif
94    
95     #ifndef X_HAVE_UTF8_STRING
96     #define Xutf8TextPropertyToTextList XmbTextPropertyToTextList
97     #define Xutf8TextListToTextProperty XmbTextListToTextProperty
98     #endif
99    
100     /*
101     * Fetch the contents of cut buffer n from the root window.
102     */
103     static char *
104     fetch_buffer (a, nb, force_mb)
105     Atom a;
106     int *nb;
107     int force_mb;
108     {
109     unsigned long after;
110     char **list;
111     char *data;
112     int count;
113     XTextProperty pt;
114    
115     *nb = 0;
116     if (XGetWindowProperty (dpy, root, a,
117     0L, 10000000L, False, AnyPropertyType,
118     &pt.encoding, &pt.format, &pt.nitems,
119     &after, &pt.value) != Success || !pt.nitems)
120     return strdup ("");
121    
122     if (pt.nitems)
123     {
124     (force_mb ? XmbTextPropertyToTextList : Xutf8TextPropertyToTextList)
125     (dpy, &pt, &list, &count);
126    
127     data = strdup (list[0]);
128     *nb = strlen (data);
129    
130     XFreeStringList (list);
131     XFree (pt.value);
132     }
133     else
134     data = strdup ("");
135    
136     return data;
137     }
138    
139     /*
140     * Store the string p into cut buffer n on the root window.
141     */
142     static void
143     store_buffer (p, nb, atom, force_mb)
144     char *p;
145     int nb;
146     Atom atom;
147     int force_mb;
148     {
149     XTextProperty pt;
150    
151     (force_mb ? XmbTextListToTextProperty : Xutf8TextListToTextProperty)
152     (dpy, &p, 1, XStdICCTextStyle, &pt);
153    
154     XChangeProperty (dpy, root, atom, pt.encoding,
155     8, PropModeReplace, pt.value, pt.nitems);
156    
157     XFree (pt.value);
158     }
159    
160     /*
161     * Add an atom to the program's atom cache.
162     */
163     static Atom
164     get_atom (n, ifexists)
165     int n, ifexists;
166     {
167     char tmp[32];
168    
169     if (n >= natoms)
170     {
171     atom = (Atom *) XtRealloc ((char *) atom, (n + 1) * sizeof (Atom));
172     while (natoms < n + 1)
173     atom[natoms++] = 0;
174     }
175    
176     if (!atom[n])
177     {
178     sprintf (tmp, "CUT_BUFFER%d", n);
179     atom[n] = XInternAtom (dpy, tmp, (Bool) ifexists);
180     }
181    
182     return atom[n];
183     }
184    
185     static void
186     initialize_properties (void)
187     {
188     int i;
189    
190     for (i = nbuffs - 1; i >= 0; i--)
191     {
192     Atom a;
193     int nbytes;
194     char *data;
195    
196     a = get_atom (i, False);
197     data = fetch_buffer (a, &nbytes, 0);
198     store_buffer (data, nbytes, a, 0);
199    
200     free (data);
201     }
202     }
203    
204     /*
205     * Draw a string in the window with top-left corner justification.
206     */
207     static void
208     place_text (cb, str, len, y)
209     CbWidget cb;
210     char *str;
211     int len, y;
212     {
213     int cols;
214     GC gc;
215    
216     gc = (cb->core.window == seln) ? cb->gc_inv : cb->gc;
217     if (y <= (int) cb->core.height)
218     {
219     /* we rely on clipping... */
220     /*cols = ((int) cb->core.width + cb->font_width - 1)
221     / cb->font_width; */
222     /*len = min(len, cols); */
223     if (len > 0)
224     {
225     y -= cb->font_ascent;
226     #if X_HAVE_UTF8_STRING
227     Xutf8DrawImageString
228     #else
229     XmbDrawImageString
230     #endif
231     (dpy, cb->core.window, cb->fontset, gc, 0, y, str, len);
232     }
233     }
234     }
235    
236     /*
237     * ============================================================================
238     * The following collection of functions and data structures define
239     * the cb widget. Each cb widget displays a single cut buffer value
240     * in a window, and provides cut and paste access to that buffer.
241     */
242    
243     static void
244     cb_initialize (req, wdg, args, nargs) /*ARGSUSED */
245     Widget req, wdg;
246     ArgList args;
247     Cardinal *nargs;
248     {
249     CbWidget cb = (CbWidget) wdg;
250     XFontSetExtents *xfe = XExtentsOfFontSet (cb->fontset);
251    
252     cb->font_width = xfe->max_logical_extent.width;
253     cb->font_height = xfe->max_logical_extent.height;
254     cb->font_ascent = xfe->max_logical_extent.y;
255     cb->gc = 0;
256     cb->gc_inv = 0;
257     }
258    
259     static void
260     cb_realize (wdg, mask, attrs)
261     Widget wdg;
262     XtValueMask *mask;
263     XSetWindowAttributes *attrs;
264     {
265     CbWidget cb = (CbWidget) wdg;
266     XtGCMask v_mask = 0L;
267     XGCValues values;
268    
269     XtCreateWindow (wdg, InputOutput, CopyFromParent, *mask, attrs);
270     XStoreName (dpy, cb->core.window, cb->core.name);
271    
272     values.foreground = cb->fgnd;
273     values.background = cb->core.background_pixel;
274     values.plane_mask = AllPlanes;
275     v_mask = GCForeground | GCBackground | GCPlaneMask;
276     cb->gc = XtGetGC (wdg, v_mask, &values);
277    
278     values.foreground = cb->core.background_pixel;
279     values.background = cb->fgnd;
280     cb->gc_inv = XtGetGC (wdg, v_mask, &values);
281     }
282    
283     /*
284     * Redraw the contents of one of the subwindows.
285     * The function assumes the cut buffer contains text data, and parses
286     * it accordingly. The data is split into lines at each '\n' character.
287     * Lines which extend beyond the subwindow's borders are clipped; no
288     * wrap-around processing is done.
289     * Keep it simple.
290     */
291     static void
292     cb_redisplay (wdg, event, region) /*ARGSUSED */
293     Widget wdg;
294     XEvent *event;
295     Region region;
296     {
297     CbWidget cb = (CbWidget) wdg;
298     char *p, *pp, *base;
299     int y, nbytes;
300    
301     y = 0;
302     p = pp = base = fetch_buffer (cb->atom, &nbytes, 0);
303     while (pp < base + nbytes)
304     {
305     if (*pp == '\n')
306     {
307     place_text (cb, p, pp - p, y);
308     p = pp + 1;
309     y += cb->font_height;
310     }
311     pp++;
312     }
313     place_text (cb, p, pp - p, y);
314     XFree (base);
315     }
316    
317     static void
318     cb_destroy (wdg)
319     Widget wdg;
320     {
321     CbWidget cb = (CbWidget) wdg;
322    
323     XtReleaseGC (wdg, cb->gc);
324     XtReleaseGC (wdg, cb->gc_inv);
325     }
326    
327     /*
328     * Make this widget the owner of the PRIMARY selection.
329     * The window contents are then redrawn with highlighting.
330     *
331     * It seems that if a XSetSelectionOwner is performed on a client's
332     * window, no SelectionClear event is generated if another window
333     * within the same client is already the selection owner.
334     * This function originally attempted to avoid that problem and force
335     * a SelectionClear event by firstly setting the selection ownership
336     * to None and then setting it to this widget's window. Apparently
337     * that didn't work for all X servers.
338     *
339     * Therefore the function must do the SelectionClear logic itself,
340     * which means it must know when another xcb widget has selection
341     * ownership, which brings about the need for the 'seln' global variable.
342     * This breaks all the rules for object oriented widgets. Disgusting, no?
343     */
344     static void
345     cb_cut (wdg, event, parms, nparms) /*ARGSUSED */
346     Widget wdg;
347     XEvent *event;
348     String *parms;
349     Cardinal *nparms;
350     {
351     CbWidget cb = (CbWidget) wdg;
352     Window win = cb->core.window;
353     Window w, tmp;
354    
355     if (win == seln)
356     return;
357    
358     XSetSelectionOwner (dpy, XA_PRIMARY, win, event->xbutton.time);
359     w = XGetSelectionOwner (dpy, XA_PRIMARY);
360     if (seln && w != seln)
361     {
362     tmp = seln;
363     seln = 0;
364     XClearArea (dpy, tmp, 0, 0, 0, 0, False);
365     cb_redisplay (XtWindowToWidget (dpy, tmp), (XEvent *) 0, (Region) 0);
366     }
367    
368     if (w == win)
369     {
370     seln = win;
371     XClearArea (dpy, win, 0, 0, 0, 0, False);
372     cb_redisplay (wdg, (XEvent *) 0, (Region) 0);
373     #ifdef XVIEW
374     XSetSelectionOwner (dpy, caret, win, event->xbutton.time);
375     XSetSelectionOwner (dpy, clipboard, win, event->xbutton.time);
376     #endif
377     }
378     }
379    
380     static void
381     cb_paste (wdg, event, parms, nparms) /*ARGSUSED */
382     Widget wdg;
383     XEvent *event;
384     String *parms;
385     Cardinal *nparms;
386     {
387     CbWidget cb = (CbWidget) wdg;
388     Window w;
389     char *ptr;
390     int n;
391    
392     w = XGetSelectionOwner (dpy, XA_PRIMARY);
393     if (w == None)
394     {
395     ptr = fetch_buffer (atom[0], &n, 0); /* copy from cb0 */
396     store_buffer (ptr, n, cb->atom, 0);
397     XFree (ptr);
398     }
399     else if (w != cb->core.window)
400     XConvertSelection (dpy, XA_PRIMARY, xa_utf8_string,
401     convert_to = cb->atom, XtWindow(wdg), event->xbutton.time);
402     }
403    
404     static void
405     cb_clear (wdg, event, parms, nparms) /*ARGSUSED */
406     Widget wdg;
407     XEvent *event;
408     String *parms;
409     Cardinal *nparms;
410     {
411     CbWidget cb = (CbWidget) wdg;
412     Window win = cb->core.window;
413    
414     store_buffer ("", 0, cb->atom, 0);
415     if (win == seln)
416     {
417     seln = 0;
418     XSetSelectionOwner (dpy, XA_PRIMARY, None, event->xbutton.time);
419     }
420     }
421    
422     static void
423     cb_rotate (wdg, event, parms, nparms) /*ARGSUSED */
424     Widget wdg;
425     XEvent *event;
426     String *parms;
427     Cardinal *nparms;
428     {
429     int n = 0;
430    
431     if (*nparms > 0)
432     n = atoi (parms[0]);
433    
434     if (n != 0)
435     XRotateWindowProperties (dpy, root, atom, nbuffs, n);
436     }
437    
438     static void
439     cb_quit (wdg, event, parms, nparms) /*ARGSUSED */
440     Widget wdg;
441     XEvent *event;
442     String *parms;
443     Cardinal *nparms;
444     {
445     exit (0);
446     }
447    
448     /*
449     * Clear and redraw the widget's window.
450     */
451     static void
452     cb_refresh (wdg, event, parms, nparms) /*ARGSUSED */
453     Widget wdg;
454     XEvent *event;
455     String *parms;
456     Cardinal *nparms;
457     {
458     XClearArea (dpy, wdg->core.window, 0, 0, 0, 0, False);
459     cb_redisplay (wdg, (XEvent *) 0, (Region) 0);
460     }
461    
462     /*
463     * Someone or something wants a copy of the current PRIMARY selection.
464     * Such a request is only satisfied if the target type is STRING.
465     * (No conversion facilities are provided by this program).
466     * The selection request is met by copying the current contents
467     * of the cut buffer to the target window+atom.
468     */
469     static void
470     cb_selreq (wdg, event, parms, nparms) /*ARGSUSED */
471     Widget wdg;
472     XEvent *event;
473     String *parms;
474     Cardinal *nparms;
475     {
476     int nbytes;
477     char *ptr;
478     XSelectionEvent notify;
479     XSelectionRequestEvent *rq;
480     CbWidget cb = (CbWidget) wdg;
481     Window win = cb->core.window;
482    
483     #ifdef XVIEW
484     unsigned long data;
485     #endif
486    
487     rq = (XSelectionRequestEvent *) event;
488    
489     notify.type = SelectionNotify;
490     notify.display = rq->display;
491     notify.requestor = rq->requestor;
492     notify.selection = rq->selection;
493     notify.target = rq->target;
494     notify.property = None;
495     notify.time = rq->time;
496    
497     #ifdef XVIEW
498     if (rq->selection == XA_PRIMARY)
499     {
500     if (rq->target == yield)
501     {
502     /* tell 'em we'll give it up */
503     data = 1;
504     XChangeProperty (dpy, rq->requestor, rq->property,
505     rq->target, 32, PropModeReplace,
506     (unsigned char *) &data, 1);
507     notify.property = rq->property;
508     }
509     else
510     {
511     #endif
512    
513     if (win == seln)
514     {
515     XICCEncodingStyle style;
516     Atom target = None;
517    
518     if (rq->target == XA_STRING)
519     {
520     target = XA_STRING;
521     style = XStringStyle;
522     }
523     else if (rq->target == xa_text)
524     {
525     target = xa_compound_text;
526     style = XStdICCTextStyle;
527     }
528     else if (rq->target == xa_compound_text)
529     {
530     target = xa_compound_text;
531     style = XCompoundTextStyle;
532     }
533     #ifdef X_HAVE_UTF8_STRING
534     else if (rq->target == xa_utf8_string)
535     {
536     target = xa_utf8_string;
537     style = XUTF8StringStyle;
538     }
539     #endif
540    
541     if (target != None)
542     {
543     XTextProperty pt;
544     char *fl;
545    
546     fl = fetch_buffer (cb->atom, &nbytes, 0);
547     Xutf8TextListToTextProperty (dpy, &fl, 1, style, &pt);
548     XFree (fl);
549    
550     XChangeProperty (dpy, rq->requestor, rq->property,
551     pt.encoding, 8, PropModeReplace,
552     pt.value, pt.nitems);
553    
554     notify.property = rq->property;
555     XFree (pt.value);
556     }
557     }
558    
559     #ifdef XVIEW
560     }
561     }
562     else if (rq->selection == caret)
563     {
564     if (rq->target == yield)
565     {
566     /*
567     * Give up the caret (which meant that we
568     * own the clipboard)
569     */
570     XSetSelectionOwner (dpy, caret, None,
571     event->xselectionrequest.time);
572     data = 1;
573     XChangeProperty (dpy, rq->requestor, rq->property,
574     rq->target, 32, PropModeReplace,
575     (unsigned char *) &data, 1);
576     notify.property = rq->property;
577     }
578     }
579     else if (rq->selection == clipboard && win == seln)
580     {
581     ptr = fetch_buffer (cb->atom, &nbytes, 0);
582     if (rq->target == lengthc || rq->target == length)
583     {
584     /*
585     * Send the length of the selection.
586     */
587     data = nbytes;
588     XChangeProperty (dpy, rq->requestor, rq->property,
589     rq->target, 32, PropModeReplace,
590     (unsigned char *) &data, 1);
591     notify.property = rq->property;
592     }
593     else if (rq->target == XA_STRING ||
594     rq->target == xa_text || rq->target == xa_compound_text)
595     {
596     /*
597     * Send the selection itself.
598     * All of our selections will be XA_STRING,
599     * but if they ask for COMPOUND_TEXT, it's ok
600     * to say that that's what we've got...
601     */
602     XChangeProperty (dpy, rq->requestor, rq->property,
603     (rq->target == xa_compound_text ? xa_compound_text : XA_STRING), 8,
604     PropModeReplace, ptr, nbytes);
605     notify.property = rq->property;
606     }
607     XFree (ptr);
608     }
609     #endif
610    
611     XSendEvent (dpy, rq->requestor, False, 0, (XEvent *) & notify);
612     }
613    
614     /*
615     * Boo hiss, someone has taken the PRIMARY selection ownership
616     * away from this widget. The current window contents must
617     * be redrawn without highlighting.
618     */
619     static void
620     cb_selclear (wdg, event, parms, nparms) /*ARGSUSED */
621     Widget wdg;
622     XEvent *event;
623     String *parms;
624     Cardinal *nparms;
625     {
626     CbWidget cb = (CbWidget) wdg;
627    
628     #ifdef XVIEW
629     Window w;
630     Time t;
631     #endif
632    
633     if (event->xproperty.atom != XA_PRIMARY)
634     return;
635    
636     seln = 0;
637     XClearArea (dpy, cb->core.window, 0, 0, 0, 0, False);
638     cb_redisplay (wdg, (XEvent *) 0, (Region) 0);
639     #ifdef XVIEW
640     /*
641     * Since we don't have ownership of PRIMARY anymore,
642     * we'd better get rid of CLIPBOARD and _SUN_SELN_CARET,
643     * if they're still ours.
644     */
645     t = event->xselectionclear.time;
646     w = XGetSelectionOwner (dpy, caret);
647     if (w == cb->core.window)
648     XSetSelectionOwner (dpy, caret, None, t);
649     w = XGetSelectionOwner (dpy, clipboard);
650     if (w == cb->core.window)
651     XSetSelectionOwner (dpy, clipboard, None, t);
652     #endif
653     }
654    
655     static XtResource resources[] =
656     {
657     #define offset(field) XtOffset(CbWidget, field)
658     /* {name, class, type, size, offset, default_type, default_addr}, */
659     {XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
660     offset (fgnd), XtRString, (XtPointer) "XtDefaultForeground"},
661     {XtNfontSet, XtCFontSet, XtRFontSet, sizeof (XFontSet),
662     offset (fontset), XtRString, (XtPointer) "XtDefaultFontSet"},
663     {XtNatom, XtCAtom, XtRAtom, sizeof (Atom), /* internal use */
664     offset (atom), XtRImmediate, (XtPointer) 0},
665     #undef offset
666     };
667    
668     static XtActionsRec actions[] =
669     {
670     {"cut", cb_cut},
671     {"paste", cb_paste},
672     {"clear", cb_clear},
673     {"rotate", cb_rotate},
674     {"quit", cb_quit},
675     {"refresh", cb_refresh},
676     {"selreq", cb_selreq},
677     {"selclear", cb_selclear},
678     };
679    
680     static char cb_transl[] = "\
681     <Btn1Down>: cut() \n\
682     Shift <Btn2Down>: clear() \n\
683     <Btn2Down>: paste() \n\
684     Shift <Btn3Down>: rotate(-1) \n\
685     <Btn3Down>: rotate(1) \n\
686     <Key>Left: rotate(-1) \n\
687     <Key>Right: rotate(1) \n\
688     <Key>Up: rotate(-1) \n\
689     <Key>Down: rotate(1) \n\
690     <Key>q: quit() \n\
691     <SelReq>: selreq() \n\
692     <SelClr>: selclear() \n\
693     ";
694    
695     CbClassRec cbClassRec =
696     {
697     {
698     (WidgetClass) & widgetClassRec, /* superclass */
699     "Buffer", /* class_name */
700     sizeof (CbRec), /* widget_size */
701     NULL, /* class_initialize */
702     NULL, /* class_part_initialize */
703     FALSE, /* class_inited */
704     cb_initialize, /* initialize */
705     NULL, /* initialize_hook */
706     cb_realize, /* realize */
707     actions, /* actions */
708     XtNumber (actions), /* num_actions */
709     resources, /* resources */
710     XtNumber (resources), /* num_resources */
711     NULLQUARK, /* xrm_class */
712     TRUE, /* compress_motion */
713     TRUE, /* compress_exposure */
714     TRUE, /* compress_enterleave */
715     FALSE, /* visible_interest */
716     cb_destroy, /* destroy */
717     NULL, /* resize */
718     cb_redisplay, /* expose */
719     NULL, /* set_values */
720     NULL, /* set_values_hook */
721     XtInheritSetValuesAlmost, /* set_values_almost */
722     NULL, /* get_values_hook */
723     NULL, /* accept_focus */
724     XtVersion, /* version */
725     NULL, /* callback_private */
726     cb_transl, /* tm_table */
727     XtInheritQueryGeometry, /* query_geometry */
728     XtInheritDisplayAccelerator, /* display_accelerator */
729     NULL /* extension */
730     },
731     };
732    
733     WidgetClass cbWidgetClass = (WidgetClass) & cbClassRec;
734    
735     /*
736     * Here endeth the section concerned with the cb widget.
737     * Normal viewing shall now be resumed.
738     * ============================================================================
739     */
740    
741    
742     static void
743     usage ()
744     {
745     fprintf (stderr,
746     "Usage: %s [Xt option] [-l layout] [-n count] [-p|-s|-S list] [-r count]\n",
747     PGM_NAME);
748     exit (1);
749     }
750    
751     /*
752     * Gracefully exit after an XIO error, or after a "delete window"
753     * directive from the window manager.
754     * xioerror() avoids messy error messages sometimes seen from xterm
755     * or in the xdm-errors file when forcibly destroying the client program.
756     */
757     static int
758     xioerror (d) /*ARGSUSED */
759     Display *d;
760     {
761     exit (1); /*NOTREACHED */
762     }
763    
764     static void
765     wmdel (wdg, ptr, ep, cont) /*ARGSUSED */
766     Widget wdg;
767     XtPointer ptr;
768     XEvent *ep;
769     Boolean *cont;
770     {
771     if (ep->type == ClientMessage && ep->xclient.data.l[0] == delwin)
772     exit (0);
773     }
774    
775     /*
776     * Print the contents of a cut buffer on stdout.
777     */
778     static void
779     doprint (n, ptr, nb)
780     int n;
781     char *ptr;
782     int nb;
783     {
784     Atom a;
785    
786     a = get_atom (n, True);
787     if (a)
788     {
789     ptr = fetch_buffer (a, &nb, !use_utf8);
790    
791     if (write (1, ptr, nb) != nb)
792     {
793     fprintf (stderr, "Write error\n");
794     exit (1);
795     }
796    
797     XFree (ptr);
798     }
799     }
800    
801     /*
802     * Load a new value into one of the cut buffers.
803     */
804     static void
805     doset (n, ptr, nb)
806     int n;
807     char *ptr;
808     int nb;
809     {
810     char *str = malloc (nb + 1);
811    
812     memcpy (str, ptr, nb);
813     str[nb] = 0;
814    
815     store_buffer (str, nb, get_atom (n, False), !use_utf8);
816     free (str);
817     }
818    
819     static void
820     timeout (arg, id)
821     char *arg;
822     XtIntervalId *id;
823     {
824     exit (2);
825     }
826    
827     /*
828     * Copy the PRIMARY selection into a cut buffer.
829     */
830     static void
831     dogetseln (n, ptr, nb)
832     int n;
833     char *ptr;
834     int nb;
835     {
836     char *data;
837     int nbytes;
838     Atom atm;
839     XEvent event;
840    
841     atm = get_atom (n, False);
842     if (XGetSelectionOwner (dpy, XA_PRIMARY) == None)
843     {
844     if (n != 0)
845     {
846     data = fetch_buffer (atom[0], &nbytes, 0);
847     store_buffer (data, nbytes, atm, 0);
848     XFree (data);
849     }
850     return;
851     }
852    
853     XSelectInput (dpy, root, PropertyChangeMask);
854     XConvertSelection (dpy, XA_PRIMARY, xa_utf8_string, atm, root, CurrentTime);
855     XtAppAddTimeOut (app, 3000, timeout, (char *) 0);
856    
857     for (;;)
858     {
859     XtAppNextEvent (app, &event);
860     if (event.type == PropertyNotify
861     && event.xproperty.window == root
862     && event.xproperty.atom == atm
863     && event.xproperty.state == PropertyNewValue)
864     {
865     return;
866     }
867    
868     XtDispatchEvent (&event); /* This cannot happen. !!?? */
869     }
870     }
871    
872     /*
873     * Process an ASCII list of cut buffer numbers.
874     * Lists must obey the form "buffno[,buffno...]"
875     * where buffno is a non-negative integer or a range
876     * of the form M-N. A processing function is called
877     * for each buffer number in the list. Duplicates and
878     * list ordering is significant.
879     */
880     static void
881     dolist (list, fn, data, nbytes)
882     char *list;
883     void (*fn) ();
884     char *data;
885     int nbytes;
886     {
887     int m, n, x;
888    
889     while (*list)
890     {
891     if (!isdigit (*list))
892     usage ();
893     for (m = 0; isdigit (*list); list++)
894     m = m * 10 + *list - '0';
895    
896     (*fn) (m, data, nbytes);
897    
898     if (*list == '-')
899     {
900     list++;
901     if (!isdigit (*list))
902     usage ();
903     for (n = 0; isdigit (*list); list++)
904     n = n * 10 + *list - '0';
905    
906     x = (m > n) ? -1 : 1;
907     while (m != n)
908     {
909     m += x;
910     (*fn) (m, data, nbytes);
911     }
912     }
913    
914     if (*list == ',')
915     list++;
916     else if (*list)
917     usage ();
918     }
919     }
920    
921     /*
922     * Perform a task mode command, i.e.
923     * do something to the cut buffers immediately,
924     * without the need to create any X windows first.
925     */
926     static void
927     dotask (cmd, arg)
928     int cmd;
929     char *arg;
930     {
931     char *ptr;
932     int i, n, nb;
933    
934     ptr = (char *) 0;
935     n = nb = 0;
936    
937     switch (cmd)
938     {
939     case 'p': /* print one or more buffers */
940     dolist (arg, doprint, (char *) 0, 0);
941     break;
942     case 'r': /* rotate the buffer contents */
943     n = atoi (arg);
944    
945     if (n == 0)
946     break;
947    
948     initialize_properties ();
949    
950     XRotateWindowProperties (dpy, root, atom, nbuffs, n);
951     break;
952     case 's': /* store data in one or more buffers */
953     do
954     {
955     ptr = XtRealloc (ptr, nb + BUFINC);
956     i = BUFINC;
957     do
958     {
959     n = read (0, ptr + nb, i);
960     nb += n;
961     i -= n;
962     }
963     while (n > 0 && i > 0);
964     }
965     while (n > 0);
966    
967     if (n == -1)
968     {
969     fprintf (stderr, "Read error\n");
970     exit (1);
971     }
972    
973     dolist (arg, doset, ptr, nb);
974     XtFree (ptr);
975     break;
976     case 'S':
977     dolist (arg, dogetseln, (char *) 0, 0);
978     break;
979     }
980     }
981    
982     typedef struct
983     {
984     int nbuffs;
985     char *layout;
986     }
987     ares_t, *ares_ptr;
988    
989     static ares_t ares;
990    
991     static XtResource res[] =
992     {
993     #define offset(field) XtOffset(ares_ptr, field)
994     {XtNbufferCount, XtCBufferCount, XtRInt, sizeof (int),
995     offset (nbuffs), XtRImmediate, (XtPointer) 8},
996     {XtNlayout, XtCLayout, XtRString, sizeof (char *),
997     offset (layout), XtRImmediate, "horiz"},
998     #undef offset
999     };
1000    
1001     static char *def[] =
1002     { /* default resource values */
1003     ".bufferCount: 8",
1004     ".layout: horizontal",
1005     "*fontSet: XtDefaultFontSet",
1006     "*Buffer.width: 60",
1007     "*Buffer.height: 60",
1008     0,
1009     };
1010    
1011     static XrmOptionDescRec opt[] =
1012     {
1013     {"-n", ".bufferCount", XrmoptionSepArg, (caddr_t) 8},
1014     {"-l", ".layout", XrmoptionSepArg, (caddr_t) "horiz"},
1015     };
1016    
1017     /*
1018     * Parse the command line options, and
1019     * perform all the windows initializations.
1020     */
1021     static void
1022     init (argc, argv)
1023     int argc;
1024     char **argv;
1025     {
1026     int i, n;
1027     char **p;
1028     char name[16];
1029     Arg args[3];
1030    
1031     /*
1032     * Set up the atoms that we already know about.
1033     */
1034     natoms = 8;
1035     atom = (Atom *) XtMalloc (natoms * sizeof (Atom));
1036     atom[0] = XA_CUT_BUFFER0;
1037     atom[1] = XA_CUT_BUFFER1;
1038     atom[2] = XA_CUT_BUFFER2;
1039     atom[3] = XA_CUT_BUFFER3;
1040     atom[4] = XA_CUT_BUFFER4;
1041     atom[5] = XA_CUT_BUFFER5;
1042     atom[6] = XA_CUT_BUFFER6;
1043     atom[7] = XA_CUT_BUFFER7;
1044    
1045     /*
1046     * Initialize the toolkit, parse the command line,
1047     * initialize the resources database, and find out
1048     * how many buffers to deal with.
1049     */
1050     XtSetLanguageProc (0, 0, 0);
1051     top = XtAppInitialize (&app, PGM_CLASS, opt, 2, &argc, argv, def, 0, 0);
1052     dpy = XtDisplay (top);
1053     root = RootWindow (dpy, DefaultScreen (dpy));
1054    
1055     XtGetApplicationResources (top, &ares, res, XtNumber (res), 0, 0);
1056     nbuffs = max (ares.nbuffs, 1);
1057    
1058     /* search for the -u and -v switches first */
1059     for (p = argv + 1; p < argv + argc; p++)
1060     {
1061     if (eq (*p, "-u"))
1062     use_utf8 = 1;
1063     else if (eq (*p, "-V"))
1064     {
1065     printf ("xcb version %s\n", version);
1066     exit (0);
1067     }
1068     }
1069    
1070     /*
1071     * If the command line contains one of the task mode
1072     * switches (print, set, rotate), it is processed here.
1073     * If there are multiple task mode args, only the first
1074     * one is processed.
1075     */
1076     for (p = argv + 1; p < argv + argc; p++)
1077     {
1078     if (eq (*p, "-p") || eq (*p, "-r") || eq (*p, "-s") || eq (*p, "-S"))
1079     {
1080     if (p == argv + argc - 1)
1081     usage ();
1082    
1083     dotask (p[0][1], p[1]);
1084     XCloseDisplay (dpy);
1085     exit (0);
1086     }
1087     }
1088    
1089     /*
1090     * If no task mode request has been made of us, this code
1091     * proceeds with the rest of the windows initialization.
1092     * The container widget is created, the sub-widgets are
1093     * created and then everything is realized.
1094     */
1095     if (argc > 1)
1096     usage ();
1097    
1098     wdg = (Widget *) XtMalloc (nbuffs * sizeof (Widget));
1099    
1100     #ifdef ATHENA
1101     box = XtCreateWidget ("container", formWidgetClass, top, 0, 0);
1102     XtManageChild (box);
1103    
1104     for (i = 0; i < nbuffs; i++)
1105     {
1106     XtSetArg (args[0], XtNatom, get_atom (i, False));
1107     n = 1;
1108     if (i > 0)
1109     {
1110     if (ares.layout[0] == 'h')
1111     {
1112     XtSetArg (args[1], XtNfromHoriz, wdg[i - 1]);
1113     XtSetArg (args[2], XtNfromVert, 0);
1114     n = 3;
1115     }
1116    
1117     if (ares.layout[0] == 'v')
1118     {
1119     XtSetArg (args[1], XtNfromVert, wdg[i - 1]);
1120     XtSetArg (args[2], XtNfromHoriz, 0);
1121     n = 3;
1122     }
1123     }
1124    
1125     sprintf (name, "buffer%d", i);
1126     wdg[i] = XtCreateWidget (name, cbWidgetClass, box, args, n);
1127     XtManageChild (wdg[i]);
1128     }
1129     #endif
1130    
1131     #ifdef MOTIF
1132     n = 0;
1133    
1134     if (ares.layout[0] == 'h')
1135     {
1136     XtSetArg (args[0], XmNorientation, XmHORIZONTAL);
1137     n = 1;
1138     }
1139    
1140     if (ares.layout[0] == 'v')
1141     {
1142     XtSetArg (args[0], XmNorientation, XmVERTICAL);
1143     n = 1;
1144     }
1145    
1146     box = XtCreateWidget ("container", xmRowColumnWidgetClass, top, args, n);
1147     XtManageChild (box);
1148    
1149     for (i = 0; i < nbuffs; i++)
1150     {
1151     Widget frame;
1152    
1153     XtSetArg (args[0], XtNatom, get_atom (i, False));
1154     n = 1;
1155    
1156     sprintf (name, "frame%d", i);
1157     frame = XmCreateFrame (box, name, 0, 0);
1158     XtManageChild (frame);
1159    
1160     sprintf (name, "buffer%d", i);
1161     wdg[i] = XtCreateWidget (name, cbWidgetClass, frame, args, n);
1162     XtManageChild (wdg[i]);
1163     }
1164     #endif
1165    
1166     XSelectInput (dpy, root, PropertyChangeMask);
1167     XSetIOErrorHandler (xioerror);
1168    
1169     XtRealizeWidget (top);
1170    
1171     delwin = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
1172     XSetWMProtocols (dpy, XtWindow (top), &delwin, 1);
1173     XtAddEventHandler (top, 0, True, wmdel, (XtPointer) 0);
1174    
1175     xa_compound_text = XInternAtom (dpy, "COMPOUND_TEXT", False);
1176     xa_utf8_string = XInternAtom (dpy, "UTF8_STRING", False);
1177     xa_text = XInternAtom (dpy, "TEXT", False);
1178    
1179     if (!xa_utf8_string)
1180     xa_utf8_string = xa_compound_text;
1181    
1182     #ifdef XVIEW
1183     clipboard = XInternAtom (dpy, "CLIPBOARD", False);
1184     caret = XInternAtom (dpy, "_SUN_SELN_CARET", False);
1185     yield = XInternAtom (dpy, "_SUN_SELN_YIELD", False);
1186     length = XInternAtom (dpy, "LENGTH", False);
1187     lengthc = XInternAtom (dpy, "LENGTH_CHARS", False);
1188     #endif
1189    
1190     initialize_properties ();
1191     }
1192    
1193     /*
1194     * Process incoming events.
1195     * The program needs to know when a cut buffer value changes.
1196     * We achieve this by eavesdropping on PropertyNotify events arising
1197     * on the root window. If such an event is delivered to us, the
1198     * function immediately clears the window displaying that property
1199     * and causes an Expose event to be generated for the window.
1200     * This is horrible nasty stuff.
1201     */
1202     static void
1203     xevents ()
1204     {
1205     XEvent event;
1206     int i;
1207    
1208     for (;;)
1209     {
1210     XtAppNextEvent (app, &event);
1211    
1212     if (event.type == PropertyNotify)
1213     {
1214     for (i = 0; i < nbuffs; i++)
1215     if (event.xproperty.atom == atom[i])
1216     XClearArea (dpy, XtWindow (wdg[i]), 0, 0, 0, 0, True);
1217     }
1218     else if (event.type == SelectionNotify)
1219     {
1220     if (event.xselection.property)
1221     {
1222     /* horribly inefficient... */
1223     XConvertSelection (dpy, XA_PRIMARY, event.xselection.target,
1224     convert_to, root, event.xselection.time);
1225     }
1226     else
1227     {
1228     Atom target;
1229    
1230     if (event.xselection.target == xa_text)
1231     target = XA_STRING;
1232     else if (event.xselection.target == xa_compound_text)
1233     target = xa_text;
1234     else if (event.xselection.target == xa_utf8_string)
1235     target = xa_compound_text;
1236     else
1237     target = None;
1238    
1239     if (target != None)
1240     XConvertSelection (dpy, XA_PRIMARY, target,
1241     convert_to, event.xselection.requestor, event.xselection.time);
1242     }
1243     }
1244     else
1245     {
1246     (void) XtDispatchEvent (&event);
1247     }
1248     }
1249     }
1250    
1251     main (argc, argv)
1252     int argc;
1253     char **argv;
1254     {
1255     init (argc, argv);
1256     xevents ();
1257     return 0;
1258     }