1 /*
2  * xvevent.c - EventLoop and support routines for XV
3  *
4  *  Author:    John Bradley, University of Pennsylvania
5  *                (bradley@cis.upenn.edu)
6  *
7  *  Contains:
8  *            int  EventLoop()
9  *            void DrawWindow(x,y,w,h)
10  *            void WResize(w,h)
11  *            void WRotate()
12  *            void WCrop(w,h,dx,dy)
13  *            void WUnCrop()
14  *            void GetWindowPos(&xwa)
15  *            void SetWindowPos(&xwa)
16  *            void SetEpicMode()
17  *            int  xvErrorHandler(disp, err)
18  */
19 
20 #include "copyright.h"
21 
22 #define NEEDSTIME    /* for -wait handling in eventloop */
23 
24 #include "xv.h"
25 
26 #include "bits/dropper"
27 #include "bits/dropperm"
28 #include "bits/pen"
29 #include "bits/penm"
30 #include "bits/blur"
31 #include "bits/blurm"
32 
33 static int   rotatesLeft = 0;
34 static int   origcropx, origcropy, origcropvalid=0;
35 static int   canstartwait;
36 static int   frominterrupt = 0;
37 static char *xevPriSel = NULL;
38 static Time  lastEventTime;
39 static Cursor dropper = 0, pen = 0, blur = 0;
40 
41 
42 static void SelectDispMB       PARM((int));
43 static void Select24to8MB      PARM((int));
44 static void SelectRootMB       PARM((int));
45 static void SelectWindowMB     PARM((int));
46 static void SelectSizeMB       PARM((int));
47 
48 static void DoPrint            PARM((void));
49 static void debugEvent         PARM((XEvent *));
50 static const char *win2name    PARM((Window));
51 static void handleButtonEvent  PARM((XEvent *, int *, int *));
52 static void handleKeyEvent     PARM((XEvent *, int *, int *));
53 static void zoomCurs           PARM((u_int));
54 static void textViewCmd        PARM((void));
55 static void setSizeCmd         PARM((void));
56 static void WMaximize          PARM((void));
57 static void CropKey            PARM((int, int, int, int));
58 static void TrackPicValues     PARM((int, int));
59 static int  CheckForConfig     PARM((void));
60 static Bool IsConfig           PARM((Display *, XEvent *, char *));
61 static void onInterrupt        PARM((int));
62 
63 static void   Paint            PARM((void));
64 static void   paintPixel       PARM((int, int));
65 static void   paintLine        PARM((int, int, int, int));
66 static void   paintXLine       PARM((int, int, int, int, int));
67 static void   BlurPaint        PARM((void));
68 static int    highbit          PARM((u_long));
69 static u_long RGBToXColor      PARM((int, int, int));
70 static void   blurPixel        PARM((int, int));
71 
72 static void   annotatePic      PARM((void));
73 
74 static int    debkludge_offx;
75 static int    debkludge_offy;
76 
77 /****************/
EventLoop()78 int EventLoop()
79 /****************/
80 {
81   XEvent event;
82   int    retval,done,waiting;
83 #ifdef USE_TICKS
84   clock_t waitsec_ticks=0L, orgtime_ticks=0L, curtime_ticks;
85   clock_t elapsed_ticks=0L, remaining_interval;
86 #else
87   time_t orgtime=0L, curtime;
88 #endif
89 
90 
91 #ifndef NOSIGNAL
92   signal(SIGQUIT, onInterrupt);
93 #endif
94 
95   if (startGrab == 1) {
96      startGrab = 2;
97      FakeButtonPress(&but[BGRAB]);
98      FakeKeyPress(ctrlW, XK_Return);
99      return(1);
100   }
101 
102   /* note: there's no special event handling if we're using the root window.
103      if we're using the root window, we will recieve NO events for mainW */
104 
105   /* note: 'canstartwait' is magically turned 'true' in HandleEvent when I
106      think I've finally gotten 'mainW' drawn.  It does not necessarily
107      mean that any waiting is/will be done.  Also note that if we're
108      using a root mode, canstartwait is instantly turned on, as we aren't
109      going to be getting Expose/Configure events on the root window */
110 
111   done = retval = waiting = canstartwait = 0;
112 
113   if (useroot) canstartwait = 1;
114   else if (mainW) {           /* if mainW iconified, start wait now */
115     XWindowAttributes xwa;
116     XSync(theDisp, False);
117     if (XGetWindowAttributes(theDisp, mainW, &xwa)) {
118       if (xwa.map_state != IsViewable) canstartwait = 1;
119     }
120   }
121 
122   while (!done) {
123 
124     if (waitsec >= 0.0 && canstartwait && !waiting && XPending(theDisp)==0) {
125       /* we wanna wait, we can wait, we haven't started waiting yet, and
126 	 all pending events (ie, drawing the image the first time)
127 	 have been dealt with:  START WAITING */
128 #ifdef USE_TICKS
129       waitsec_ticks = (clock_t)(waitsec * CLK_TCK);
130       orgtime_ticks = times(NULL);  /* unclear if NULL valid, but OK on Linux */
131 #else
132       orgtime = time(NULL);
133 #endif
134       waiting = 1;
135     }
136 
137 
138     /* if there's an XEvent pending *or* we're not doing anything
139        in real-time (polling, flashing the selection, etc.) get next event */
140     if ((waitsec<0.0 && !polling && !HaveSelection()) || XPending(theDisp)>0)
141     {
142       XNextEvent(theDisp, &event);
143       retval = HandleEvent(&event,&done);
144     }
145 
146     else {                      /* no events.  check wait status */
147       if (HaveSelection()) {
148 	DrawSelection(0);
149 	DrawSelection(1);
150 	XFlush(theDisp);
151 	Timer(200);             /* milliseconds */
152       }
153 
154       if (polling) {
155 	if (CheckPoll(2)) return POLLED;
156 	else if (!XPending(theDisp)) sleep(1);
157       }
158 
159       if (waitsec>=0.0 && waiting) {
160 #ifdef USE_TICKS
161         curtime_ticks = times(NULL);   /* value in ticks */
162         if (curtime_ticks < orgtime_ticks) {
163           /* clock ticks rolled over:  need to correct for that (i.e.,
164            *  curtime_ticks is presumably quite small, while orgtime_ticks
165            *  should be close to LONG_MAX, so do math accordingly--any way
166            *  to check whether clock_t is *not* a signed long?) */
167           elapsed_ticks = curtime_ticks + (LONG_MAX - orgtime_ticks);
168         } else
169           elapsed_ticks = curtime_ticks - orgtime_ticks;
170         remaining_interval = waitsec_ticks - elapsed_ticks;
171         if (remaining_interval >= (clock_t)(1 * CLK_TCK))
172           sleep(1);
173         else {
174           /* less than one second remaining:  do delay in msec, then return */
175           Timer((remaining_interval * 1000L) / CLK_TCK);  /* can't overflow */
176           return waitloop? NEXTLOOP : NEXTQUIT;
177         }
178 #else
179         curtime = time(NULL);          /* value in seconds */
180 	if (curtime - orgtime < (time_t)waitsec)
181           sleep(1);
182 	else
183           return waitloop? NEXTLOOP : NEXTQUIT;
184 #endif
185       }
186     }
187   }  /* while (!done) */
188 
189   if (!useroot && origcropvalid) WUnCrop();
190   origcropvalid = 0;
191 
192   return(retval);
193 }
194 
195 
196 
197 /****************/
HandleEvent(event,donep)198 int HandleEvent(event, donep)
199      XEvent *event;
200      int    *donep;
201 {
202   static int wasInfoUp=0, wasCtrlUp=0, wasDirUp=0, wasGamUp=0, wasPsUp=0;
203 #ifdef HAVE_JPEG
204   static int wasJpegUp=0;
205 #endif
206 #ifdef HAVE_JP2K
207   static int wasJp2kUp=0;
208 #endif
209 #ifdef HAVE_TIFF
210   static int wasTiffUp=0;
211 #endif
212 #ifdef HAVE_PNG
213   static int wasPngUp=0;
214 #endif
215 #ifdef HAVE_PCD
216   static int wasPcdUp=0;
217 #endif
218 #ifdef HAVE_PIC2
219   static int wasPic2Up=0;
220 #endif
221 #ifdef HAVE_MGCSFX
222   static int wasMgcSfxUp=0;
223 #endif
224 
225   static int mainWKludge=0;  /* force first mainW expose after a mainW config
226 				to redraw all of mainW */
227 
228   int done=0, retval=0;
229 
230 
231   if (DEBUG) debugEvent((XEvent *) event);
232 
233 
234   switch (event->type) {
235 
236   case ButtonPress:
237     lastEventTime = ((XButtonEvent *) event)->time;
238     handleButtonEvent(event, &done, &retval);
239     break;
240 
241 
242   case KeyRelease:
243   case KeyPress:
244     lastEventTime = ((XKeyEvent *) event)->time;
245     handleKeyEvent(event, &done, &retval);
246     break;
247 
248 
249   case Expose: {
250     XExposeEvent *exp_event = (XExposeEvent *) event;
251     int x,y,w,h;
252     Window win;
253 
254 #ifdef VMS
255     static int borders_sized = 0;
256 
257     if (!borders_sized  && !useroot && exp_event->window == mainW) {
258       /*
259        * Initial expose of main window, find the size of the ancestor
260        * window just prior to the root window and adjust saved size
261        * of display so that maximize functions will allow for window
262        * decorations.
263        */
264       int status, count, mwid, mhgt, x, y, w, h, b, d, mbrd;
265       Window root, parent, *children, crw = exp_event->window;
266       borders_sized = 1;
267       status = XGetGeometry(theDisp, crw,
268 			    &root, &x, &y, &mwid, &mhgt, &mbrd, &d);
269 
270       for ( parent = crw, w=mwid, h=mhgt;
271 	   status && (parent != root) && (parent != vrootW); ) {
272 	crw = parent;
273 	status = XQueryTree ( theDisp, crw, &root, &parent,
274 			     &children, &count );
275 	if ( children != NULL ) XFree ( children );
276       }
277       status = XGetGeometry(theDisp, crw, &root, &x, &y, &w, &h, &b, &d);
278       if ( status ) {
279 	dispWIDE = dispWIDE + mwid - w + (2*b);
280 	dispHIGH = dispHIGH + mhgt - h + b;
281 	/*printf("New display dims: %d %d\n", dispWIDE, dispHIGH ); */
282       }
283     }
284 #endif
285 
286 
287     win = exp_event->window;
288     x = exp_event->x;      y = exp_event->y;
289     w = exp_event->width;  h = exp_event->height;
290 
291     if (PUCheckEvent  (event)) break;   /* event has been processed */
292     if (PSCheckEvent  (event)) break;   /* event has been processed */
293 
294 #ifdef HAVE_JPEG
295     if (JPEGCheckEvent(event)) break;   /* event has been processed */
296 #endif
297 
298 #ifdef HAVE_JP2K
299     if (JP2KCheckEvent(event)) break;   /* event has been processed */
300 #endif
301 
302 #ifdef HAVE_TIFF
303     if (TIFFCheckEvent(event)) break;   /* event has been processed */
304 #endif
305 
306 #ifdef HAVE_PNG
307     if (PNGCheckEvent (event)) break;   /* event has been processed */
308 #endif
309 
310     if (PCDCheckEvent(event)) break;    /* event has been processed */
311 
312 #ifdef HAVE_PIC2
313     if (PIC2CheckEvent(event)) break;   /* event has been processed */
314 #endif
315 
316 #ifdef HAVE_PCD
317     if (PCDCheckEvent (event)) break;   /* event has been processed */
318 #endif
319 
320 #ifdef HAVE_MGCSFX
321     if (MGCSFXCheckEvent(event)) break; /* event has been processed */
322 #endif
323 
324 #ifdef TV_MULTILINGUAL
325     if (CharsetCheckEvent(event)) break; /* event has been processed */
326 #endif
327 
328     if (GamCheckEvent (event)) break;   /* event has been processed */
329     if (BrowseCheckEvent (event, &retval, &done)) break;   /* event eaten */
330     if (TextCheckEvent   (event, &retval, &done)) break;   /* event eaten */
331 
332     /* if the window doesn't do intelligent redraw, drop but last expose */
333     if (exp_event->count>0 &&
334 	win != mainW && win != ctrlW &&	win != dirW && win != infoW) break;
335 
336 
337 
338     if (win == mainW && CheckForConfig()) {
339       if (DEBUG) fprintf(stderr,"Expose mainW ignored.  Config pending.\n");
340       break;
341     }
342 
343 
344 
345 
346     if (win==mainW || win==ctrlW || win==dirW || win==infoW) {
347       /* must be a 'smart' window.  group exposes into an expose 'region' */
348       int           count;
349       Region        reg;
350       XRectangle    rect;
351       XEvent        evt;
352 
353       xvbcopy((char *) exp_event, (char *) &evt, sizeof(XEvent));
354       reg = XCreateRegion();
355       count = 0;
356 
357       do {
358 	rect.x      = evt.xexpose.x;
359 	rect.y      = evt.xexpose.y;
360 	rect.width  = evt.xexpose.width;
361 	rect.height = evt.xexpose.height;
362 	XUnionRectWithRegion(&rect, reg, reg);
363 	count++;
364 
365 	if (DEBUG) debugEvent((XEvent *) &evt);
366 
367       } while (XCheckWindowEvent(theDisp,evt.xexpose.window,
368 				 ExposureMask, &evt));
369 
370       XClipBox(reg, &rect);  /* bounding box of region */
371       XSetRegion(theDisp, theGC, reg);
372 
373       x = rect.x;      y = rect.y;
374       w = rect.width;  h = rect.height;
375 
376       if (DEBUG) fprintf(stderr,"window: 0x%08x  collapsed %d expose events\n",
377 			 (u_int) exp_event->window, count);
378       if (DEBUG) fprintf(stderr,"        region bounding box: %d,%d %dx%d\n",
379 			 x, y, w, h);
380 
381 
382       if (win == mainW) {
383 	if (DEBUG) fprintf(stderr,"EXPOSE:  ");
384 	if (!CheckForConfig()) {
385 
386 	  if (mainWKludge) {
387 	    if (DEBUG) fprintf(stderr, "Using mainWKludge\n");
388 	    x = 0; y = 0;  w = eWIDE;  h = eHIGH;
389 	    XSetClipMask(theDisp, theGC, None);
390 	    mainWKludge = 0;
391 	  }
392 
393 	  if (DEBUG) fprintf(stderr,"No configs pending.\n");
394 	  /* if (DEBUG) XClearArea(theDisp, mainW, x,y,w,h, False); */
395 	  DrawWindow(x,y,w,h);
396 
397 	  if (HaveSelection()) DrawSelection(0);
398 
399 	  canstartwait = 1;  /* finished drawing */
400 	  XSync(theDisp, False);
401 	}
402 	else
403 	  if (DEBUG) fprintf(stderr,"Ignored.  Config pending\n");
404       }
405 
406       else if (win == infoW)          RedrawInfo(x,y,w,h);
407       else if (win == ctrlW)          RedrawCtrl(x,y,w,h);
408       else if (win == dirW)           RedrawDirW(x,y,w,h);
409 
410       XSetClipMask(theDisp, theGC, None);
411       XDestroyRegion(reg);
412     }
413 
414     else if (win == nList.win)      LSRedraw(&nList,0);
415     else if (win == nList.scrl.win) SCRedraw(&nList.scrl);
416     else if (win == dList.win)      LSRedraw(&dList,0);
417     else if (win == dList.scrl.win) SCRedraw(&dList.scrl);
418     else if (win == dnamW)          RedrawDNamW();
419   }
420     break;
421 
422 
423 
424   case ClientMessage: {
425     Atom proto, delwin;
426     XClientMessageEvent *client_event = (XClientMessageEvent *) event;
427 
428     if (PUCheckEvent (event)) break;   /* event has been processed */
429 
430     proto = XInternAtom(theDisp, "WM_PROTOCOLS", FALSE);
431     delwin = XInternAtom(theDisp, "WM_DELETE_WINDOW", FALSE);
432 
433     if (client_event->message_type == proto &&
434 	client_event->data.l[0]    == delwin) {
435       /* it's a WM_DELETE_WINDOW event */
436 
437       if (BrowseDelWin(client_event->window)) break;
438       if (TextDelWin(client_event->window)) break;
439 #ifdef TV_MULTILINGUAL
440       if (CharsetDelWin(client_event->window)) break;
441 #endif
442 
443       if      (client_event->window == infoW) InfoBox(0);
444       else if (client_event->window == gamW)  GamBox(0);
445       else if (client_event->window == ctrlW) CtrlBox(0);
446       else if (client_event->window == dirW)  DirBox(0);
447       else if (client_event->window == psW)   PSDialog(0);
448 
449 #ifdef HAVE_JPEG
450       else if (client_event->window == jpegW) JPEGDialog(0);
451 #endif
452 
453 #ifdef HAVE_JP2K
454       else if (client_event->window == jp2kW) JP2KDialog(0);
455 #endif
456 
457 #ifdef HAVE_TIFF
458       else if (client_event->window == tiffW) TIFFDialog(0);
459 #endif
460 
461 #ifdef HAVE_PNG
462       else if (client_event->window == pngW)  PNGDialog(0);
463 #endif
464 
465       else if (client_event->window == pcdW)  PCDDialog(0);
466 
467 #ifdef HAVE_PIC2
468       else if (client_event->window == pic2W) PIC2Dialog(0);
469 #endif
470 
471 #ifdef HAVE_PCD
472       else if (client_event->window == pcdW)  PCDDialog(0);
473 #endif
474 
475 #ifdef HAVE_MGCSFX
476       else if (client_event->window == mgcsfxW) MGCSFXDialog(0);
477 #endif
478 
479       else if (client_event->window == mainW) Quit(0);
480     }
481   }
482     break;
483 
484 
485   case ConfigureNotify: {
486     XConfigureEvent *cevt = (XConfigureEvent *) event;
487     Window           win  = cevt->window;
488 
489     if (BrowseCheckEvent (event, &retval, &done)) break;   /* event eaten */
490     if (TextCheckEvent   (event, &retval, &done)) break;   /* event eaten */
491 
492     /*
493      * if it's a configure of one of the 'major' xv windows, update the
494      * NORMAL position hints for this window appropriately (so it can be
495      * iconified and deiconified correctly)
496      */
497 
498     if (win==ctrlW || win==gamW || win==infoW || win==mainW || win==dirW) {
499       XSizeHints hints;
500 
501 #define BAD_IDEA
502 #ifdef BAD_IDEA
503       /*
504        * if there is a virtual window manager running (e.g., tvtwm / olvwm),
505        * we're going to get 'cevt' values in terms of the
506        * 'real' root window (the one that is the size of the screen).
507        * We'll want to translate them into values that are in terms of
508        * the 'virtual' root window (the 'big' one)
509        */
510 
511       if (vrootW != rootW) {
512 	int x1,y1;
513 	Window child;
514 
515 	XTranslateCoordinates(theDisp, rootW, vrootW, cevt->x, cevt->y,
516 			      &x1, &y1, &child);
517 	if (DEBUG) fprintf(stderr,"  CONFIG trans %d,%d root -> %d,%d vroot\n",
518 			   cevt->x, cevt->y, x1, y1);
519 	cevt->x = x1;  cevt->y = y1;
520       }
521 #endif
522 
523 #ifndef VMS
524       /* read hints for this window and adjust any position hints, but
525          only if this is a 'synthetic' event sent to us by the WM
526 	 ('real' events from the server have useless x,y info, since the
527 	 mainW has been reparented by the WM) */
528 
529       if (cevt->send_event &&
530 	  XGetNormalHints(theDisp, cevt->window, &hints)) {
531 
532 	if (DEBUG) fprintf(stderr,"  CONFIG got hints (0x%x  %d,%d)\n",
533 			   (u_int) hints.flags, hints.x, hints.y);
534 
535 	hints.x = cevt->x;
536 	hints.y = cevt->y;
537 	XSetNormalHints(theDisp, cevt->window, &hints);
538 
539 	if (DEBUG) fprintf(stderr,"  CONFIG set hints (0x%x  %d,%d)\n",
540 		(u_int) hints.flags, hints.x, hints.y);
541       }
542 #endif
543     }
544 
545 
546     if (cevt->window == mainW) {
547 
548       /* when we load a new image, we may get *2* configure events from
549        * the 'XMoveResizeWindow()' call.  One at the 'old' size, in the
550        * new position, and one at the new size at the new position.
551        * We want to *IGNORE* the one at the old size, as we don't want to
552        * call Resize() for images that we won't need.
553        *
554        * Note that we may also only get *one* Configure event (if the window
555        * didn't move), and we may also only get *one* Configure event in
556        * some unasked for size (if the asked for size wasn't acceptable to
557        * the window manager), and we may also get *no* Configure event
558        * if the window doesn't move or change size
559        *
560        * This sucks!
561        *
562        * So, if we have just loaded an image, and we get a Synthetic conf
563        * that is not the desired size (eWIDExeHIGH), ignore it, as it's
564        * just the conf generated by moving the old window.  And stop
565        * ignoring further config events
566        *
567        * EVIL KLUDGE:  do *not* ignore configs that are <100x100.  Not
568        * ignoring them won't be a big performance problem, and it'll get
569        * around the 'I only got one config in the wrong size' problem when
570        * initially displaying small images
571        */
572 
573       if (cevt->width<100 && cevt->height < 100 ) ignoreConfigs = 0;
574 
575 /* fprintf(stderr,"***mainw, ignore=%d, send_event=%d, evtSize=%d,%d, size=%d,%d\n", ignoreConfigs, cevt->send_event, cevt->width, cevt->height, eWIDE, eHIGH); */
576 
577       if (ignoreConfigs==1 && cevt->send_event &&
578 	  (cevt->width != eWIDE || cevt->height != eHIGH)) {
579 	ignoreConfigs=0;        /* ignore this one only */
580 	break;
581       }
582 
583       ignoreConfigs = 0;
584 
585 
586       if (!rotatesLeft) {
587 	if (CheckForConfig()) {
588 	  if (DEBUG) fprintf(stderr,"more configs pending.  ignored\n");
589 	}
590 
591 	else {
592 	  XEvent xev;
593 	  if (DEBUG) fprintf(stderr,"No configs pend.");
594 
595 	  if (cevt->width == eWIDE && cevt->height == eHIGH) {
596 	    if (DEBUG) fprintf(stderr,"No redraw\n");
597 	  }
598 	  else {
599 	    if (DEBUG) fprintf(stderr,"Do full redraw\n");
600 
601 	    Resize(cevt->width, cevt->height);
602 
603 	    /* eat any pending expose events and do a full redraw */
604 	    while (XCheckTypedWindowEvent(theDisp, mainW, Expose, &xev)) {
605 	      XExposeEvent *exp = (XExposeEvent *) &xev;
606 
607 	      if (DEBUG)
608 		fprintf(stderr,"  ate expose (%s) (count=%d) %d,%d %dx%d\n",
609 			exp->send_event ? "synth" : "real", exp->count,
610 			exp->x, exp->y, exp->width, exp->height);
611 	    }
612 
613 	    DrawWindow(0,0,cevt->width, cevt->height);
614 
615 	    if (HaveSelection()) DrawSelection(0);
616             canstartwait=1;
617 	    XSync(theDisp, False);
618 	    SetCursors(-1);
619 	  }
620 	}
621       }
622 
623       if (rotatesLeft>0) {
624 	rotatesLeft--;
625 	if (!rotatesLeft) mainWKludge = 1;
626       }
627       if (!rotatesLeft) SetCursors(-1);
628     }
629 
630   }
631     break;
632 
633 
634 
635   case CirculateNotify:
636   case DestroyNotify:
637   case GravityNotify:       break;
638 
639   case MapNotify: {
640     XMapEvent *map_event = (XMapEvent *) event;
641 
642     if (map_event->window == mainW ||
643 	(map_event->window == ctrlW && dispMode != RMB_WINDOW)) {
644       if (DEBUG) fprintf(stderr,"map event received on mainW/ctrlW\n");
645 
646       if (autoclose) {
647 	if (autoclose>1) autoclose -= 2;  /* grab kludge */
648 	if (wasInfoUp) { InfoBox(wasInfoUp);     wasInfoUp=0; }
649 	if (wasCtrlUp) { CtrlBox(wasCtrlUp);     wasCtrlUp=0; }
650 	if (wasDirUp)  { DirBox(wasDirUp);       wasDirUp=0; }
651 	UnHideBrowseWindows();
652 	UnHideTextWindows();
653 	if (wasGamUp)  { GamBox(wasGamUp);       wasGamUp=0; }
654 	if (wasPsUp)   { PSDialog(wasPsUp);      wasPsUp=0; }
655 #ifdef HAVE_JPEG
656 	if (wasJpegUp) { JPEGDialog(wasJpegUp);  wasJpegUp=0; }
657 #endif
658 #ifdef HAVE_JP2K
659 	if (wasJp2kUp) { JP2KDialog(wasJpegUp);  wasJp2kUp=0; }
660 #endif
661 #ifdef HAVE_TIFF
662 	if (wasTiffUp) { TIFFDialog(wasTiffUp);  wasTiffUp=0; }
663 #endif
664 #ifdef HAVE_PNG
665 	if (wasPngUp)  { PNGDialog(wasPngUp);    wasPngUp=0; }
666 #endif
667 #ifdef HAVE_PCD
668 	if (wasPcdUp)  { PCDDialog(wasPcdUp);    wasPcdUp=0; }
669 #endif
670 #ifdef HAVE_PIC2
671 	if (wasPic2Up) { PIC2Dialog(wasPic2Up);  wasPic2Up=0; }
672 #endif
673 #ifdef HAVE_MGCSFX
674 	if (wasMgcSfxUp) { MGCSFXDialog(wasMgcSfxUp);  wasMgcSfxUp=0; }
675 #endif
676       }
677     }
678   }
679     break;
680 
681 
682   case UnmapNotify: {
683     XUnmapEvent *unmap_event = (XUnmapEvent *) event;
684 
685     if (unmap_event->window == mainW ||
686 	(unmap_event->window == ctrlW && dispMode != RMB_WINDOW)) {
687       if (DEBUG) fprintf(stderr,"unmap event received on mainW/ctrlW\n");
688       if (DEBUG) fprintf(stderr,"dispMode = %d\n", dispMode);
689 
690       /* don't do it if we've just switched to a root mode */
691       if ((unmap_event->window == mainW && dispMode == 0) ||
692 	  (unmap_event->window == ctrlW && dispMode != 0)) {
693 
694 	if (autoclose) {
695 	  if (autoclose>1) autoclose -= 2;  /* grab kludge */
696 
697 	  if (unmap_event->window == mainW) {
698 	    if (ctrlUp) { wasCtrlUp = ctrlUp;  CtrlBox(0); }
699 	  }
700 
701 	  if (infoUp) { wasInfoUp = infoUp;  InfoBox(0); }
702 	  if (dirUp)  { wasDirUp  = dirUp;   DirBox(0); }
703 	  HideBrowseWindows();
704 	  HideTextWindows();
705 	  if (gamUp)  { wasGamUp  = gamUp;   GamBox(0); }
706 	  if (psUp)   { wasPsUp   = psUp;    PSDialog(0); }
707 #ifdef HAVE_JPEG
708 	  if (jpegUp) { wasJpegUp = jpegUp;  JPEGDialog(0); }
709 #endif
710 #ifdef HAVE_JP2K
711 	  if (jp2kUp) { wasJp2kUp = jp2kUp;  JP2KDialog(0); }
712 #endif
713 #ifdef HAVE_TIFF
714 	  if (tiffUp) { wasTiffUp = tiffUp;  TIFFDialog(0); }
715 #endif
716 #ifdef HAVE_PNG
717 	  if (pngUp)  { wasPngUp  = pngUp;   PNGDialog(0); }
718 #endif
719 #ifdef HAVE_PCD
720 	  if (pcdUp)  { wasPcdUp = pcdUp;    PCDDialog(0); }
721 #endif
722 #ifdef HAVE_PIC2
723 	  if (pic2Up) { wasPic2Up = pic2Up;  PIC2Dialog(0); }
724 #endif
725 #ifdef HAVE_MGCSFX
726 	  if (mgcsfxUp) { wasMgcSfxUp = mgcsfxUp;  MGCSFXDialog(0); }
727 #endif
728 	}
729       }
730     }
731   }
732     break;
733 
734   case ReparentNotify: {
735     XReparentEvent *reparent_event = (XReparentEvent *) event;
736 
737     if (DEBUG) {
738       fprintf(stderr,"Reparent: mainW=%x ->win=%x ->ev=%x  ->parent=%x  ",
739 	      (u_int) mainW,                 (u_int) reparent_event->window,
740 	      (u_int) reparent_event->event, (u_int) reparent_event->parent);
741       fprintf(stderr,"%d,%d\n", reparent_event->x, reparent_event->y);
742     }
743 
744     if (reparent_event->window == mainW) {
745       ch_offx = reparent_event->x;  /* offset required for ChangeAttr call */
746       ch_offy = reparent_event->y;
747 
748       p_offx = p_offy = 0;          /* topleft correction for WMs titlebar */
749 
750       if (ch_offx == 0 && ch_offy == 0) {
751 	/* looks like the user is running MWM or OLWM */
752 
753 	XWindowAttributes xwa;
754 
755 	/* first query the attributes of mainW.  x,y should be the offset
756 	   from the parent's topleft corner to the windows topleft.
757 	   OLWM puts the info here */
758 
759 	XSync(theDisp, False);
760 	XGetWindowAttributes(theDisp, mainW, &xwa);
761 
762 	if (DEBUG)
763 	  fprintf(stderr,"XGetAttr: mainW %d,%d %dx%d\n", xwa.x, xwa.y,
764 		  xwa.width, xwa.height);
765 
766 	if (xwa.x == 0 && xwa.y == 0) {
767 	  /* MWM, at least mine, puts 0's in those fields.  To get the
768 	     info, we'll have to query the parent window */
769 
770 	  XSync(theDisp, False);
771 	  XGetWindowAttributes(theDisp, reparent_event->parent, &xwa);
772 
773 	  if (DEBUG)
774 	    fprintf(stderr,"XGetAttr: parent %d,%d %dx%d\n", xwa.x, xwa.y,
775 		    xwa.width, xwa.height);
776 	}
777 	else {
778 	  /* KLUDGE:  if we're running olwm, xwa.{x,y} won't be 0,0.
779 	     in olwm, the window drifts down and right each time
780 	     SetWindowPos() is called.  God knows why.  Anyway, I'm
781 	     inserting a kludge here to increase 'ch_offx' and 'ch_offy'
782 	     by bwidth so that this drifting won't happen.  No doubt this'll
783 	     screw up behavior on some *other* window manager, but it should
784 	     work with TWM, OLWM, and MWM (the big three) */
785 	  ch_offx += bwidth;
786 	  ch_offy += bwidth;
787 	}
788 
789 	p_offx = xwa.x;
790 	p_offy = xwa.y;
791       }
792 
793       /* Gather info to keep right border inside */
794       {
795 	Window current;
796 	Window root_r;
797 	Window parent_r;
798 	Window *children_r;
799 	unsigned int nchildren_r;
800 	XWindowAttributes xwa;
801 
802 	parent_r=mainW;
803 	current=mainW;
804 	do {
805 	  current=parent_r;
806 	  XQueryTree(theDisp, current, &root_r, &parent_r,
807 		     &children_r, &nchildren_r);
808 	  if (children_r!=NULL) {
809 	    XFree(children_r);
810 	  }
811 	} while (parent_r!=root_r && parent_r!=vrootW);
812 	XGetWindowAttributes(theDisp, current, &xwa);
813 	debkludge_offx = eWIDE-xwa.width+p_offx;
814 	debkludge_offy = eHIGH-xwa.height+p_offy;
815       }
816 
817 #if 1
818       /* FIXME: if we want to do this, we first have to wait for a configure
819        * notify to avoid a race condition because the location might be in-
820        * correct if the window manager does placement after managing the window.
821        */
822       /* move window around a bit... */
823       {
824 	XWindowAttributes xwa;
825 
826 	GetWindowPos(&xwa);
827 	//fprintf(stderr, "RAC: orig window pos %d,%d\n", xwa.x, xwa.y);
828 
829 	xwa.width = eWIDE;  xwa.height = eHIGH;
830 	//fprintf(stderr, "RAC: image size now %d,%d\n", xwa.width, xwa.height);
831 
832 	/* try to keep the damned thing on-screen, if possible */
833 	if (xwa.x + xwa.width  > vrWIDE) xwa.x = vrWIDE - xwa.width;
834 	if (xwa.y + xwa.height > vrHIGH) xwa.y = vrHIGH - xwa.height;
835 	if (xwa.x < 0) xwa.x = 0;
836 	if (xwa.y < 0) xwa.y = 0;
837 
838 	//fprintf(stderr, "RAC: moving window to %d,%d\n", xwa.x, xwa.y);
839 	SetWindowPos(&xwa);
840       }
841 #endif
842     }
843   }
844     break;
845 
846 
847   case EnterNotify:
848   case LeaveNotify: {
849     XCrossingEvent *cross_event = (XCrossingEvent *) event;
850     if (cross_event->window == mainW || 0
851 	/* (cross_event->window == gamW && cmapInGam) */ ) {
852 
853       if (cross_event->type == EnterNotify && cross_event->window == mainW) {
854 	zoomCurs(cross_event->state);
855       }
856 
857 
858       if (cross_event->type == EnterNotify && LocalCmap && !ninstall)
859 	XInstallColormap(theDisp,LocalCmap);
860 
861       if (cross_event->type == LeaveNotify && LocalCmap && !ninstall)
862 	XUninstallColormap(theDisp,LocalCmap);
863     }
864   }
865     break;
866 
867 
868   case SelectionClear:  break;
869 
870   case SelectionRequest:
871     {
872       XSelectionRequestEvent *xsrevt = (XSelectionRequestEvent *) event;
873       XSelectionEvent  xse;
874 
875       if (xsrevt->owner     != ctrlW      ||
876 	  xsrevt->selection != XA_PRIMARY ||
877 	  xsrevt->target    != XA_STRING) {  /* can't do it. */
878 	xse.property = None;
879       }
880       else {
881 	if (xsrevt->property == None) xse.property = xsrevt->target;
882 	                         else xse.property = xsrevt->property;
883 
884 	if (xse.property != None) {
885           xerrcode = 0;
886 	  XChangeProperty(theDisp, xsrevt->requestor, xse.property,
887 			  XA_STRING, 8, PropModeReplace,
888 			  (byte *) ((xevPriSel) ? xevPriSel           : "\0"),
889 			  (int)    ((xevPriSel) ? strlen(xevPriSel)+1 : 1));
890           XSync(theDisp, False);
891           if (!xerrcode) xse.property = None;
892 	}
893       }
894 
895       xse.type       = SelectionNotify;
896       xse.send_event = True;
897       xse.display    = theDisp;
898       xse.requestor  = xsrevt->requestor;
899       xse.selection  = xsrevt->selection;
900       xse.target     = xsrevt->target;
901       xse.time       = xsrevt->time;
902       XSendEvent(theDisp, xse.requestor, False, NoEventMask, (XEvent *) &xse);
903       XSync(theDisp, False);
904     }
905     break;
906 
907 
908 
909   default: break;		/* ignore unexpected events */
910   }  /* switch */
911 
912   frominterrupt = 0;
913   *donep = done;
914   return(retval);
915 }
916 
917 
918 /***********************************/
SelectDispMB(i)919 static void SelectDispMB(i)
920      int i;
921 {
922   /* called to handle selection of a dispMB item */
923 
924   if (i<0 || i>=DMB_MAX) return;
925 
926   if (dispMB.dim[i]) return;    /* disabled */
927 
928   if (i>=DMB_RAW && i<=DMB_SMOOTH) {
929     if      (i==DMB_RAW)  epicMode = EM_RAW;
930     else if (i==DMB_DITH) epicMode = EM_DITH;
931     else                  epicMode = EM_SMOOTH;
932 
933     SetEpicMode();
934     GenerateEpic(eWIDE, eHIGH);
935     DrawEpic();
936     SetCursors(-1);
937   }
938 
939   else if (i==DMB_COLRW) {   /* toggle rw on/off */
940     dispMB.flags[i] = !dispMB.flags[i];
941     allocMode = (dispMB.flags[i]) ? AM_READWRITE : AM_READONLY;
942     ChangeCmapMode(colorMapMode, 1, 0);
943   }
944 
945   else if (i>=DMB_COLNORM && i<=DMB_COLSTDC && !dispMB.flags[i]) {
946     switch (i) {
947     case DMB_COLNORM:
948       ChangeCmapMode(CM_NORMAL, 1, 0);
949       defaultCmapMode = CM_NORMAL;
950       break;
951     case DMB_COLPERF:
952       ChangeCmapMode(CM_PERFECT,1, 0);
953       defaultCmapMode = CM_PERFECT;
954       break;
955     case DMB_COLOWNC:
956       ChangeCmapMode(CM_OWNCMAP,1, 0);
957       defaultCmapMode = CM_OWNCMAP;
958       break;
959     case DMB_COLSTDC:
960       ChangeCmapMode(CM_STDCMAP,1, 0);
961       defaultCmapMode = CM_STDCMAP;
962       break;
963     }
964   }
965 }
966 
967 
968 /***********************************/
SelectRootMB(i)969 static void SelectRootMB(i)
970      int i;
971 {
972   /* called to handle selection of a rootMB item */
973 
974   if (i<0 || i>=RMB_MAX) return;
975   if (rootMB.flags[i])   return;
976   if (rootMB.dim[i])     return;
977 
978   dispMode = i;
979 
980   /* move checkmark */
981   for (i=RMB_WINDOW; i<RMB_MAX; i++) rootMB.flags[i] = 0;
982   rootMB.flags[dispMode] = 1;
983 
984   HandleDispMode();
985 }
986 
987 
988 /***********************************/
Select24to8MB(i)989 static void Select24to8MB(i)
990      int i;
991 {
992   if (i<0 || i>=CONV24_MAX) return;
993 
994   if (conv24MB.dim[i]) return;
995 
996   if (i==CONV24_8BIT || i==CONV24_24BIT) {
997     if (i != picType) {
998       Change824Mode(i);
999       if (i==CONV24_8BIT && state824==0) state824 = 1;  /* did 24->8 */
1000       else if (i==CONV24_24BIT && state824==1) {
1001 	/* went 24->8->24 */
1002 	char buf[512];
1003 
1004 	sprintf(buf,"Warning:  You appear to have taken a 24-bit ");
1005 	strcat(buf, "image, turned it to an 8-bit image, and turned ");
1006 	strcat(buf, "it back into a 24-bit image.  Understand that ");
1007 	strcat(buf, "image data has probably been lost in this ");
1008 	strcat(buf, "transformation.  You *may* want to reload the ");
1009 	strcat(buf, "original image to avoid this problem.");
1010 
1011 	ErrPopUp(buf, "\nI Know!");
1012 
1013 	state824 = 2;   /* shut up until next image is loaded */
1014       }
1015     }
1016   }
1017 
1018   else if (i==CONV24_LOCK) {
1019     conv24MB.flags[i] = !conv24MB.flags[i];
1020   }
1021 
1022   else if (i>=CONV24_FAST && i<=CONV24_BEST) {
1023     conv24 = i;
1024     for (i=CONV24_FAST; i<=CONV24_BEST; i++) {
1025       conv24MB.flags[i] = (i==conv24);
1026     }
1027   }
1028 }
1029 
1030 
1031 /***********************************/
SelectWindowMB(i)1032 static void SelectWindowMB(i)
1033      int i;
1034 {
1035   if (i<0 || i>=WMB_MAX) return;
1036   if (windowMB.dim[i]) return;
1037 
1038   switch (i) {
1039   case WMB_BROWSE:
1040     if (strlen(searchdir)) chdir(searchdir);
1041     else chdir(initdir);
1042     OpenBrowse();
1043     break;
1044 
1045   case WMB_COLEDIT:  GamBox (!gamUp);   break;
1046   case WMB_INFO:     InfoBox(!infoUp);  break;
1047 
1048   case WMB_COMMENT:
1049     if (!commentUp) OpenCommentText();
1050     else CloseCommentText();
1051     break;
1052 
1053   case WMB_TEXTVIEW:  textViewCmd();  break;
1054   case WMB_ABOUTXV:   ShowLicense();  break;
1055   case WMB_KEYHELP:   ShowKeyHelp();  break;
1056 
1057   default:  break;
1058   }
1059 }
1060 
1061 
1062 /***********************************/
SelectSizeMB(i)1063 static void SelectSizeMB(i)
1064      int i;
1065 {
1066   int w,h;
1067 
1068   if (i<0 || i>=SZMB_MAX) return;
1069   if (sizeMB.dim[i]) return;
1070 
1071   switch (i) {
1072   case SZMB_NORM:
1073     if (cWIDE>maxWIDE || cHIGH>maxHIGH) {
1074       double r,wr,hr;
1075       wr = ((double) cWIDE) / maxWIDE;
1076       hr = ((double) cHIGH) / maxHIGH;
1077 
1078       r = (wr>hr) ? wr : hr;   /* r is the max(wr,hr) */
1079       w = (int) ((cWIDE / r) + 0.5);
1080       h = (int) ((cHIGH / r) + 0.5);
1081     }
1082     else { w = cWIDE;  h = cHIGH; }
1083 
1084     WResize(w, h);
1085     break;
1086 
1087   case SZMB_MAXPIC:   WMaximize();  break;
1088 
1089   case SZMB_MAXPECT:
1090     {
1091       int w1,h1;
1092       w1 = eWIDE;  h1 = eHIGH;
1093       eWIDE = dispWIDE;  eHIGH = dispHIGH;
1094       FixAspect(0,&w,&h);
1095       eWIDE = w1;  eHIGH = h1;  /* play it safe */
1096       WResize(w,h);
1097     }
1098     break;
1099 
1100   case SZMB_DOUBLE:  WResize(eWIDE*2,      eHIGH*2);       break;
1101   case SZMB_HALF:    WResize(eWIDE/2,      eHIGH/2);       break;
1102   case SZMB_M10:     WResize((eWIDE*9)/10, (eHIGH*9)/10);  break;
1103 
1104   case SZMB_P10:
1105     w = (eWIDE*11)/10;  h = (eHIGH*11)/10;
1106     if (w==eWIDE) w++;
1107     if (h==eHIGH) h++;
1108     WResize(w,h);
1109     break;
1110 
1111 
1112   case SZMB_SETSIZE:  setSizeCmd();  break;
1113   case SZMB_ASPECT:   FixAspect(1, &w, &h);  WResize(w,h);  break;
1114 
1115   case SZMB_4BY3:
1116     w = eWIDE;  h = (w * 3) / 4;
1117     if (h>maxHIGH) { h = eHIGH;  w = (h*4)/3; }
1118     WResize(w,h);
1119     break;
1120 
1121   case SZMB_INTEXP:
1122     {
1123       /* round  (eWIDE/cWIDE),(eHIGH/cHIGH) to nearest
1124 	 integer expansion/compression values */
1125 
1126       double w,h;
1127 
1128       if (eWIDE >= cWIDE) {
1129 	w = ((double) eWIDE) / cWIDE;
1130 	w = floor(w + 0.5);
1131 	if (w < 1.0) w = 1.0;
1132       }
1133       else {     /* eWIDE < cWIDE */
1134 	int i;  double t,d,min,pick;
1135 
1136 	w = (double) eWIDE / (double) cWIDE;
1137 	min = 1.0;  pick=1.0;
1138 	for (i=1; i<cWIDE; i++) {
1139 	  t = 1.0 / (double) i;
1140 	  d = fabs(w - t);
1141 	  if (d < min) { pick = t;  min = d; }
1142 	  if (t < w) break;
1143 	}
1144 	w = pick;
1145       }
1146 
1147       if (eHIGH >= cHIGH) {
1148 	h = ((double) eHIGH) / cHIGH;
1149 	h = floor(h + 0.5);
1150 	if (h<1.0) h = 1.0;
1151       }
1152       else {   /* eHIGH < cHIGH */
1153 	int i;  double t,d,min,pick;
1154 
1155 	h = (double) eHIGH / (double) cHIGH;
1156 	min = 1.0;  pick=1.0;
1157 	for (i=1; i<cHIGH; i++) {
1158 	  t = 1.0 / (double) i;
1159 	  d = fabs(h - t);
1160 	  if (d < min) { pick = t;  min = d; }
1161 	  if (t < h) break;
1162 	}
1163 	h = pick;
1164       }
1165 
1166       WResize((int) (w*cWIDE), (int) (h*cHIGH));
1167     }
1168     break;
1169 
1170   default: break;
1171   }
1172 }
1173 
1174 
1175 /***********************************/
DoPrint()1176 static void DoPrint()
1177 {
1178   /* pops open appropriate dialog boxes, issues print command */
1179 
1180   int                i;
1181   char               txt[512], str[PRINTCMDLEN + 10];
1182   static const char *labels[] = { "\03Color", "\07Grayscale", " B/W", "\033Cancel" };
1183                           /* ^B ("\02") already used for moving cursor back */
1184 
1185   strcpy(txt, "Print:  Enter a command that will read a PostScript file ");
1186   strcat(txt, "from stdin and print it to the desired printer.\n\n");
1187 #ifndef VMS
1188   strcat(txt, "(e.g., 'lpr -Pname' on Unix systems)");
1189 #else
1190   strcat(txt, "(e.g., 'Print /Queue = XV_Queue' on a VMS system)");
1191 #endif
1192 
1193   i = GetStrPopUp(txt, labels, 4, printCmd, PRINTCMDLEN, "", 0);
1194   if (i == 3 || strlen(printCmd)==0) return;   /* CANCEL */
1195 
1196   if (dirUp == BLOAD) DirBox(0);
1197 
1198   SetDirSaveMode(F_FORMAT, F_PS);
1199   SetDirSaveMode(F_COLORS, i);
1200 
1201   if (printCmd[0] != '|' && printCmd[0] != '!')
1202     sprintf(str, "| %s", printCmd);
1203   else strcpy(str, printCmd);
1204 
1205   DirBox(BSAVE);
1206   SetDirFName(str);
1207 
1208   FakeButtonPress(&dbut[S_BOK]);
1209 }
1210 
1211 
1212 /***********************************/
debugEvent(e)1213 static void debugEvent(e)
1214      XEvent *e;
1215 {
1216   switch (e->type) {
1217   case ButtonPress:
1218   case ButtonRelease:
1219     fprintf(stderr,"DBGEVT: %s %s %d,%d, state=%x, button=%d\n",
1220 	    (e->type == ButtonPress) ? "ButtonPress  " : "ButtonRelease",
1221 	    win2name(e->xbutton.window), e->xbutton.x, e->xbutton.y,
1222 	    e->xbutton.state, e->xbutton.button);
1223     break;
1224 
1225   case KeyPress:
1226   case KeyRelease:
1227     fprintf(stderr,"DBGEVT: %s %s state=%x, keycode=%d\n",
1228 	    (e->type == KeyPress) ? "KeyPress  " : "KeyRelease",
1229 	    win2name(e->xkey.window),
1230 	    e->xkey.state, e->xkey.keycode);
1231     break;
1232 
1233   case Expose:
1234     fprintf(stderr,"DBGEVT: Expose %s %d,%d %dx%d count=%d\n",
1235 	    win2name(e->xexpose.window), e->xexpose.x, e->xexpose.y,
1236 	    e->xexpose.width, e->xexpose.height, e->xexpose.count);
1237     break;
1238 
1239   case ClientMessage:
1240     fprintf(stderr,"DBGEVT: ClientMessage %s\n",win2name(e->xclient.window));
1241     break;
1242 
1243   case ConfigureNotify:
1244     fprintf(stderr,"DBGEVT: ConfigureNotify %s %d,%d %dx%d  %s\n",
1245 	    win2name(e->xconfigure.window), e->xconfigure.x, e->xconfigure.y,
1246 	    e->xconfigure.width, e->xconfigure.height,
1247 	    (e->xconfigure.send_event) ? "synthetic" : "real");
1248     break;
1249 
1250   case MapNotify:
1251     fprintf(stderr,"DBGEVT: MapNotify %s\n", win2name(e->xany.window));
1252     break;
1253 
1254   case ReparentNotify:
1255     fprintf(stderr,"DBGEVT: ReparentNotify %s\n", win2name(e->xany.window));
1256     break;
1257 
1258   case EnterNotify:
1259   case LeaveNotify:
1260     fprintf(stderr,"DBGEVT: %s %s\n",
1261 	    (e->type==EnterNotify) ? "EnterNotify" : "LeaveNotify",
1262 	    win2name(e->xany.window));
1263     break;
1264 
1265   default:
1266     fprintf(stderr,"DBGEVT: unknown event type (%d), window %s\n",
1267 	    e->type, win2name(e->xany.window));
1268     break;
1269   }
1270 }
1271 
win2name(win)1272 static const char *win2name(win)
1273      Window win;
1274 {
1275   static char foo[16];
1276 
1277   if      (win == mainW)  return "mainW";
1278   else if (win == rootW)  return "rootW";
1279   else if (win == vrootW) return "vrootW";
1280   else if (win == infoW)  return "infoW";
1281   else if (win == ctrlW)  return "ctrlW";
1282   else if (win == dirW)   return "dirW";
1283   else if (win == gamW)   return "gamW";
1284   else if (win == psW)    return "psW";
1285 
1286   else {
1287     sprintf(foo, "0x%08x", (u_int) win);
1288     return foo;
1289   }
1290 }
1291 
1292 
1293 /***********************************/
handleButtonEvent(event,donep,retvalp)1294 static void handleButtonEvent(event, donep, retvalp)
1295   XEvent *event;
1296   int    *donep, *retvalp;
1297 {
1298   XButtonEvent *but_event;
1299   int i,x,y, done, retval, shift;
1300   Window win;
1301 
1302   but_event = (XButtonEvent *) event;
1303   done = *donep;  retval = *retvalp;
1304 
1305   win = but_event->window;
1306   x = but_event->x;  y = but_event->y;
1307   shift = but_event->state & ShiftMask;
1308 
1309   switch (event->type) {
1310   case ButtonPress:
1311     /* *always* check for pop-up events, as errors can happen... */
1312     if (PUCheckEvent  (event)) break;
1313 
1314     if (autoquit && win == mainW) Quit(0);
1315 
1316     if (viewonly) break;     /* ignore all other button presses */
1317 
1318     if (win == mainW && !useroot && showzoomcursor) {
1319       DoZoom(x, y, but_event->button);
1320       break;
1321     }
1322 
1323     if (PSCheckEvent  (event)) break;
1324 
1325 #ifdef HAVE_JPEG
1326     if (JPEGCheckEvent(event)) break;
1327 #endif
1328 
1329 #ifdef HAVE_JP2K
1330     if (JP2KCheckEvent(event)) break;
1331 #endif
1332 
1333 #ifdef HAVE_TIFF
1334     if (TIFFCheckEvent(event)) break;
1335 #endif
1336 
1337 #ifdef HAVE_PNG
1338     if (PNGCheckEvent (event)) break;
1339 #endif
1340 
1341 #ifdef HAVE_PCD
1342     if (PCDCheckEvent (event)) break;	/* event has been processed */
1343 #endif
1344 
1345 #ifdef HAVE_PIC2
1346     if (PIC2CheckEvent(event)) break;
1347 #endif
1348 
1349 #ifdef HAVE_MGCSFX
1350     if (MGCSFXCheckEvent(event)) break;
1351 #endif
1352 
1353 #ifdef TV_MULTILINGUAL
1354     if (CharsetCheckEvent(event)) break;
1355 #endif
1356 
1357     if (GamCheckEvent (event)) break;
1358     if (BrowseCheckEvent (event, &retval, &done)) break;
1359     if (TextCheckEvent   (event, &retval, &done)) break;
1360 
1361     switch (but_event->button) {
1362 
1363     case Button1:
1364       if      (win == mainW) DoSelection(but_event);
1365 
1366       else if (win == ctrlW) {
1367 	if      (MBClick(&dispMB,   x,y)) SelectDispMB  (MBTrack(&dispMB)  );
1368 	else if (MBClick(&conv24MB, x,y)) Select24to8MB (MBTrack(&conv24MB));
1369 	else if (MBClick(&rootMB,   x,y)) SelectRootMB  (MBTrack(&rootMB)  );
1370 	else if (MBClick(&windowMB, x,y)) SelectWindowMB(MBTrack(&windowMB));
1371 	else if (MBClick(&sizeMB,   x,y)) SelectSizeMB  (MBTrack(&sizeMB)  );
1372 	else if (MBClick(&algMB, x,y)) {
1373 	  algMB.dim[ALG_BLEND] = !HaveSelection();
1374 	  i = MBTrack(&algMB);
1375 	  if (i>=0) DoAlg(i);
1376 	  break;
1377 	}
1378 
1379 	i=ClickCtrl(x,y);
1380 
1381 	switch (i) {
1382 	case BNEXT:   retval= NEXTPIC;  done=1;     break;
1383 	case BPREV:   retval= PREVPIC;  done=1;     break;
1384 	case BLOAD:   DirBox(BLOAD);                break;
1385 	case BSAVE:   DirBox(BSAVE);                break;
1386 	case BDELETE: if (DeleteCmd()) { done = 1;  retval = DELETE; }  break;
1387 	case BPRINT:  DoPrint();                    break;
1388 
1389 	case BCOPY:   DoImgCopy();                  break;
1390 	case BCUT:    DoImgCut();                   break;
1391 	case BPASTE:  DoImgPaste();                 break;
1392 	case BCLEAR:  DoImgClear();                 break;
1393 	case BGRAB:   if (Grab()) {done=1; retval = GRABBED;}  break;
1394 	case BUP10:   SelectSizeMB(SZMB_P10);       break;
1395 	case BDN10:   SelectSizeMB(SZMB_M10);       break;
1396 	case BROTL:   Rotate(1);                    break;
1397 	case BROTR:   Rotate(0);                    break;
1398 	case BFLIPH:  Flip(0);                      break;
1399 	case BFLIPV:  Flip(1);                      break;
1400 
1401 	case BCROP:   Crop();                       break;
1402 	case BUNCROP: UnCrop();                     break;
1403 	case BACROP:  AutoCrop();                   break;
1404 
1405 	case BPAD:
1406 	  {
1407 	    int mode, wide, high, opaque, omode;  char *str;
1408 
1409 	    while (PadPopUp(&mode, &str, &wide, &high, &opaque, &omode)==0) {
1410 	      if (DoPad(mode, str, wide, high, opaque, omode)) {
1411 		done = 1;  retval = PADDED;  break;
1412 	      }
1413 	    }
1414 	  }
1415 	  break;
1416 
1417 	case BANNOT:  annotatePic();                break;
1418 
1419 	case BABOUT:  SelectWindowMB(WMB_ABOUTXV);  break;
1420 	case BXV:     retval = DFLTPIC;  done=1;    break;
1421 	case BQUIT:   retval = QUIT;     done=1;    break;
1422 
1423 	default:      break;
1424 	}
1425 
1426 	if (i==BFLIPH || i==BFLIPV) {
1427 	  DrawEpic();
1428 	  SetCursors(-1);
1429 	}
1430       }
1431 
1432       else if (win == nList.win) {
1433 	i=LSClick(&nList,but_event);
1434 	if (curname<0) ActivePrevNext();
1435 	if (i>=0) { done = 1;  retval = i; }
1436       }
1437 
1438       else if (win == nList.scrl.win) SCTrack(&nList.scrl, x, y);
1439 
1440       else if (win == dirW) {
1441 	i=ClickDirW(x,y);
1442 
1443 	switch (i) {
1444 	case S_BOK:   if (dirUp == BLOAD) {
1445 	    if (!DirCheckCD()) {
1446 	      retval = LOADPIC;
1447 	      done=1;
1448 	    }
1449 	  }
1450 	  else if (dirUp == BSAVE) {
1451 	    DoSave();
1452 	  }
1453 	  break;
1454 
1455 	case S_BCANC: DirBox(0);  break;
1456 
1457 	case S_BRESCAN:
1458 	  WaitCursor();  LoadCurrentDirectory();  SetCursors(-1);
1459 	  break;
1460 	}
1461       }
1462 
1463       else if (win == dList.win) {
1464 	i=LSClick(&dList,but_event);
1465 	SelectDir(i);
1466       }
1467 
1468       else if (win == dList.scrl.win) SCTrack(&dList.scrl, x,y);
1469       else if (win == infoW)          InfoBox(0);  /* close info */
1470 
1471       break;
1472 
1473 
1474     case Button2:
1475       if (win == mainW && !useroot) {
1476 	if (!shift && !DoSelection(but_event)) TrackPicValues(x,y);
1477 	else if (shift) Paint();
1478       }
1479       break;
1480 
1481     case Button3:  /* if using root, MUST NOT get rid of ctrlbox. */
1482       if (!shift && !useroot) CtrlBox(!ctrlUp);
1483       else if (shift) BlurPaint();
1484       break;
1485 
1486     case Button4:   /* note min vs. max, + vs. - */
1487       if (win == ctrlW || win == nList.win || win == nList.scrl.win) {
1488 	SCRL *sp=&nList.scrl;
1489 	int  halfpage=sp->page/2;
1490 
1491 	if (sp->val > sp->min+halfpage)
1492 	  SCSetVal(sp,sp->val-halfpage);
1493 	else
1494 	  SCSetVal(sp,sp->min);
1495       }
1496       else if (win ==  dirW || win == dList.win || win == dList.scrl.win) {
1497 	SCRL *sp=&dList.scrl;
1498 	int  halfpage=sp->page/2;
1499 
1500 	if (sp->val > sp->min+halfpage)
1501 	  SCSetVal(sp,sp->val-halfpage);
1502 	else
1503 	  SCSetVal(sp,sp->min);
1504       }
1505       break;
1506 
1507     case Button5:   /* note max vs. min, - vs. + */
1508       if (win == ctrlW || win == nList.win || win == nList.scrl.win) {
1509 	SCRL *sp=&nList.scrl;
1510 	int  halfpage=sp->page/2;
1511 
1512 	if (sp->val < sp->max-halfpage)
1513 	  SCSetVal(sp,sp->val+halfpage);
1514 	else
1515 	  SCSetVal(sp,sp->max);
1516       }
1517       else if (win ==  dirW || win == dList.win || win == dList.scrl.win) {
1518 	SCRL *sp=&dList.scrl;
1519 	int  halfpage=sp->page/2;
1520 
1521 	if (sp->val < sp->max-halfpage)
1522 	  SCSetVal(sp,sp->val+halfpage);
1523 	else
1524 	  SCSetVal(sp,sp->max);
1525       }
1526       break;
1527 
1528     default:       break;
1529     }
1530   }
1531 
1532   *donep = done;  *retvalp = retval;
1533 }
1534 
1535 
1536 /***********************************/
handleKeyEvent(event,donep,retvalp)1537 static void handleKeyEvent(event, donep, retvalp)
1538   XEvent *event;
1539   int    *donep, *retvalp;
1540 {
1541   /* handles KeyPress and KeyRelease events, called from HandleEvent */
1542 
1543   XKeyEvent *key_event;
1544   KeySym     ks;
1545   char       buf[128];
1546   int        stlen, dealt, done, retval, shift, ctrl, meta, ck;
1547   u_int      svkeystate;
1548 
1549   key_event = (XKeyEvent *) event;
1550   done      = *donep;
1551   retval    = *retvalp;
1552 
1553   switch (event->type) {
1554   case KeyRelease:
1555     if (viewonly) break;     /* ignore all user input */
1556 
1557     stlen = XLookupString(key_event,buf,128,&ks,(XComposeStatus *) NULL);
1558     dealt = 0;
1559 
1560     if (key_event->window == mainW) {
1561       u_int foo = key_event->state;
1562 
1563       if (ks == XK_Shift_L   || ks == XK_Shift_R)
1564 	foo = foo & (u_int) (~ShiftMask);
1565       if (ks == XK_Control_L || ks == XK_Control_R)
1566 	foo = foo & (u_int) (~ControlMask);
1567       if (ks == XK_Meta_L    || ks == XK_Meta_R)
1568 	foo = foo & (u_int) (~Mod1Mask);
1569       if (ks == XK_Alt_L     || ks == XK_Alt_R)
1570 	foo = foo & (u_int) (~Mod1Mask);
1571 
1572       zoomCurs(foo);
1573     }
1574     break;
1575 
1576 
1577   case KeyPress:
1578     svkeystate = key_event->state;
1579     key_event->state &= ~Mod1Mask;      /* remove meta from translation */
1580     stlen = XLookupString(key_event,buf,128,&ks,(XComposeStatus *) NULL);
1581     key_event->state = svkeystate;
1582 
1583     shift = key_event->state & ShiftMask;
1584     ctrl  = key_event->state & ControlMask;
1585     meta  = key_event->state & Mod1Mask;
1586     dealt = 0;
1587 
1588     RemapKeyCheck(ks, buf, &stlen);
1589 
1590     if (PUCheckEvent  (event)) break;          /* always check popups */
1591 
1592     if (autoquit && key_event->window == mainW) Quit(0);
1593 
1594     if (viewonly && !frominterrupt) break;     /* ignore all user input */
1595 
1596     if (PSCheckEvent  (event)) break;
1597 
1598     if (key_event->window == mainW) {
1599       u_int foo = key_event->state;
1600 
1601       if (ks == XK_Shift_L   || ks == XK_Shift_R)   foo = foo | ShiftMask;
1602       if (ks == XK_Control_L || ks == XK_Control_R) foo = foo | ControlMask;
1603       if (ks == XK_Meta_L    || ks == XK_Meta_R)    foo = foo | Mod1Mask;
1604       if (ks == XK_Alt_L     || ks == XK_Alt_R)     foo = foo | Mod1Mask;
1605       zoomCurs(foo);
1606     }
1607 
1608 #ifdef HAVE_JPEG
1609     if (JPEGCheckEvent(event)) break;
1610 #endif
1611 
1612 #ifdef HAVE_JP2K
1613     if (JP2KCheckEvent(event)) break;
1614 #endif
1615 
1616 #ifdef HAVE_TIFF
1617     if (TIFFCheckEvent(event)) break;
1618 #endif
1619 
1620 #ifdef HAVE_PNG
1621     if (PNGCheckEvent (event)) break;
1622 #endif
1623 
1624     if (PCDCheckEvent (event)) break;
1625 
1626 #ifdef HAVE_PIC2
1627     if (PIC2CheckEvent(event)) break;
1628 #endif
1629 
1630 #ifdef HAVE_PCD
1631     if (PCDCheckEvent (event)) break;
1632 #endif
1633 
1634 #ifdef HAVE_MGCSFX
1635     if (MGCSFXCheckEvent(event)) break;
1636 #endif
1637 
1638     if (GamCheckEvent (event)) break;
1639     if (BrowseCheckEvent (event, &retval, &done)) break;
1640     if (TextCheckEvent   (event, &retval, &done)) break;
1641 
1642 
1643     /* Support for multi-image files ("multipage docs").  Check for PgUp/PgDn
1644        or 'p' in any window but control or directory; PgUp/PgDn are already
1645        used to page through the file list in those windows.  If no cropping
1646        rectangle is active, shift-Up and shift-Down also work. */
1647 
1648     if (key_event->window != ctrlW && key_event->window != dirW) {
1649       dealt = 1;
1650 
1651       ck = CursorKey(ks, shift, 0);
1652       if (ck==CK_PAGEUP || (ck==CK_UP && shift && !but[BCROP].active)) {
1653 	if (strlen(pageBaseName) && numPages>1) {
1654 	  done = 1;  retval = OP_PAGEUP;
1655 	}
1656 	else XBell(theDisp,0);
1657       }
1658 
1659       else if (ck==CK_PAGEDOWN ||
1660 	       (ck==CK_DOWN && shift && !but[BCROP].active)) {
1661 	if (strlen(pageBaseName) && numPages>1) {
1662 	  done = 1;  retval = OP_PAGEDN;
1663 	}
1664 	else XBell(theDisp,0);
1665       }
1666 
1667       else if (buf[0] == 'p' && stlen>0) {
1668 	if (strlen(pageBaseName) && numPages>1) {
1669 	  int                i,j, okay;
1670 	  char               buf[64], txt[512];
1671 	  static const char *labels[] = { "\nOk", "\033Cancel" };
1672 
1673 	  /* ask what page to go to */
1674 	  sprintf(txt, "Go to page number...   (1-%d)", numPages);
1675 	  sprintf(buf, "%d", curPage + 1);
1676 
1677 	  okay = 0;
1678 	  do {
1679 	    i = GetStrPopUp(txt, labels, 2, buf, 64, "0123456789", 1);
1680 	    if (!i && strlen(buf) > (size_t) 0) {
1681 	      /* hit 'Ok', had a string entered */
1682 	      /* check for page in range */
1683 	      j = atoi(buf);
1684 	      if (j>=1 && j<=numPages) {
1685 		curPage = j;   /* one page past desired page */
1686 		done = 1;
1687 		retval = OP_PAGEUP;
1688 		okay=1;
1689 	      }
1690 	      else XBell(theDisp, 0);
1691 	    }
1692 	    else okay = 1;
1693 	  } while (!okay);
1694 	}
1695 	else XBell(theDisp, 0);
1696       }
1697 
1698       else dealt = 0;
1699 
1700       if (dealt) break;
1701     }
1702 
1703 
1704 
1705     /* check for crop rect keys */
1706     if (key_event->window == mainW) {
1707       dealt = 1;
1708       ck = CursorKey(ks, shift, 0);
1709       if      (ck==CK_LEFT)  CropKey(-1, 0,shift,ctrl);
1710       else if (ck==CK_RIGHT) CropKey( 1, 0,shift,ctrl);
1711       else if (ck==CK_UP)    CropKey( 0,-1,shift,ctrl);
1712       else if (ck==CK_DOWN)  CropKey( 0, 1,shift,ctrl);
1713       else dealt = 0;
1714       if (dealt) break;
1715     }
1716 
1717 
1718     /* check for List keys */
1719     if (key_event->window == ctrlW || key_event->window == dirW) {
1720       LIST *theList;
1721 
1722       ck = CursorKey(ks, shift, 1);
1723       if (key_event->window == dirW) theList = &dList;
1724       else theList = &nList;
1725 
1726       dealt = 1;
1727 
1728       if      (ck==CK_PAGEUP)   LSKey(theList,LS_PAGEUP);
1729       else if (ck==CK_PAGEDOWN) LSKey(theList,LS_PAGEDOWN);
1730       else if (ck==CK_UP)       LSKey(theList,LS_LINEUP);
1731       else if (ck==CK_DOWN)     LSKey(theList,LS_LINEDOWN);
1732       else if (ck==CK_HOME)     LSKey(theList,LS_HOME);
1733       else if (ck==CK_END)      LSKey(theList,LS_END);
1734       else dealt = 0;
1735 
1736       if (theList == &nList && dealt && curname<0) ActivePrevNext();
1737 
1738       if (theList == &dList && dealt) {  /* changed dir selection */
1739 	SelectDir(-1);  /* nothing was double-clicked */
1740       }
1741 
1742       if (dealt) break;
1743     }
1744 
1745 
1746     /* check dir filename arrows */
1747     if (key_event->window == dirW) {
1748       ck = CursorKey(ks, shift, 1);
1749       if (ck==CK_LEFT)  { DirKey('\002'); break; }
1750       if (ck==CK_RIGHT) { DirKey('\006'); break; }
1751     }
1752 
1753 
1754     /* check for preset keys     (meta-1, meta-2, meta-3, meta-4, meta-0)
1755      * and cut/copy/paste/clear  (meta-x, meta-c, meta-v, meta-d)
1756      * and 8/24 bit toggle       (meta-8)
1757      * and some algorithms       (meta-b, meta-t, meta-p, meta-l, etc.)
1758      */
1759 
1760     if (meta) {  /* meta is down */
1761       dealt = 1;
1762       if      (ks==XK_1) FakeButtonPress(&gbut[G_B1]);
1763       else if (ks==XK_2) FakeButtonPress(&gbut[G_B2]);
1764       else if (ks==XK_3) FakeButtonPress(&gbut[G_B3]);
1765       else if (ks==XK_4) FakeButtonPress(&gbut[G_B4]);
1766       else if (ks==XK_r || ks==XK_0)
1767 	                 FakeButtonPress(&gbut[G_BRESET]);
1768 
1769       else if (ks==XK_x) FakeButtonPress(&but[BCUT]);
1770       else if (ks==XK_c) FakeButtonPress(&but[BCOPY]);
1771       else if (ks==XK_v) FakeButtonPress(&but[BPASTE]);
1772       else if (ks==XK_d) FakeButtonPress(&but[BCLEAR]);
1773 
1774       else if (ks==XK_u) DoAlg(ALG_NONE);
1775       else if (ks==XK_b) DoAlg(ALG_BLUR);
1776       else if (ks==XK_s) DoAlg(ALG_SHARPEN);
1777       else if (ks==XK_e) DoAlg(ALG_EDGE);
1778       else if (ks==XK_m) DoAlg(ALG_TINF);
1779       else if (ks==XK_o) DoAlg(ALG_OIL);
1780       else if (ks==XK_k) DoAlg(ALG_MEDIAN);
1781 
1782       else if ((ks==XK_B  || (ks==XK_b && shift)) && HaveSelection())
1783 	                                        DoAlg(ALG_BLEND);
1784 
1785       else if (ks==XK_p)                        DoAlg(ALG_PIXEL);
1786 
1787       else if (ks==XK_S || (ks==XK_s && shift)) DoAlg(ALG_SPREAD);
1788 
1789       else if (ks==XK_t || ks==XK_T) {
1790 	if (ctrl || shift || ks==XK_T)          DoAlg(ALG_ROTATE);
1791         else                                    DoAlg(ALG_ROTATECLR);
1792       }
1793 
1794       else if (ks==XK_a) FakeButtonPress(&gbut[G_BAPPLY]);
1795 
1796       else if (ks==XK_8) {
1797 	if (picType==PIC8) Select24to8MB(CONV24_24BIT);
1798 	              else Select24to8MB(CONV24_8BIT);
1799       }
1800 
1801       else dealt = 0;
1802 
1803       if (dealt) break;
1804     }
1805 
1806     /* Check for function keys */
1807     if (key_event->window == ctrlW || key_event->window == mainW) {
1808       if (ks >= XK_F1 && ks <= XK_F1 + FSTRMAX - 1) {
1809         int fkey = ks - XK_F1;
1810         if (fkeycmds[fkey] && fullfname[0]) {
1811 #define CMDLEN 4096
1812           char cmd[CMDLEN];
1813           /* If a command begins with '@', we do not reload the current file */
1814           int noreload = (fkeycmds[fkey][0] == '@');
1815           int x = 0, y = 0, w = 0, h = 0;
1816           if (HaveSelection())
1817             GetSelRCoords(&x, &y, &w, &h);
1818           snprintf(cmd, CMDLEN, fkeycmds[fkey] + noreload, fullfname, x, y, w, h);
1819 #undef CMDLEN
1820           if (DEBUG) fprintf(stderr, "Executing '%s'\n", cmd);
1821           WaitCursor();
1822           system(cmd);
1823           SetCursors(-1);
1824           if (!noreload) {
1825             retval = RELOAD;
1826             done = 1;
1827           }
1828           break;
1829         }
1830       }
1831     }
1832 
1833     if (!stlen) break;
1834 
1835     if (key_event->window == dirW) {
1836       if (DirKey(buf[0])) XBell(theDisp,0);
1837     }
1838     else {                               /* commands valid in any window */
1839       switch (buf[0]) {
1840 
1841 	/* things in dispMB */
1842       case 'r':    SelectDispMB(DMB_RAW);           break;
1843       case 'd':    SelectDispMB(DMB_DITH);          break;
1844       case 's':    SelectDispMB(DMB_SMOOTH);        break;
1845 
1846 	/* things in sizeMB */
1847       case 'n':    SelectSizeMB(SZMB_NORM);         break;
1848       case 'm':    SelectSizeMB(SZMB_MAXPIC);       break;
1849       case 'M':    SelectSizeMB(SZMB_MAXPECT);      break;
1850       case '>':    SelectSizeMB(SZMB_DOUBLE);       break;
1851       case '<':    SelectSizeMB(SZMB_HALF);         break;
1852       case '.':    SelectSizeMB(SZMB_P10);          break;
1853       case ',':    SelectSizeMB(SZMB_M10);          break;
1854       case 'S':    SelectSizeMB(SZMB_SETSIZE);      break;
1855       case 'a':    SelectSizeMB(SZMB_ASPECT);       break;
1856       case '4':    SelectSizeMB(SZMB_4BY3);         break;
1857       case 'I':    SelectSizeMB(SZMB_INTEXP);       break;
1858 
1859 	/* things in windowMB */
1860       case '\026':
1861       case 'V':    SelectWindowMB(WMB_BROWSE);      break;  /* ^V or V */
1862       case 'e':    SelectWindowMB(WMB_COLEDIT);     break;  /*  e */
1863       case 'i':    SelectWindowMB(WMB_INFO);        break;  /*  i */
1864       case '\003': SelectWindowMB(WMB_COMMENT);     break;  /* ^C */
1865       case '\024': SelectWindowMB(WMB_TEXTVIEW);    break;  /* ^T */
1866       case '\001': SelectWindowMB(WMB_ABOUTXV);     break;  /* ^A */
1867 
1868 
1869 
1870 	/* buttons in ctrlW */
1871       case '\t':
1872       case ' ':    FakeButtonPress(&but[BNEXT]);    break;
1873 
1874       case '\r':
1875       case '\n':
1876 	if (nList.selected >= 0 && nList.selected < nList.nstr) {
1877 	  done = 1;  retval = nList.selected;
1878 	  if (frominterrupt) retval = RELOAD;
1879 	}
1880 	break;
1881 
1882       case '\010': FakeButtonPress(&but[BPREV]);    break;
1883 
1884 
1885       case '\014': FakeButtonPress(&but[BLOAD]);    break;  /* ^L */
1886       case '\023': FakeButtonPress(&but[BSAVE]);    break;  /* ^S */
1887       case '\020': FakeButtonPress(&but[BPRINT]);   break;  /* ^P */
1888       case '\177':
1889       case '\004': FakeButtonPress(&but[BDELETE]);  break;  /* ^D */
1890 
1891 	/* BCOPY, BCUT, BPASTE, BCLEAR handled in 'meta' case */
1892 
1893       case '\007': FakeButtonPress(&but[BGRAB]);    break;  /* ^G */
1894 
1895 	/* BUP10, BDN10 handled in sizeMB case */
1896 
1897       case 'T':    FakeButtonPress(&but[BROTL]);    break;
1898       case 't':    FakeButtonPress(&but[BROTR]);    break;
1899       case 'h':    FakeButtonPress(&but[BFLIPH]);   break;
1900       case 'v':    FakeButtonPress(&but[BFLIPV]);   break;
1901       case 'c':    FakeButtonPress(&but[BCROP]);    break;
1902       case 'u':    FakeButtonPress(&but[BUNCROP]);  break;
1903       case 'C':    FakeButtonPress(&but[BACROP]);   break;
1904       case 'P':    FakeButtonPress(&but[BPAD]);     break;
1905       case 'A':    FakeButtonPress(&but[BANNOT]);   break;
1906 
1907 	/* BABOUT handled in windowMB case */
1908 
1909       case '\021': /* ^Q */
1910       case 'q':    FakeButtonPress(&but[BQUIT]);    break;
1911 
1912       case '?':    if (!useroot) CtrlBox(!ctrlUp);  break;
1913 
1914 	/* things in color editor */
1915       case 'R':    FakeButtonPress(&gbut[G_BRESET]);   break;
1916       case 'H':    FakeButtonPress(&gbut[G_BHISTEQ]);  break;
1917       case 'N':    FakeButtonPress(&gbut[G_BMAXCONT]); break;
1918 
1919       default:     break;
1920       }
1921     }
1922   }
1923 
1924   *donep = done;  *retvalp = retval;
1925 }
1926 
1927 
1928 /***********************************/
zoomCurs(mask)1929 static void zoomCurs(mask)
1930      u_int mask;
1931 {
1932   int zc;
1933   zc = ((mask & ControlMask) && !(mask & ShiftMask) && !(mask & Mod1Mask));
1934 
1935   if (zc != showzoomcursor) {
1936     showzoomcursor = zc;
1937     SetCursors(-1);
1938   }
1939 }
1940 
1941 
1942 /***********************************/
textViewCmd()1943 static void textViewCmd()
1944 {
1945   int   i;
1946   char *name;
1947 
1948   i = nList.selected;
1949   if (i<0 || i>=numnames) return;     /* shouldn't happen */
1950 
1951   if (namelist[i][0] != '/') {  /* prepend 'initdir' */
1952     name = (char *) malloc(strlen(namelist[i]) + strlen(initdir) + 2);
1953     if (!name) FatalError("malloc in textViewCmd failed");
1954     sprintf(name,"%s/%s", initdir, namelist[i]);
1955   }
1956   else name = namelist[i];
1957 
1958   TextView(name);
1959 
1960   if (name != namelist[i]) free(name);
1961 }
1962 
1963 
1964 /***********************************/
setSizeCmd()1965 static void setSizeCmd()
1966 {
1967   /* open 'set size' prompt window, get a string, parse it, and try to
1968      set the window size accordingly */
1969 
1970   int                i, arg1, arg2, numargs, pct1, pct2, state, neww, newh;
1971   char               txt[512], buf[64], *sp, ch;
1972   static const char *labels[] = { "\nOk", "\033Cancel" };
1973 
1974   sprintf(txt, "Enter new image display size (ex. '400 x 300'),\n");
1975   strcat (txt, "expansion ratio (ex. '75%'),\n");
1976   strcat (txt, "or expansion ratios (ex. '200% x 125%'):");
1977 
1978   sprintf(buf, "%d x %d", eWIDE, eHIGH);    /* current vals */
1979 
1980   i = GetStrPopUp(txt, labels, 2, buf, 64, "0123456789x% ", 1);
1981 
1982   if (i) return;     /* cancelled */
1983   if (strlen(buf) == 0) return;     /* no command */
1984 
1985 
1986   /* attempt to parse the string accordingly...
1987    * parses strings of the type: <num> [%] [ x <num> [%] ]
1988    * (-ish.  <num> all by itself isn't legal)
1989    * there may be any # of spaces between items, including zero
1990    */
1991 
1992   arg1 = arg2 = numargs = pct1 = pct2 = state = 0;
1993   sp = buf;
1994 
1995   do  {
1996     ch = *sp++;
1997 
1998     switch (state) {
1999     case 0:             /* haven't seen arg1 yet */
2000       if      (ch == ' ') {}
2001       else if (ch == '%' || ch == 'x' || ch == '\0') state = 99;  /* no arg1 */
2002       else { arg1  = (ch - '0');  state = 1; }
2003       break;
2004 
2005     case 1:             /* parsing arg1 */
2006       numargs = 1;
2007       if      (ch == ' ')  state = 2;
2008       else if (ch == '%')  state = 3;
2009       else if (ch == 'x')  state = 4;
2010       else if (ch == '\0') state = 99;
2011       else arg1 = (arg1 * 10) + (ch - '0');
2012       break;
2013 
2014     case 2:             /* got arg1 and whitespace */
2015       if      (ch == ' ') {}
2016       else if (ch == '%') state = 3;
2017       else if (ch == 'x') state = 4;
2018       else state = 99;
2019       break;
2020 
2021     case 3:             /* got arg1 % */
2022       pct1 = 1;
2023       if      (ch == ' ')  {}
2024       else if (ch == '%')  state = 99;
2025       else if (ch == 'x')  state = 4;
2026       else if (ch == '\0') state = 100;
2027       else state = 99;
2028       break;
2029 
2030     case 4:             /* got arg1 [%] x */
2031       if      (ch == ' ') {}
2032       else if (ch == '%' || ch == 'x' || ch == '\0') state = 99;
2033       else { arg2 = (ch - '0');  state = 5; }
2034       break;
2035 
2036     case 5:             /* parsing arg2 */
2037       numargs = 2;
2038       if      (ch == ' ')  state = 6;
2039       else if (ch == '%')  state = 7;
2040       else if (ch == 'x')  state = 99;
2041       else if (ch == '\0') state = 100;
2042       else arg2 = (arg2 * 10) + (ch - '0');
2043       break;
2044 
2045     case 6:             /* got arg2 and whitespace */
2046       if      (ch == ' ')  {}
2047       else if (ch == '%')  state = 7;
2048       else if (ch == 'x')  state = 99;
2049       else if (ch == '\0') state = 100;
2050       else state = 99;
2051       break;
2052 
2053     case 7:             /* got arg1 [%] x arg2 % */
2054       pct2  = 1;
2055       state = 100;
2056       break;
2057 
2058     case 99:            /* error in parsing */
2059       break;
2060 
2061     case 100:           /* successful parse */
2062       break;
2063     }
2064   } while (state!=99 && state!=100);
2065 
2066   /* done parsing... */
2067   if (state == 99) {
2068     ErrPopUp("Error:  The entered SetSize string is not valid.", "\nRight.");
2069     return;
2070   }
2071 
2072   if (DEBUG)
2073     fprintf(stderr,"setSize:  arg1=%d, arg2=%d, numargs=%d, pct=%d,%d\n",
2074 	    arg1, arg2, numargs, pct1, pct2);
2075 
2076   /* otherwise... */
2077   if (numargs == 1) {
2078     if (pct1) {
2079       neww = (cWIDE * arg1) / 100;
2080       newh = (cHIGH * arg1) / 100;
2081     }
2082     else return;    /* shouldn't happen */
2083   }
2084   else {   /* numargs = 2; */
2085     neww = (pct1) ? (cWIDE * arg1) / 100 : arg1;
2086     newh = (pct2) ? (cHIGH * arg2) / 100 : arg2;
2087   }
2088 
2089   if (neww < 1 || newh < 1 || neww > 64000 || newh > 64000) {
2090     sprintf(txt, "The new desired image display size of '%d x %d' is %s",
2091 	    neww, newh, "ludicrous.  Ignored.");
2092     ErrPopUp(txt, "\nSez you!");
2093     return;
2094   }
2095 
2096   WResize(neww, newh);
2097 }
2098 
2099 
2100 /***********************************/
NewCutBuffer(str)2101 void NewCutBuffer(str)
2102      char *str;
2103 {
2104   /* called whenever contents of CUT_BUFFER0 and PRIMARY selection should
2105      be changed.  Only works for strings.  Copies the data, so the string
2106      doesn't have to be static. */
2107 
2108   if (!str) return;
2109 
2110   XStoreBytes(theDisp, str, (int) strlen(str));   /* CUT_BUFFER0 */
2111   XSetSelectionOwner(theDisp, XA_PRIMARY, ctrlW, lastEventTime);
2112 
2113   if (xevPriSel) free(xevPriSel);
2114   xevPriSel = (char *) malloc(strlen(str) + 1);
2115   if (xevPriSel) strcpy(xevPriSel, str);
2116 }
2117 
2118 /***********************************/
DrawWindow(x,y,w,h)2119 void DrawWindow(x,y,w,h)
2120      int x,y,w,h;
2121 {
2122   if (x+w < eWIDE) w++;  /* add one for broken servers (?) */
2123   if (y+h < eHIGH) h++;
2124 
2125   if (theImage)
2126     XPutImage(theDisp,mainW,theGC,theImage,x,y,x,y, (u_int) w, (u_int) h);
2127   else
2128     if (DEBUG) fprintf(stderr,"Tried to DrawWindow when theImage was NULL\n");
2129 }
2130 
2131 
2132 /***********************************/
WResize(w,h)2133 void WResize(w,h)
2134      int w,h;
2135 {
2136   XWindowAttributes xwa;
2137 
2138   RANGE(w,1,maxWIDE);  RANGE(h,1,maxHIGH);
2139 
2140   if (useroot) {
2141     Resize(w,h);
2142     MakeRootPic();
2143     SetCursors(-1);
2144     return;
2145   }
2146 
2147   GetWindowPos(&xwa);
2148 
2149   /* determine if new size goes off edge of screen.  if so move window so it
2150      doesn't go off screen */
2151   if (xwa.x + w > vrWIDE) xwa.x = vrWIDE - w;
2152   if (xwa.y + h > vrHIGH) xwa.y = vrHIGH - h;
2153   if (xwa.x < 0) xwa.x = 0;
2154   if (xwa.y < 0) xwa.y = 0;
2155 
2156   if (DEBUG) fprintf(stderr,"%s: resizing window to %d,%d at %d,%d\n",
2157 		     cmd,w,h,xwa.x,xwa.y);
2158 
2159   /* resize the window */
2160   xwa.width = w;  xwa.height = h;
2161 
2162   SetWindowPos(&xwa);
2163 }
2164 
2165 
2166 
2167 
2168 /***********************************/
WMaximize()2169 static void WMaximize()
2170 {
2171   if (useroot) WResize((int) dispWIDE, (int) dispHIGH);
2172   else {
2173     XWindowAttributes xwa;
2174     xvbzero((char *) &xwa, sizeof(XWindowAttributes));
2175     xwa.x = xwa.y = 0;
2176     xwa.width  = dispWIDE;
2177     xwa.height = dispHIGH;
2178     SetWindowPos(&xwa);
2179   }
2180 }
2181 
2182 
2183 
2184 
2185 /***********************************/
WRotate()2186 void WRotate()
2187 {
2188   /* rotate the window and redraw the contents  */
2189 
2190   if (but[BCROP].active) BTSetActive(&but[BCROP],0);
2191 
2192   if (useroot || eWIDE == eHIGH) {
2193     /* won't see any configure events.  Manually redraw image */
2194     DrawEpic();
2195     SetCursors(-1);
2196     return;
2197   }
2198   else {
2199     rotatesLeft++;
2200     XClearWindow(theDisp, mainW);  /* get rid of old bits */
2201     GenExpose(mainW, 0, 0, (u_int) eWIDE, (u_int) eHIGH);
2202     { int ew, eh;
2203       ew = eWIDE;  eh = eHIGH;
2204       WResize(eWIDE, eHIGH);
2205       if (ew>maxWIDE || eh>maxHIGH) {   /* rotated pic too big, scale down */
2206 	double r,wr,hr;
2207 	wr = ((double) ew) / maxWIDE;
2208 	hr = ((double) eh) / maxHIGH;
2209 
2210 	r = (wr>hr) ? wr : hr;   /* r is the max(wr,hr) */
2211 	ew = (int) ((ew / r) + 0.5);
2212 	eh = (int) ((eh / r) + 0.5);
2213 	WResize(ew,eh);
2214       }
2215     }
2216   }
2217 }
2218 
2219 
2220 /***********************************/
WCrop(w,h,dx,dy)2221 void WCrop(w,h,dx,dy)
2222      int w,h,dx,dy;
2223 {
2224   int ex, ey;
2225   XWindowAttributes xwa;
2226 
2227   if (useroot) {
2228     MakeRootPic();
2229     SetCursors(-1);
2230   }
2231 
2232   else {
2233     /* we want to move window to old x,y + dx,dy (in pic coords) */
2234     GetWindowPos(&xwa);
2235 
2236     if (!origcropvalid) {  /* first crop.  remember win pos */
2237       origcropvalid = 1;
2238       origcropx = xwa.x;
2239       origcropy = xwa.y;
2240     }
2241 
2242     CoordC2E(dx, dy, &ex, &ey);
2243 
2244     xwa.x += ex;  xwa.y += ey;
2245     xwa.width = w;  xwa.height = h;
2246     GenExpose(mainW, 0, 0, (u_int) eWIDE, (u_int) eHIGH);
2247     SetWindowPos(&xwa);
2248   }
2249 }
2250 
2251 
2252 /***********************************/
WUnCrop()2253 void WUnCrop()
2254 {
2255   int w,h;
2256   XWindowAttributes xwa;
2257 
2258   /* a proper epic has been generated.  eWIDE,eHIGH are the new window size */
2259 
2260 
2261   if (useroot) {
2262     MakeRootPic();
2263     SetCursors(-1);
2264   }
2265 
2266   else {   /* !useroot */
2267     GetWindowPos(&xwa);
2268 
2269     w = eWIDE;  h = eHIGH;
2270 
2271     /* restore to position when originally cropped */
2272     if (origcropvalid) {  /* *should* always be true... */
2273       origcropvalid = 0;
2274       xwa.x = origcropx;
2275       xwa.y = origcropy;
2276     }
2277 
2278     /* keep on screen */
2279     if (xwa.x + w > vrWIDE) xwa.x = vrWIDE - w;
2280     if (xwa.y + h > vrHIGH) xwa.y = vrHIGH - h;
2281     if (xwa.x < 0) xwa.x = 0;
2282     if (xwa.y < 0) xwa.y = 0;
2283 
2284     xwa.width = w;  xwa.height = h;
2285 
2286     if (!useroot) {
2287       SetWindowPos(&xwa);
2288       GenExpose(mainW, 0, 0, (u_int) eWIDE, (u_int) eHIGH);
2289     }
2290   }
2291 }
2292 
2293 
2294 
2295 /***********************************/
GetWindowPos(xwa)2296 void GetWindowPos(xwa)
2297 XWindowAttributes *xwa;
2298 {
2299   Window child;
2300 
2301   /* returns the x,y,w,h coords of mainW.  x,y are relative to rootW
2302      the border is not included (x,y map to top-left pixel in window) */
2303 
2304   /* Get the window width/height */
2305   XGetWindowAttributes(theDisp,mainW,xwa);
2306 
2307   /* Get the window origin */
2308   XTranslateCoordinates(theDisp,mainW,rootW,0,0,&xwa->x,&xwa->y,&child);
2309 }
2310 
2311 
2312 /***********************************/
SetWindowPos(xwa)2313 void SetWindowPos(xwa)
2314 XWindowAttributes *xwa;
2315 {
2316   /* sets window x,y,w,h values */
2317   XWindowChanges    xwc;
2318 
2319   /* Adjust from window origin, to border origin */
2320   xwc.x = xwa->x - xwa->border_width - ch_offx;
2321   xwc.y = xwa->y - xwa->border_width - ch_offy;
2322 
2323   if (!xwa->border_width) xwa->border_width = bwidth;
2324   xwc.border_width = xwa->border_width;
2325 
2326   /* if we're less than max size in one axis, allow window manager doohickeys
2327      on the screen */
2328 
2329   if (xwa->width  < dispWIDE && xwc.x < p_offx) xwc.x = p_offx;
2330   if (xwa->height < dispHIGH && xwc.y < p_offy) xwc.y = p_offy;
2331 
2332   /* Try to keep bottom right decorations inside */
2333 #ifdef CRAP
2334   if (xwc.x+eWIDE-debkludge_offx>dispWIDE) {
2335     xwc.x=dispWIDE-eWIDE+debkludge_offx;
2336     if (xwc.x<0) xwc.x=0;
2337   }
2338   if (xwc.y+eHIGH-debkludge_offy>dispHIGH) {
2339     xwc.y=dispHIGH-eHIGH+debkludge_offy;
2340     if (xwc.y<0) xwc.y=0;
2341   }
2342 #else
2343   if (xwc.x+eWIDE+p_offx>dispWIDE) {
2344     xwc.x=dispWIDE-(eWIDE+debkludge_offx);
2345     if (xwc.x<0) xwc.x=0;
2346   }
2347   if (xwc.y+eHIGH+p_offy>dispHIGH) {
2348     xwc.y=dispHIGH-(eHIGH+debkludge_offy);
2349     if (xwc.y<0) xwc.y=0;
2350   }
2351 #endif
2352 
2353   xwc.width  = xwa->width;
2354   xwc.height = xwa->height;
2355 
2356 #define BAD_IDEA
2357 #ifdef BAD_IDEA
2358   /* if there is a virtual window manager running, then we should translate
2359      the coordinates that are in terms of 'real' screen into coordinates
2360      that are in terms of the 'virtual' root window
2361      from: Daren W. Latham <dwl@mentat.udev.cdc.com> */
2362 
2363   if (vrootW != rootW) { /* virtual window manager running */
2364     int x1,y1;
2365     Window child;
2366 
2367     XTranslateCoordinates(theDisp, rootW, vrootW, xwc.x, xwc.y, &x1, &y1, &child);
2368     if (DEBUG) fprintf(stderr,"SWP: translate: %d,%d -> %d,%d\n",
2369 		       xwc.x, xwc.y, x1, y1);
2370     xwc.x = x1;  xwc.y = y1;
2371   }
2372 #endif
2373 
2374 
2375   if (DEBUG) {
2376     fprintf(stderr,
2377 	    "SWP: xwa=%d,%d %dx%d xwc=%d,%d %dx%d off=%d,%d bw=%d klg=%d,%d\n",
2378 	    xwa->x, xwa->y, xwa->width, xwa->height,
2379 	    xwc.x, xwc.y, xwc.width, xwc.height, p_offx, p_offy,
2380 	    xwa->border_width, kludge_offx, kludge_offy);
2381   }
2382 
2383   xwc.x += kludge_offx;
2384   xwc.y += kludge_offy;
2385 
2386 #if defined(DXWM) || defined(HAVE_XUI)
2387   /* dxwm seems to *only* pay attention to the hints */
2388   {
2389     XSizeHints hints;
2390     if (DEBUG) fprintf(stderr,"SWP: doing the DXWM thing\n");
2391     /* read hints for this window and adjust any position hints */
2392     if (XGetNormalHints(theDisp, mainW, &hints)) {
2393       hints.flags |= USPosition | USSize;
2394       hints.x = xwc.x;  hints.y = xwc.y;
2395       hints.width = xwc.width; hints.height = xwc.height;
2396       XSetNormalHints(theDisp, mainW, &hints);
2397     }
2398 
2399 #ifndef MWM     /* don't do this if you're running MWM */
2400     xwc.x -= 5;   xwc.y -= 25;    /* EVIL KLUDGE */
2401 #endif /* MWM */
2402   }
2403 #endif
2404 
2405   /* all non-DXWM window managers (?) */
2406   /* Move/Resize the window. */
2407   XConfigureWindow(theDisp, mainW,
2408 		   CWX | CWY | CWWidth | CWHeight /*| CWBorderWidth*/, &xwc);
2409 }
2410 
2411 
2412 
2413 /***********************************/
CropKey(dx,dy,grow,crop)2414 static void CropKey(dx,dy,grow,crop)
2415      int dx,dy,grow,crop;
2416 {
2417   int ocx, ocy;
2418 
2419   if (crop) { /* chop off a pixel from the appropriate edge */
2420     int dealt=1;
2421 
2422     ocx = cXOFF;  ocy = cYOFF;
2423     if      (dx<0 && cWIDE>1) DoCrop(cXOFF,   cYOFF,   cWIDE-1, cHIGH);
2424     else if (dx>0 && cWIDE>1) DoCrop(cXOFF+1, cYOFF,   cWIDE-1, cHIGH);
2425     else if (dy<0 && cHIGH>1) DoCrop(cXOFF,   cYOFF,   cWIDE,   cHIGH-1);
2426     else if (dy>0 && cHIGH>1) DoCrop(cXOFF,   cYOFF+1, cWIDE,   cHIGH-1);
2427     else { dealt = 0;  XBell(theDisp, 0); }
2428 
2429     if (dealt) {
2430       if (useroot) DrawEpic();
2431       else {
2432 	if (HaveSelection()) EnableSelection(0);
2433 	CreateXImage();
2434 	WCrop(eWIDE, eHIGH, cXOFF-ocx, cYOFF-ocy);
2435       }
2436     }
2437     return;
2438   }
2439 
2440   if (grow) MoveGrowSelection(0,  0,  dx, dy);
2441        else MoveGrowSelection(dx, dy, 0,  0);
2442 }
2443 
2444 
2445 /***********************************/
TrackPicValues(mx,my)2446 static void TrackPicValues(mx,my)
2447      int mx,my;
2448 {
2449   Window       rW,cW;
2450   int          rx,ry,ox,oy,x,y, orgx,orgy;
2451   u_int        mask;
2452   u_long       wh, bl;
2453   int          ty, w, ecol, done1;
2454   char         foo[128];
2455   const char   *str  =
2456    "8888,8888 = 123,123,123  #123456  (123,123,123 HSV)  [-2345,-2345]";
2457 
2458   ecol = 0;  wh = infobg;  bl = infofg;
2459 
2460   if (!dropper) {
2461     Pixmap      pix, mask;
2462     XColor      cfg, cbg;
2463 
2464     cfg.red = cfg.green = cfg.blue = 0x0000;
2465     cbg.red = cbg.green = cbg.blue = 0xffff;
2466 
2467     pix = MakePix1(rootW, dropper_bits,  dropper_width,  dropper_height);
2468     mask= MakePix1(rootW, dropperm_bits, dropperm_width, dropperm_height);
2469     if (pix && mask)
2470       dropper = XCreatePixmapCursor(theDisp, pix, mask, &cfg, &cbg,
2471 				    dropper_x_hot, dropper_y_hot);
2472     if (pix)  XFreePixmap(theDisp, pix);
2473     if (mask) XFreePixmap(theDisp, mask);
2474   }
2475 
2476   if (dropper) XDefineCursor(theDisp, mainW, dropper);
2477 
2478   /* do a colormap search for black and white if LocalCmap
2479      and use those colors instead of infobg and infofg */
2480 
2481   if (LocalCmap) {
2482     XColor ctab[256];   int  i;   long cval;
2483 
2484     for (i=0; i<nfcols; i++) ctab[i].pixel = freecols[i];
2485     XQueryColors(theDisp,LocalCmap,ctab,nfcols);
2486 
2487     /* find 'blackest' pixel */
2488     cval = 0x10000 * 3;
2489     for (i=0; i<nfcols; i++)
2490       if ((long)ctab[i].red + (long)ctab[i].green + (long)ctab[i].blue <cval) {
2491 	cval = ctab[i].red + ctab[i].green + ctab[i].blue;
2492 	bl = ctab[i].pixel;
2493       }
2494 
2495     /* find 'whitest' pixel */
2496     cval = -1;
2497     for (i=0; i<nfcols; i++)
2498       if ((long)ctab[i].red + (long)ctab[i].green + (long)ctab[i].blue >cval) {
2499 	cval = ctab[i].red + ctab[i].green + ctab[i].blue;
2500 	wh = ctab[i].pixel;
2501       }
2502   }
2503 
2504 
2505   XSetFont(theDisp, theGC, monofont);
2506   w = XTextWidth(monofinfo, str, (int) strlen(str));
2507 
2508   if (my > eHIGH/2) ty = 0;
2509                else ty = eHIGH-(monofinfo->ascent + mfinfo->descent)-4;
2510 
2511   XSetForeground(theDisp, theGC, bl);
2512   XFillRectangle(theDisp, mainW, theGC, 0, ty, (u_int) w + 8,
2513 		 (u_int) (monofinfo->ascent+monofinfo->descent) + 4);
2514   XSetForeground(theDisp, theGC, wh);
2515   XSetBackground(theDisp, theGC, bl);
2516   foo[0] = '\0';
2517 
2518 
2519   done1 = ox = oy = orgx = orgy = 0;
2520   while (1) {
2521     int px, py, pix;
2522 
2523     if (!XQueryPointer(theDisp,mainW,&rW,&cW,&rx,&ry,&x,&y,&mask)) continue;
2524     if (done1 && !(mask & Button2Mask)) break;    /* button released */
2525 
2526     CoordE2P(x,y, &px, &py);
2527     RANGE(px,0,pWIDE-1);
2528     RANGE(py,0,pHIGH-1);
2529 
2530     if (px!=ox || py!=oy || !done1) {  /* moved, or firsttime.  erase & draw */
2531       double h1, s1, v1;
2532       int    rval, gval, bval;
2533 
2534       if (picType == PIC8) {
2535 	ecol = pix = pic[py * pWIDE + px];
2536 	rval = rcmap[pix];  gval = gcmap[pix];  bval = bcmap[pix];
2537       }
2538       else {  /* PIC24 */
2539 	rval = pic[py * pWIDE * 3 + px * 3];
2540 	gval = pic[py * pWIDE * 3 + px * 3 + 1];
2541 	bval = pic[py * pWIDE * 3 + px * 3 + 2];
2542       }
2543 
2544       clearR = rval;  clearG = gval;  clearB = bval;
2545 
2546       rgb2hsv(rval, gval, bval, &h1, &s1, &v1);
2547       if (h1<0.0) h1 = 0.0;   /* map 'NOHUE' to 0.0 */
2548 
2549       if (!done1) { orgx = px;  orgy = py; }
2550 
2551       sprintf(foo,
2552    "%4d,%4d = %3d,%3d,%3d  #%02x%02x%02x  (%3d %3d %3d HSV)  [%5d,%5d]",
2553 	      px, py, rval, gval, bval, rval, gval, bval,
2554 	      (int) h1, (int) (s1 * 100), (int) (v1 * 100),
2555 	      px-orgx, py-orgy);
2556 
2557       XDrawImageString(theDisp,mainW,theGC, 4, ty + 2 + monofinfo->ascent,
2558 		       foo, (int) strlen(foo));
2559       ox = px;  oy = py;
2560       done1 = 1;
2561     }
2562   }
2563   SetCursors(-1);
2564 
2565 
2566   if (foo[0]) {
2567     strcat(foo, "\n");
2568     NewCutBuffer(foo);
2569   }
2570 
2571   XSetFont(theDisp, theGC, mfont);
2572   DrawWindow(0,ty,eWIDE,(monofinfo->ascent+monofinfo->descent)+4);
2573 
2574   if (picType == PIC8 && ecol != editColor) ChangeEC(ecol);
2575 }
2576 
2577 
2578 /***********************************/
IsConfig(dpy,ev,arg)2579 static Bool IsConfig(dpy, ev, arg)
2580      Display *dpy;
2581      XEvent  *ev;
2582      char    *arg;
2583 {
2584   XConfigureEvent *cev;
2585 
2586   if (ev->type == ConfigureNotify) {
2587     cev = (XConfigureEvent *) ev;
2588     if (cev->window == mainW && (cev->width != eWIDE || cev->height != eHIGH))
2589       *arg = 1;
2590   }
2591   return False;
2592 }
2593 
2594 /***********************************/
CheckForConfig()2595 static int CheckForConfig()
2596 {
2597   XEvent ev;
2598   char   foo;
2599 
2600   /* returns true if there's a config event in which mainW changes size
2601      in the event queue */
2602 
2603   XSync(theDisp, False);
2604   foo = 0;
2605   XCheckIfEvent(theDisp, &ev, IsConfig, &foo);
2606   return foo;
2607 }
2608 
2609 
2610 /************************************************************************/
SetEpicMode()2611 void SetEpicMode()
2612 {
2613   if (epicMode == EM_RAW) {
2614     dispMB.dim[DMB_RAW]    = 1;
2615     dispMB.dim[DMB_DITH]   = !(ncols>0 && picType == PIC8);
2616     dispMB.dim[DMB_SMOOTH] = 0;
2617   }
2618 
2619   else if (epicMode == EM_DITH) {
2620     dispMB.dim[DMB_RAW]    = 0;
2621     dispMB.dim[DMB_DITH]   = 1;
2622     dispMB.dim[DMB_SMOOTH] = 0;
2623   }
2624 
2625   else if (epicMode == EM_SMOOTH) {
2626     dispMB.dim[DMB_RAW]    = 0;
2627     dispMB.dim[DMB_DITH]   = 1;
2628     dispMB.dim[DMB_SMOOTH] = 1;
2629   }
2630 }
2631 
2632 
2633 /************************************************************************/
xvErrorHandler(disp,err)2634 int xvErrorHandler(disp, err)
2635      Display *disp;
2636      XErrorEvent *err;
2637 {
2638   char buf[128];
2639 
2640   /* in case the error occurred during the Grab command... */
2641   XUngrabServer(theDisp);
2642   XUngrabButton(theDisp, (u_int) AnyButton, 0, rootW);
2643 
2644   xerrcode = err->error_code;
2645 
2646   /* non-fatal errors:   (sets xerrcode and returns)
2647    *    BadAlloc
2648    *    BadAccess errors on XFreeColors call
2649    *    Any error on the 'XKillClient()' call
2650    *    BadWindow errors (on a GetProperty call) (workaround SGI problem)
2651    *    BadLength errors on XChangeProperty
2652    *    BadMatch  errors on XGetImage
2653    */
2654 
2655   if ((xerrcode == BadAlloc)                                               ||
2656       (xerrcode == BadAccess && err->request_code==88 /* X_FreeColors */ ) ||
2657       (err->request_code == 113                       /* X_KillClient */ ) ||
2658       (xerrcode == BadLength && err->request_code==18 /* X_ChangeProp */ ) ||
2659       (xerrcode == BadMatch  && err->request_code==73 /* X_GetImage   */ ) ||
2660       (xerrcode == BadWindow && err->request_code==20 /* X_GetProperty*/))
2661     return 0;
2662 
2663   else {
2664     /* all other errors are 'fatal' */
2665     XGetErrorText(disp, xerrcode, buf, 128);
2666     fprintf(stderr,"X Error: %s\n",buf);
2667     fprintf(stderr,"  Major Opcode:  %d\n",err->request_code);
2668 
2669     if (DEBUG) {   /* crash 'n' burn for debugging purposes */
2670       char *str;
2671       str  = NULL;
2672       *str = '0';
2673     }
2674 
2675     exit(-1);
2676   }
2677 
2678   return 0;
2679 }
2680 
2681 
2682 /************************************************************************/
onInterrupt(i)2683 static void onInterrupt(i)
2684      int i;
2685 {
2686   /* but first, if any input-grabbing popups are active, we have to 'cancel'
2687      them. */
2688 
2689   if (psUp) PSDialog(0);      /* close PS window */
2690 
2691 #ifdef HAVE_JPEG
2692   if (jpegUp) JPEGDialog(0);  /* close jpeg window */
2693 #endif
2694 
2695 #ifdef HAVE_JP2K
2696   if (jp2kUp) JP2KDialog(0);  /* close jpeg 2000 window */
2697 #endif
2698 
2699 #ifdef HAVE_TIFF
2700   if (tiffUp) TIFFDialog(0);  /* close tiff window */
2701 #endif
2702 
2703 #ifdef HAVE_PNG
2704   if (pngUp) PNGDialog(0);    /* close png window */
2705 #endif
2706 
2707   if (pcdUp) PCDDialog(0);    /* close pcd window */
2708 
2709 #ifdef HAVE_PIC2
2710   if (pic2Up) PIC2Dialog(0);  /* close pic2 window */
2711 #endif
2712 
2713 #ifdef HAVE_PCD
2714   if (pcdUp)  PCDDialog(0);   /* close pcd window */
2715 #endif
2716 
2717 #ifdef HAVE_MGCSFX
2718   if (mgcsfxUp) MGCSFXDialog(0);  /* close mgcsfx window */
2719 #endif
2720 
2721   ClosePopUp();
2722 
2723   /* make the interrupt signal look like a '\n' keypress in ctrlW */
2724   FakeKeyPress(ctrlW, XK_Return);
2725 
2726   frominterrupt = 1;
2727 }
2728 
2729 
2730 
2731 
2732 
2733 /***********************************/
Paint()2734 static void Paint()
2735 {
2736   Window  rW,cW;
2737   int     rx,ry, x,y, px,py, px1,py1, state;
2738   int     lx, ly, line, seenRelease;
2739   u_int   mask, nmask;
2740 
2741   /* paint pixels in either editCol (PIC8) or clear{R,G,B} (PIC24) until
2742      'shift' key is released.  beep on button presses other than B2.
2743      When shift is released, regen all pics (ala 'clearSelectedArea()') */
2744 
2745 
2746   if (!pen) {
2747     Pixmap      pix, pmask;
2748     XColor      cfg, cbg;
2749 
2750     cfg.red = cfg.green = cfg.blue = 0x0000;
2751     cbg.red = cbg.green = cbg.blue = 0xffff;
2752 
2753     pix = MakePix1(rootW, pen_bits,  pen_width,  pen_height);
2754     pmask= MakePix1(rootW, penm_bits, penm_width, penm_height);
2755     if (pix && pmask)
2756       pen = XCreatePixmapCursor(theDisp, pix, pmask, &cfg, &cbg,
2757 				    pen_x_hot, pen_y_hot);
2758     if (pix)   XFreePixmap(theDisp, pix);
2759     if (pmask) XFreePixmap(theDisp, pmask);
2760   }
2761 
2762   if (pen) XDefineCursor(theDisp, mainW, pen);
2763 
2764 
2765   XSelectInput(theDisp, mainW, ExposureMask | KeyPressMask
2766 	       | StructureNotifyMask /* | ButtonPressMask */
2767 	       | KeyReleaseMask | ColormapChangeMask
2768 	       | EnterWindowMask | LeaveWindowMask );
2769 
2770 
2771 
2772   state = 0;
2773   line = lx = ly = seenRelease = 0;
2774 
2775   while (state<100) {
2776     if (!XQueryPointer(theDisp,mainW,&rW,&cW,&rx,&ry,&x,&y,&mask)) continue;
2777 
2778     nmask = (~mask);
2779     px1 = px;  py1 = py;
2780     CoordE2P(x,y, &px, &py);
2781 
2782     switch (state) {
2783     case 0:               /* initial state:  make sure we do one pixel */
2784       px1 = lx = px;  py1 = ly = py;
2785       paintPixel(px, py);
2786 
2787       if      (nmask & ShiftMask  ) state = 99;
2788       else if (nmask & Button2Mask) state = 1;
2789       else if ( mask & ControlMask) state = 20;
2790       else                          state = 10;
2791       break;
2792 
2793 
2794     case 1:               /* waiting for click */
2795       if      (nmask & ShiftMask) state = 99;
2796       else if ( mask & Button2Mask) {
2797 	paintPixel(px, py);
2798 	if (mask & ControlMask) {
2799 	  lx = px;  ly = py;
2800 	  paintXLine(lx, ly, px, py, 1);
2801 	  line = 1;
2802 	  state = 20;
2803 	}
2804 	else state = 10;
2805       }
2806       break;
2807 
2808 
2809     case 10:               /* in freehand drawing mode */
2810       if      (nmask & ShiftMask  ) state = 99;
2811       else if (nmask & Button2Mask) state = 1;
2812       else if ( mask & ControlMask) {
2813 	lx = px;  ly = py;
2814 	paintXLine(lx, ly, px, py, 1);
2815 	line = 1;
2816 	state = 20;
2817       }
2818       else paintLine(px1,py1,px,py);
2819       break;
2820 
2821 
2822     case 20:               /* in line-drawing mode */
2823       if      (nmask & ShiftMask  ) state = 99;
2824       else if (nmask & ControlMask) {
2825 	/* remove xor-line, switch to freehand drawing mode or click-wait */
2826 	paintXLine(lx, ly, px1, py1, 0);
2827 	line = 0;
2828 	if (mask & Button2Mask) state = 10;
2829 	                   else state = 1;
2830       }
2831 
2832       else if ((mask & Button2Mask) && seenRelease) {
2833 	/* remove xor-line, draw line to pt, start xor-line from new pt */
2834 	paintXLine(lx, ly, px1, py1, 0);
2835 	paintLine (lx, ly, px1, py1);
2836 	paintXLine(px1,py1,px,  py,  1);
2837 	line = 1;
2838 	lx = px1;  ly = py1;
2839 
2840 	seenRelease = 0;
2841       }
2842 
2843       else {
2844 	/* if moved, erase old xor-line, draw new xor-line */
2845 	if (px != px1 || py != py1) {
2846 	  paintXLine(lx, ly, px1, py1, 0);
2847 	  paintXLine(lx, ly, px,  py,  1);
2848 	  line = 1;
2849 	}
2850 	else {
2851 	  paintXLine(lx, ly, px1, py1, 0);
2852 	  paintXLine(lx, ly, px1, py1, 1);
2853 	  XSync(theDisp, False);
2854 	  Timer(100);
2855 	}
2856 
2857 	if (nmask & Button2Mask) seenRelease = 1;
2858       }
2859       break;
2860 
2861     case 99:              /* EXIT loop:  cleanup */
2862       if (line) { /* erase old xor-line */
2863 	paintXLine(lx, ly, px1, py1, 0);
2864 	line = 0;
2865       }
2866       state = 100;     /* exit while loop */
2867       break;
2868     }
2869   }
2870 
2871 
2872   WaitCursor();
2873 
2874   XSelectInput(theDisp, mainW, ExposureMask | KeyPressMask
2875 	       | StructureNotifyMask | ButtonPressMask
2876 	       | KeyReleaseMask | ColormapChangeMask
2877 	       | EnterWindowMask | LeaveWindowMask );
2878 
2879   GenerateCpic();
2880   WaitCursor();
2881   GenerateEpic(eWIDE,eHIGH);
2882   WaitCursor();
2883   DrawEpic();        /* redraws selection, also */
2884   SetCursors(-1);
2885 }
2886 
2887 
2888 /***********************/
paintPixel(x,y)2889 static void paintPixel(x,y)
2890      int x,y;
2891 {
2892   /* paints pixel x,y (pic coords) into pic in editColor (PIC8) or clearR,G,B
2893      (PIC24) and does appropriate screen feedback. */
2894 
2895   int ex,ey,ex1,ey1,ew,eh;
2896 
2897   if (!PTINRECT(x,y,0,0,pWIDE,pHIGH)) return;
2898 
2899   if (picType == PIC8) {
2900     pic[y * pWIDE + x] = editColor;
2901   }
2902   else {  /* PIC24 */
2903     byte *pp = pic + (y * pWIDE + x) * 3;
2904     pp[0] = clearR;  pp[1] = clearG;  pp[2] = clearB;
2905   }
2906 
2907   /* visual feedback */
2908   CoordP2E(x,   y,   &ex,  &ey);
2909   CoordP2E(x+1, y+1, &ex1, &ey1);
2910 
2911   ew = ex1-ex;  eh = ey1-ey;
2912 
2913   if (picType == PIC8) XSetForeground(theDisp, theGC, cols[editColor]);
2914   else XSetForeground(theDisp, theGC, RGBToXColor(clearR, clearG, clearB));
2915 
2916   if (ew>0 && eh>0)
2917     XFillRectangle(theDisp,mainW,theGC, ex,ey, (u_int) ew, (u_int) eh);
2918 }
2919 
2920 
2921 /***********************************/
paintLine(x,y,x1,y1)2922 static void paintLine(x,y,x1,y1)
2923   int x,y,x1,y1;
2924 {
2925   int t,dx,dy,d,dd;
2926 
2927   dx = abs(x1-x);  dy = abs(y1-y);
2928 
2929   if (dx >= dy) {                       /* X is major axis */
2930     if (x > x1) {
2931        t = x; x = x1; x1 = t;
2932        t = y; y = y1; y1 = t;
2933      }
2934     d = dy + dy - dx;
2935     dd = y < y1 ? 1 : -1;
2936     while (x <= x1) {
2937       paintPixel(x,y);
2938       if (d > 0) {
2939         y += dd;
2940         d -= dx + dx;
2941       }
2942       ++x;
2943       d += dy + dy;
2944     }
2945   }
2946 
2947   else {                                /* Y is major axis */
2948     if (y > y1) {
2949        t = x; x = x1; x1 = t;
2950        t = y; y = y1; y1 = t;
2951      }
2952     d = dx + dx - dy;
2953     dd = x < x1 ? 1 : -1;
2954     while (y <= y1) {
2955       paintPixel(x,y);
2956       if (d > 0) {
2957         x += dd;
2958         d -= dy + dy;
2959       }
2960       ++y;
2961       d += dx + dx;
2962     }
2963   }
2964 
2965 
2966 }
2967 
2968 
2969 static int pntxlcol = 0;  /* index into xorMasks */
2970 
2971 /***********************************/
paintXLine(x,y,x1,y1,newcol)2972 static void paintXLine(x,y,x1,y1,newcol)
2973   int x,y,x1,y1,newcol;
2974 {
2975   /* draws a xor'd line on image from x,y to x1,y1 (pic coords) */
2976   int ex,ey, ex1,ey1,  tx,ty,tx1,ty1;
2977 
2978   if (newcol) pntxlcol = (pntxlcol+1) & 0x7;
2979 
2980   CoordP2E(x,  y,  &tx, &ty);
2981   CoordP2E(x+1,y+1,&tx1,&ty1);
2982   ex = tx + (tx1 - tx)/2;
2983   ey = ty + (ty1 - ty)/2;
2984 
2985   CoordP2E(x1,  y1,  &tx, &ty);
2986   CoordP2E(x1+1,y1+1,&tx1,&ty1);
2987   ex1 = tx + (tx1 - tx)/2;
2988   ey1 = ty + (ty1 - ty)/2;
2989 
2990   if (ex==ex1 && ey==ey1) return;
2991 
2992   XSetPlaneMask(theDisp, theGC, xorMasks[pntxlcol]);
2993   XSetFunction(theDisp, theGC, GXinvert);
2994   XDrawLine(theDisp, mainW, theGC, ex, ey, ex1, ey1);
2995   XSetFunction(theDisp, theGC, GXcopy);
2996   XSetPlaneMask(theDisp, theGC, AllPlanes);
2997 }
2998 
2999 
3000 /***********************************/
BlurPaint()3001 static void BlurPaint()
3002 {
3003   Window  rW,cW;
3004   int     rx,ry,ox,oy,x,y, px,py, done1, dragging;
3005   u_int   mask;
3006 
3007   /* blurs pixels in either editCol (PIC8) or clear{R,G,B} (PIC24) until
3008      'shift' key is released.  */
3009 
3010 
3011   /* if PIC8, uprev it to PIC24 */
3012   if (picType == PIC8) Select24to8MB(CONV24_24BIT);
3013 
3014   if (!blur) {
3015     Pixmap      pix, mask;
3016     XColor      cfg, cbg;
3017 
3018     cfg.red = cfg.green = cfg.blue = 0x0000;
3019     cbg.red = cbg.green = cbg.blue = 0xffff;
3020 
3021     pix = MakePix1(rootW, blur_bits,  blur_width,  blur_height);
3022     mask= MakePix1(rootW, blurm_bits, blurm_width, blurm_height);
3023     if (pix && mask)
3024       blur = XCreatePixmapCursor(theDisp, pix, mask, &cfg, &cbg,
3025 				    blur_x_hot, blur_y_hot);
3026     if (pix)  XFreePixmap(theDisp, pix);
3027     if (mask) XFreePixmap(theDisp, mask);
3028   }
3029 
3030   if (blur) XDefineCursor(theDisp, mainW, blur);
3031 
3032 
3033   XSelectInput(theDisp, mainW, ExposureMask | KeyPressMask
3034 	       | StructureNotifyMask /* | ButtonPressMask */
3035 	       | KeyReleaseMask | ColormapChangeMask
3036 	       | EnterWindowMask | LeaveWindowMask );
3037 
3038 
3039   done1 = dragging = ox = oy = 0;
3040   while (1) {
3041     if (!XQueryPointer(theDisp,mainW,&rW,&cW,&rx,&ry,&x,&y,&mask)) continue;
3042     if (done1 && !(mask & ShiftMask)) break;    /* Shift released */
3043     if (!(mask & Button3Mask)) { dragging = 0;  continue; }
3044 
3045     CoordE2P(x,y, &px, &py);
3046 
3047     if (!dragging || (dragging && (px!=ox || py!=oy))) {  /* click or drag */
3048       if (!dragging) blurPixel(px,py);
3049       else {
3050 	int dx,dy,i,lx,ly;
3051 
3052 	dx = px-ox;  dy = py-oy;   /* at least one will be non-zero */
3053 	if (abs(dx) > abs(dy)) {   /* X is major axis */
3054 	  for (i=0; i<=abs(dx); i++) {
3055 	    lx = ox + (i * dx)/abs(dx);
3056 	    ly = oy + (i * dy)/abs(dx);
3057 	    blurPixel(lx,ly);
3058 	  }
3059 	} else {                   /* Y is major axis */
3060 	  for (i=0; i<=abs(dy); i++) {
3061 	    lx = ox + (i * dx)/abs(dy);
3062 	    ly = oy + (i * dy)/abs(dy);
3063 	    blurPixel(lx,ly);
3064 	  }
3065 	}
3066       }
3067 
3068       done1 = 1;  dragging = 1;  ox = px;  oy = py;
3069     }
3070   }
3071 
3072   WaitCursor();
3073 
3074   XSelectInput(theDisp, mainW, ExposureMask | KeyPressMask
3075 	       | StructureNotifyMask | ButtonPressMask
3076 	       | KeyReleaseMask | ColormapChangeMask
3077 	       | EnterWindowMask | LeaveWindowMask );
3078 
3079   GenerateCpic();
3080   WaitCursor();
3081   GenerateEpic(eWIDE,eHIGH);
3082   WaitCursor();
3083   DrawEpic();        /* redraws selection, also */
3084   SetCursors(-1);
3085 }
3086 
3087 
3088 
3089 /***********************/
highbit(ul)3090 static int highbit(ul)
3091      unsigned long ul;
3092 {
3093   /* returns position of highest set bit in 'ul' as an integer (0-31),
3094      or -1 if none */
3095 
3096   int i;  unsigned long hb;
3097 
3098   hb = 0x80;  hb = hb << 24;   /* hb = 0x80000000UL */
3099   for (i=31; ((ul & hb) == 0) && i>=0;  i--, ul<<=1);
3100   return i;
3101 }
3102 
3103 
3104 /***********************/
RGBToXColor(r,g,b)3105 static unsigned long RGBToXColor(r,g,b)
3106      int r,g,b;
3107 {
3108   /* converts arbitrary rgb values (0-255) into an appropriate X color value,
3109      suitable for XSetForeground().  Works for ncols==0, all visual types,
3110      etc.  Note that it doesn't do the *best* job, it's really only for
3111      visual feedback during Painting, etc.  Should call GenEpic and such
3112      after modifying picture to redither, etc.  */
3113 
3114   unsigned long rv;
3115 
3116   if (picType == PIC8) {    /* simply find closest color in rgb map */
3117     int i,j,d,di;
3118 
3119     d = 3*(256*256);  j=0;
3120     for (i=0; i<numcols; i++) {
3121       di = ((r-rMap[i]) * (r-rMap[i])) +
3122 	   ((g-gMap[i]) + (g-gMap[i])) +
3123            ((b-bMap[i]) * (b-bMap[i]));
3124       if (i==0 || di<d) { j=i;  d=di; }
3125     }
3126 
3127     rv = cols[j];
3128   }
3129 
3130 
3131   else {    /* PIC24 */
3132     if (theVisual->class==TrueColor || theVisual->class==DirectColor) {
3133       unsigned long rmask, gmask, bmask;
3134       int           rshift, gshift, bshift, cshift, maplen;
3135 
3136       /* compute various shifting constants that we'll need... */
3137 
3138       rmask = theVisual->red_mask;
3139       gmask = theVisual->green_mask;
3140       bmask = theVisual->blue_mask;
3141 
3142       rshift = 7 - highbit(rmask);
3143       gshift = 7 - highbit(gmask);
3144       bshift = 7 - highbit(bmask);
3145 
3146       if (theVisual->class == DirectColor) {
3147 	maplen = theVisual->map_entries;
3148 	if (maplen>256) maplen=256;
3149 	cshift = 7 - highbit((u_long) (maplen-1));
3150 
3151 	r = (u_long) directConv[(r>>cshift) & 0xff] << cshift;
3152 	g = (u_long) directConv[(g>>cshift) & 0xff] << cshift;
3153 	b = (u_long) directConv[(b>>cshift) & 0xff] << cshift;
3154       }
3155 
3156 
3157       /* shift the bits around */
3158       if (rshift<0) r = r << (-rshift);
3159       else r = r >> rshift;
3160 
3161       if (gshift<0) g = g << (-gshift);
3162       else g = g >> gshift;
3163 
3164       if (bshift<0) b = b << (-bshift);
3165       else b = b >> bshift;
3166 
3167       r = r & rmask;
3168       g = g & gmask;
3169       b = b & bmask;
3170 
3171       rv =r | g | b;
3172     }
3173 
3174     else {                          /* non-TrueColor/DirectColor visual */
3175       if (!ncols)
3176 	rv = ((r + g + b >= 128*3) ? white : black);
3177       else                         /* use closest color in stdcmap */
3178 	rv = stdcols[(r&0xe0) | ((g&0xe0)>>3) | ((b&0xc0) >> 6)];
3179     }
3180   }
3181 
3182   return rv;
3183 }
3184 
3185 
3186 /***********************/
blurPixel(x,y)3187 static void blurPixel(x,y)
3188      int x,y;
3189 {
3190   /* blurs pixel x,y (pic coords) into pic in editColor (PIC8) or clearR,G,B
3191      (PIC24) and does appropriate screen feedback.  Does a 3x3 average
3192      around the pixel, and replaces it with the average value (PIC24), or
3193      the closest existing color to the average value (PIC8) */
3194 
3195   byte *pp;
3196   int i, j, d, di;
3197   int ex,ey,ex1,ey1,ew,eh;
3198   int ar,ag,ab,ac;
3199 
3200   if (!PTINRECT(x,y,0,0,pWIDE,pHIGH)) return;
3201 
3202   ar = ag = ab = ac = 0;
3203   for (i=y-1; i<=y+1; i++) {
3204     for (j=x-1; j<=x+1; j++) {
3205       if (PTINRECT(j,i, 0,0,pWIDE,pHIGH)) {
3206 	if (picType == PIC8) {
3207 	  pp = pic + i * pWIDE + j;
3208 	  ar += rMap[*pp];  ag += gMap[*pp];  ab += bMap[*pp];
3209 	}
3210 	else {
3211 	  pp = pic + (i * pWIDE + j) * 3;
3212 	  ar += pp[0];  ag += pp[1];  ab += pp[2];
3213 	}
3214 	ac++;
3215       }
3216     }
3217   }
3218 
3219   ar /= ac;  ag /= ac;  ab /= ac;
3220 
3221 
3222   if (picType == PIC8) {  /* find nearest actual color */
3223     d = 3*(256*256);  j=0;
3224     for (i=0; i<numcols; i++) {
3225       di = ((ar-rMap[i]) * (ar-rMap[i])) +
3226 	   ((ag-gMap[i]) + (ag-gMap[i])) +
3227            ((ab-bMap[i]) * (ab-bMap[i]));
3228       if (i==0 || di<d) { j=i;  d=di; }
3229     }
3230 
3231     ac = j;
3232     pic[y * pWIDE + x] = ac;
3233   }
3234   else {  /* PIC24 */
3235     pp = pic + (y * pWIDE + x) * 3;
3236     pp[0] = ar;  pp[1] = ag;  pp[2] = ab;
3237   }
3238 
3239   /* visual feedback */
3240   CoordP2E(x,   y,   &ex,  &ey);
3241   CoordP2E(x+1, y+1, &ex1, &ey1);
3242 
3243   ew = ex1-ex;  eh = ey1-ey;
3244 
3245   if (picType == PIC8) XSetForeground(theDisp, theGC, cols[ac]);
3246   else XSetForeground(theDisp, theGC, RGBToXColor(ar, ag, ab));
3247 
3248   if (ew>0 && eh>0)
3249     XFillRectangle(theDisp,mainW,theGC, ex,ey, (u_int) ew, (u_int) eh);
3250 }
3251 
3252 
3253 
3254 
3255 
3256 /***********************/
annotatePic()3257 static void annotatePic()
3258 {
3259   int                i, w,h, len;
3260   byte              *cimg;
3261   char               txt[256];
3262   static char        buf[256] = {'\0'};
3263   static const char *labels[] = {"\nOk", "\033Cancel" };
3264 
3265   sprintf(txt, "Image Annotation:\n\n%s",
3266 	  "Enter string to be placed on image.");
3267 
3268   i = GetStrPopUp(txt, labels, 2, buf, 256, "", 0);
3269   if (i==1 || strlen(buf)==0) return;
3270 
3271 
3272   /* build a 'cimg' array to be pasted on clipboard */
3273   w = strlen(buf) * 6 - 1;  h = 9;
3274   len = CIMG_PIC8 + w*h;
3275 
3276   cimg = (byte *) malloc((size_t) len);
3277   if (!cimg) {
3278     ErrPopUp("Error:  Unable to allocate memory for this operation.", "\nOk");
3279     return;
3280   }
3281 
3282   cimg[CIMG_LEN  ] =  len      & 0xff;
3283   cimg[CIMG_LEN+1] = (len>> 8) & 0xff;
3284   cimg[CIMG_LEN+2] = (len>>16) & 0xff;
3285   cimg[CIMG_LEN+3] = (len>>24) & 0xff;
3286 
3287   cimg[CIMG_W  ] =  w     & 0xff;
3288   cimg[CIMG_W+1] = (w>>8) & 0xff;
3289 
3290   cimg[CIMG_H  ] =  h     & 0xff;
3291   cimg[CIMG_H+1] = (h>>8) & 0xff;
3292 
3293   cimg[CIMG_24]    = 0;
3294   cimg[CIMG_TRANS] = 1;
3295   cimg[CIMG_TRVAL] = 0;
3296 
3297   xvbzero((char *) cimg + CIMG_PIC8, (size_t) w*h);
3298 
3299   if (picType == PIC8) {
3300     cimg[CIMG_CMAP + 3 + 0] = rMap[editColor];
3301     cimg[CIMG_CMAP + 3 + 1] = gMap[editColor];
3302     cimg[CIMG_CMAP + 3 + 2] = bMap[editColor];
3303   } else {
3304     cimg[CIMG_CMAP + 3 + 0] = clearR;
3305     cimg[CIMG_CMAP + 3 + 1] = clearG;
3306     cimg[CIMG_CMAP + 3 + 2] = clearB;
3307   }
3308 
3309   DrawStr2Pic(buf, w/2, h/2, cimg+CIMG_PIC8, w,h, 1);
3310 
3311   SaveToClip(cimg);
3312   free(cimg);
3313 
3314   /* if (HaveSelection()) EnableSelection(0); */
3315   DoImgPaste();
3316 }
3317 
3318 
3319 
3320