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, 3 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

# 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
231 if (!strcmp (slant, "*"))
232 strncpy (slant, "r", FONT_ELEMENT_SIZE);
233
234 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
253 if (*n_miss)
254 XFreeStringList (*miss);
255
256 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 /*
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 0L, 10000000L, False, AnyPropertyType,
320 &pt.encoding, &pt.format, &pt.nitems,
321 &after, &pt.value) != Success || !pt.nitems)
322 return strdup ("");
323
324 if (pt.nitems && pt.format == 8)
325 {
326 (force_mb ? XmbTextPropertyToTextList : Xutf8TextPropertyToTextList)
327 (dpy, &pt, &list, &count);
328
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 8, PropModeReplace, pt.value, pt.nitems);
358
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 atom[natoms++] = 0;
376 }
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 {
427 y -= cb->font_ascent;
428 #if X_HAVE_UTF8_STRING
429 Xutf8DrawImageString
430 #else
431 XmbDrawImageString
432 #endif
433 (dpy, cb->core.window, cb->fontset, gc, 0, y, str, len);
434 }
435 }
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 cb_initialize (req, wdg, args, nargs) /*ARGSUSED */
447 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 cb_redisplay (wdg, event, region) /*ARGSUSED */
495 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 {
509 place_text (cb, p, pp - p, y);
510 p = pp + 1;
511 y += cb->font_height;
512 }
513 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 cb_cut (wdg, event, parms, nparms) /*ARGSUSED */
548 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 cb_paste (wdg, event, parms, nparms) /*ARGSUSED */
580 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 ptr = fetch_buffer (atom[0], &n, 0); /* copy from cb0 */
594 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 convert_to = cb->atom, XtWindow (wdg), event->xbutton.time);
600 }
601
602 static void
603 cb_clear (wdg, event, parms, nparms) /*ARGSUSED */
604 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 cb_rotate (wdg, event, parms, nparms) /*ARGSUSED */
622 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 cb_quit (wdg, event, parms, nparms) /*ARGSUSED */
638 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 cb_refresh (wdg, event, parms, nparms) /*ARGSUSED */
651 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 cb_selreq (wdg, event, parms, nparms) /*ARGSUSED */
669 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 if (win == seln)
692 {
693 XICCEncodingStyle style;
694 Atom target = None;
695
696 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 #ifdef X_HAVE_UTF8_STRING
712 else if (rq->target == xa_utf8_string)
713 {
714 target = xa_utf8_string;
715 style = XUTF8StringStyle;
716 }
717 #endif
718
719 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
732 notify.property = rq->property;
733 XFree (pt.value);
734 }
735 }
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 cb_selclear (wdg, event, parms, nparms) /*ARGSUSED */
747 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 #define offset(field) XtOffset(CbWidget, field)
765 /* {name, class, type, size, offset, default_type, default_addr}, */
766 {XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
767 offset (fgnd), XtRString, (XtPointer) "XtDefaultForeground"},
768 {XtNfontSet, XtCFontSet, XtRFontSet, sizeof (XFontSet),
769 offset (fontset), XtRString, (XtPointer) "XtDefaultFontSet"},
770 {XtNatom, XtCAtom, XtRAtom, sizeof (Atom), /* internal use */
771 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 <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
802 CbClassRec cbClassRec =
803 {
804 {
805 (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 },
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 "Usage: %s [Xt option] [-l layout] [-n count] [-p|-s|-S list] [-r count]\n",
854 PGM_NAME);
855 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 xioerror (d) /*ARGSUSED */
866 Display *d;
867 {
868 exit (1); /*NOTREACHED */
869 }
870
871 static void
872 wmdel (wdg, ptr, ep, cont) /*ARGSUSED */
873 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 {
900 fprintf (stderr, "Write error\n");
901 exit (1);
902 }
903
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 {
953 data = fetch_buffer (atom[0], &nbytes, 0);
954 store_buffer (data, nbytes, atm, 0);
955 XFree (data);
956 }
957 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 && event.xproperty.window == root
969 && event.xproperty.atom == atm
970 && event.xproperty.state == PropertyNewValue)
971 {
972 return;
973 }
974
975 XtDispatchEvent (&event); /* This cannot happen. !!?? */
976 }
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 usage ();
1000 for (m = 0; isdigit (*list); list++)
1001 m = m * 10 + *list - '0';
1002
1003 (*fn) (m, data, nbytes);
1004
1005 if (*list == '-')
1006 {
1007 list++;
1008 if (!isdigit (*list))
1009 usage ();
1010 for (n = 0; isdigit (*list); list++)
1011 n = n * 10 + *list - '0';
1012
1013 x = (m > n) ? -1 : 1;
1014 while (m != n)
1015 {
1016 m += x;
1017 (*fn) (m, data, nbytes);
1018 }
1019 }
1020
1021 if (*list == ',')
1022 list++;
1023 else if (*list)
1024 usage ();
1025 }
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 case 'p': /* print one or more buffers */
1047 dolist (arg, doprint, (char *) 0, 0);
1048 break;
1049 case 'r': /* rotate the buffer contents */
1050 n = atoi (arg);
1051
1052 if (n == 0)
1053 break;
1054
1055 initialize_properties ();
1056
1057 XRotateWindowProperties (dpy, root, atom, nbuffs, n);
1058 break;
1059 case 's': /* store data in one or more buffers */
1060 do
1061 {
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 while (n > 0);
1073
1074 if (n == -1)
1075 {
1076 fprintf (stderr, "Read error\n");
1077 exit (1);
1078 }
1079
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 #define offset(field) XtOffset(ares_ptr, field)
1101 {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 { /* default resource values */
1110 ".bufferCount: 8",
1111 ".layout: horizontal",
1112 "*fontSet: XtDefaultFontSet",
1113 "*Buffer.width: 60",
1114 "*Buffer.height: 60",
1115 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 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 XtGetApplicationResources (top, &ares, res, XtNumber (res), 0, 0);
1172 nbuffs = max (ares.nbuffs, 1);
1173
1174 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 /* search for the -u and -v switches first */
1182 for (p = argv + 1; p < argv + argc; p++)
1183 {
1184 if (eq (*p, "-u"))
1185 use_utf8 = 1;
1186 else if (eq (*p, "-V"))
1187 {
1188 printf ("xcb version %s\n", version);
1189 exit (0);
1190 }
1191 }
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 {
1203 if (p == argv + argc - 1)
1204 usage ();
1205
1206 dotask (p[0][1], p[1]);
1207 XCloseDisplay (dpy);
1208 exit (0);
1209 }
1210 }
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 {
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
1240 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
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 (void) XtDispatchEvent (&event);
1354 }
1355 }
1356
1357 main (argc, argv)
1358 int argc;
1359 char **argv;
1360 {
1361 init (argc, argv);
1362 xevents ();
1363 return 0;
1364 }