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

# Content
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 #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 #include <X11/Intrinsic.h>
45 #include <locale.h>
46
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 #define eq(a,b) (strcmp((a),(b)) == 0)
64 #define max(a,b) ((a) > (b)? (a): (b))
65
66 #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
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 static Atom convert_to; /* convert selection to this cut buffer */
87
88 #ifndef X_HAVE_UTF8_STRING
89 #define Xutf8TextPropertyToTextList XmbTextPropertyToTextList
90 #define Xutf8TextListToTextProperty XmbTextListToTextProperty
91 #endif
92
93 #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 /*
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 0L, 10000000L, False, AnyPropertyType,
316 &pt.encoding, &pt.format, &pt.nitems,
317 &after, &pt.value) != Success || !pt.nitems)
318 return strdup ("");
319
320 if (pt.nitems)
321 {
322 (force_mb ? XmbTextPropertyToTextList : Xutf8TextPropertyToTextList)
323 (dpy, &pt, &list, &count);
324
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 8, PropModeReplace, pt.value, pt.nitems);
354
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 atom[natoms++] = 0;
372 }
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 {
423 y -= cb->font_ascent;
424 #if X_HAVE_UTF8_STRING
425 Xutf8DrawImageString
426 #else
427 XmbDrawImageString
428 #endif
429 (dpy, cb->core.window, cb->fontset, gc, 0, y, str, len);
430 }
431 }
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 cb_initialize (req, wdg, args, nargs) /*ARGSUSED */
443 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 cb_redisplay (wdg, event, region) /*ARGSUSED */
491 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 {
505 place_text (cb, p, pp - p, y);
506 p = pp + 1;
507 y += cb->font_height;
508 }
509 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 cb_cut (wdg, event, parms, nparms) /*ARGSUSED */
544 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 cb_paste (wdg, event, parms, nparms) /*ARGSUSED */
576 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 ptr = fetch_buffer (atom[0], &n, 0); /* copy from cb0 */
590 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 convert_to = cb->atom, XtWindow (wdg), event->xbutton.time);
596 }
597
598 static void
599 cb_clear (wdg, event, parms, nparms) /*ARGSUSED */
600 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 cb_rotate (wdg, event, parms, nparms) /*ARGSUSED */
618 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 cb_quit (wdg, event, parms, nparms) /*ARGSUSED */
634 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 cb_refresh (wdg, event, parms, nparms) /*ARGSUSED */
647 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 cb_selreq (wdg, event, parms, nparms) /*ARGSUSED */
665 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 if (win == seln)
688 {
689 XICCEncodingStyle style;
690 Atom target = None;
691
692 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 #ifdef X_HAVE_UTF8_STRING
708 else if (rq->target == xa_utf8_string)
709 {
710 target = xa_utf8_string;
711 style = XUTF8StringStyle;
712 }
713 #endif
714
715 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
728 notify.property = rq->property;
729 XFree (pt.value);
730 }
731 }
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 cb_selclear (wdg, event, parms, nparms) /*ARGSUSED */
743 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 #define offset(field) XtOffset(CbWidget, field)
761 /* {name, class, type, size, offset, default_type, default_addr}, */
762 {XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
763 offset (fgnd), XtRString, (XtPointer) "XtDefaultForeground"},
764 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof (XFontSet),
765 offset (fontset), XtRString, (XtPointer) "XtDefaultFontSet"},
766 {XtNatom, XtCAtom, XtRAtom, sizeof (Atom), /* internal use */
767 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 <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
798 CbClassRec cbClassRec =
799 {
800 {
801 (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 },
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 "Usage: %s [Xt option] [-l layout] [-n count] [-p|-s|-S list] [-r count]\n",
850 PGM_NAME);
851 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 xioerror (d) /*ARGSUSED */
862 Display *d;
863 {
864 exit (1); /*NOTREACHED */
865 }
866
867 static void
868 wmdel (wdg, ptr, ep, cont) /*ARGSUSED */
869 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 {
896 fprintf (stderr, "Write error\n");
897 exit (1);
898 }
899
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 {
949 data = fetch_buffer (atom[0], &nbytes, 0);
950 store_buffer (data, nbytes, atm, 0);
951 XFree (data);
952 }
953 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 && event.xproperty.window == root
965 && event.xproperty.atom == atm
966 && event.xproperty.state == PropertyNewValue)
967 {
968 return;
969 }
970
971 XtDispatchEvent (&event); /* This cannot happen. !!?? */
972 }
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 usage ();
996 for (m = 0; isdigit (*list); list++)
997 m = m * 10 + *list - '0';
998
999 (*fn) (m, data, nbytes);
1000
1001 if (*list == '-')
1002 {
1003 list++;
1004 if (!isdigit (*list))
1005 usage ();
1006 for (n = 0; isdigit (*list); list++)
1007 n = n * 10 + *list - '0';
1008
1009 x = (m > n) ? -1 : 1;
1010 while (m != n)
1011 {
1012 m += x;
1013 (*fn) (m, data, nbytes);
1014 }
1015 }
1016
1017 if (*list == ',')
1018 list++;
1019 else if (*list)
1020 usage ();
1021 }
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 case 'p': /* print one or more buffers */
1043 dolist (arg, doprint, (char *) 0, 0);
1044 break;
1045 case 'r': /* rotate the buffer contents */
1046 n = atoi (arg);
1047
1048 if (n == 0)
1049 break;
1050
1051 initialize_properties ();
1052
1053 XRotateWindowProperties (dpy, root, atom, nbuffs, n);
1054 break;
1055 case 's': /* store data in one or more buffers */
1056 do
1057 {
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 while (n > 0);
1069
1070 if (n == -1)
1071 {
1072 fprintf (stderr, "Read error\n");
1073 exit (1);
1074 }
1075
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 #define offset(field) XtOffset(ares_ptr, field)
1097 {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 { /* default resource values */
1106 ".bufferCount: 8",
1107 ".layout: horizontal",
1108 "*fontSet: XtDefaultFontSet",
1109 "*Buffer.width: 60",
1110 "*Buffer.height: 60",
1111 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 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 XtGetApplicationResources (top, &ares, res, XtNumber (res), 0, 0);
1168 nbuffs = max (ares.nbuffs, 1);
1169
1170 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 /* search for the -u and -v switches first */
1178 for (p = argv + 1; p < argv + argc; p++)
1179 {
1180 if (eq (*p, "-u"))
1181 use_utf8 = 1;
1182 else if (eq (*p, "-V"))
1183 {
1184 printf ("xcb version %s\n", version);
1185 exit (0);
1186 }
1187 }
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 {
1199 if (p == argv + argc - 1)
1200 usage ();
1201
1202 dotask (p[0][1], p[1]);
1203 XCloseDisplay (dpy);
1204 exit (0);
1205 }
1206 }
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 {
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
1236 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
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 (void) XtDispatchEvent (&event);
1350 }
1351 }
1352
1353 main (argc, argv)
1354 int argc;
1355 char **argv;
1356 {
1357 init (argc, argv);
1358 xevents ();
1359 return 0;
1360 }