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