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