| 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 |
} |