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