1 /* This file contains routines that encapsulate a drawing area widget.
2  * It is slightly different in format than the other files because it
3  * also has some callback wrappers that extract relevant information and
4  * then pass that to the user callback functions for redisplaying,
5  * keyboard input and mouse clicks/motion.
6  *
7  *                     This code is under the GNU Copyleft.
8  *
9  *  Dominic Giampaolo
10  *  dbg@sgi.com
11  */
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include "xstuff.h"
15 #include "libsx.h"
16 #include "libsx_private.h"
17 #include "DrawingA.h"
18 
19 
20 extern WindowState *lsx_curwin;    /* global handle for the current window */
21 
22 
23 
24 /*
25  * Internal prototypes that massage the data into something more
26  * digestable by regular humans.
27  */
28 static void  _redisplay(Widget w, void *me, XADCS *call_data);
29 static void  _resize(Widget w,    void *me, XADCS *call_data);
30 static void  _do_input(Widget w,  void *me, XADCS *call_data);
31 static void  _do_motion(Widget w, void *me, XADCS *call_data);
32 static char *TranslateKeyCode(XEvent *ev);
33 static GC    setup_gc(Widget w);
34 
35 
36 
37 
38 
39 static DrawInfo *draw_info_head = NULL;
40 static DrawInfo *cur_di=NULL;   /* current drawing area info structure */
41 static Window    window;        /* only used below by the drawing functions. */
42 static GC        drawgc;
43 static Display  *display=NULL;
44 
45 /*
46  * Drawing Area Creation Routine.
47  */
48 
MakeDrawArea(int width,int height,RedisplayCB redisplay,void * data)49 Widget MakeDrawArea(int width, int height, RedisplayCB redisplay, void *data)
50 {
51   int    n = 0;
52   Arg    wargs[5];		/* Used to set widget resources */
53   Widget draw_widget;
54   DrawInfo *di;
55 
56   if (lsx_curwin->toplevel == NULL && OpenDisplay(0, NULL) == 0)
57     return NULL;
58 
59   di = (DrawInfo *)calloc(sizeof(DrawInfo), 1);
60   if (di == NULL)
61     return NULL;
62 
63   n = 0;
64   XtSetArg(wargs[n], XtNwidth, width);		n++;
65   XtSetArg(wargs[n], XtNheight,height);		n++;
66 
67   draw_widget = XtCreateManagedWidget("drawing_area", drawingAreaWidgetClass,
68 				      lsx_curwin->form_widget,wargs,n);
69 
70   if (draw_widget == NULL)
71    {
72      free(di);
73      return NULL;
74    }
75 
76   di->drawgc     = setup_gc(draw_widget);
77   di->foreground = BlackPixel(lsx_curwin->display, lsx_curwin->screen);
78   di->background = WhitePixel(lsx_curwin->display, lsx_curwin->screen);
79   di->mask       = 0xffffffff;
80 
81   di->user_data   = data;
82   di->redisplay   = redisplay;
83 
84   XtAddCallback(draw_widget, XtNexposeCallback, (XtCallbackProc)_redisplay,di);
85   XtAddCallback(draw_widget, XtNresizeCallback, (XtCallbackProc)_resize,   di);
86   XtAddCallback(draw_widget, XtNinputCallback,  (XtCallbackProc)_do_input, di);
87   XtAddCallback(draw_widget, XtNmotionCallback, (XtCallbackProc)_do_motion,di);
88 
89   lsx_curwin->last_draw_widget = draw_widget;
90 
91   di->widget = draw_widget;
92   di->next = draw_info_head;
93   draw_info_head = di;
94   cur_di = di;
95 
96   /*
97    * Make sure the font is set to something sane.
98    */
99   if (lsx_curwin->font == NULL)
100     lsx_curwin->font = GetFont("fixed");
101   SetWidgetFont(draw_widget, lsx_curwin->font);
102 
103   return draw_widget;
104 }
105 
106 
107 
108 /*
109  * Internal function for getting a graphics context so we can draw.
110  */
setup_gc(Widget w)111 static GC setup_gc(Widget w)
112 {
113   int fore_g,back_g;      /* Fore and back ground pixels */
114   GC  drawgc;
115 
116   back_g = WhitePixel(XtDisplay(w),DefaultScreen(XtDisplay(w)));
117   fore_g = BlackPixel(XtDisplay(w),DefaultScreen(XtDisplay(w)));
118 
119   /* Create drawing GC */
120   drawgc = XCreateGC(XtDisplay(w), DefaultRootWindow(XtDisplay(w)), 0, 0);
121 
122   XSetBackground(XtDisplay(w), drawgc, back_g);
123   XSetForeground(XtDisplay(w), drawgc, fore_g);
124   XSetFunction(XtDisplay(w),   drawgc, GXcopy);
125 
126   return drawgc;
127 } /* end of setup_gc() */
128 
129 
130 
131 /*
132  * This function searches through our list of drawing area info structures
133  * and returns the one that matches the specified widget.  We have to be
134  * careful and make sure that the DrawInfo structure we match is for the
135  * right display.
136  */
libsx_find_draw_info(Widget w)137 DrawInfo *libsx_find_draw_info(Widget w)
138 {
139   DrawInfo *di;
140 
141   if (w == NULL)
142     return NULL;
143 
144   for(di=draw_info_head;  di; di=di->next)
145    {
146      for(; di && di->widget != w; di=di->next)
147        ;
148 
149      if (di == NULL)       /* we didn't find it */
150        break;
151      if (XtDisplay(di->widget) == XtDisplay(w)) /* then we've found it */
152        break;
153    }
154 
155   return di;
156 }
157 
158 
159 
160 
SetButtonDownCB(Widget w,MouseButtonCB button_down)161 void   SetButtonDownCB(Widget w, MouseButtonCB button_down)
162 {
163   DrawInfo *di;
164 
165   if ((di = libsx_find_draw_info(w)) == NULL)
166     return;
167 
168   di->button_down = button_down;
169 }
170 
171 
SetButtonUpCB(Widget w,MouseButtonCB button_up)172 void   SetButtonUpCB(Widget w, MouseButtonCB button_up)
173 {
174   DrawInfo *di;
175 
176   if ((di = libsx_find_draw_info(w)) == NULL)
177     return;
178 
179   di->button_up = button_up;
180 }
181 
SetKeypressCB(Widget w,KeyCB keypress)182 void   SetKeypressCB(Widget w, KeyCB keypress)
183 {
184   DrawInfo *di;
185 
186   if ((di = libsx_find_draw_info(w)) == NULL)
187     return;
188 
189   di->keypress = keypress;
190 }
191 
192 
SetMouseMotionCB(Widget w,MotionCB motion)193 void   SetMouseMotionCB(Widget w, MotionCB motion)
194 {
195   DrawInfo *di;
196 
197   if ((di = libsx_find_draw_info(w)) == NULL)
198     return;
199 
200   di->motion = motion;
201 }
202 
203 
204 
SetDrawMode(int mode)205 void SetDrawMode(int mode)
206 {
207   if (display == NULL)
208     return;
209 
210   if (mode == SANE_XOR)
211    {
212      cur_di->mask = cur_di->foreground ^ cur_di->background;
213      XSetForeground(display, drawgc, 0xffffffff);
214      XSetBackground(display, drawgc, cur_di->background);
215      XSetFunction(display,   drawgc, GXxor);
216      XSetPlaneMask(display,  drawgc, cur_di->mask);
217    }
218   else
219    {
220      XSetForeground(display, drawgc, cur_di->foreground);
221      XSetBackground(display, drawgc, cur_di->background);
222      XSetFunction(display,   drawgc, mode);
223      XSetPlaneMask(display,  drawgc, 0xffffffff);
224      cur_di->mask = 0xffffffff;
225    }
226 }
227 
228 
SetLineWidth(int width)229 void SetLineWidth(int width)
230 {
231   if (display && width > 0)
232     XSetLineAttributes(display, drawgc, width, LineSolid, CapButt, JoinMiter);
233 }
234 
235 
SetDrawArea(Widget w)236 void SetDrawArea(Widget w)
237 {
238   DrawInfo *di;
239 
240   if (lsx_curwin->toplevel == NULL || w == NULL)
241     return;
242 
243   if ((di=libsx_find_draw_info(w)) == NULL)  /* w isn't really a draw area */
244     return;
245 
246   window  = (Window)XtWindow(w);
247   drawgc  = di->drawgc;
248   display = XtDisplay(w);
249   cur_di  = di;
250 
251   lsx_curwin->last_draw_widget = w;
252 
253 
254 #ifdef    OPENGL_SUPPORT
255 
256   if (lsx_curwin->gl_context)
257     glXMakeCurrent(display, XtWindow(w), lsx_curwin->gl_context);
258 
259 #endif /* OPENGL_SUPPORT */
260 
261 }
262 
263 
264 #ifdef    OPENGL_SUPPORT
265 
SwapBuffers(void)266 void SwapBuffers(void)
267 {
268   if (lsx_curwin == NULL || lsx_curwin->last_draw_widget == NULL)
269     return;
270 
271   glXSwapBuffers(display, window);
272 }
273 
274 #endif /* OPENGL_SUPPORT */
275 
276 
GetDrawAreaSize(int * w,int * h)277 void GetDrawAreaSize(int *w, int *h)
278 {
279   int n;
280   Arg wargs[2];
281   Dimension nwidth, nheight;
282 
283   if (lsx_curwin->toplevel == NULL || lsx_curwin->last_draw_widget == NULL
284       || w == NULL || h == NULL)
285     return;
286 
287   *w = *h = 0;
288 
289   n = 0;
290   XtSetArg(wargs[n], XtNwidth,  &nwidth);      n++;
291   XtSetArg(wargs[n], XtNheight, &nheight);     n++;
292 
293   XtGetValues(lsx_curwin->last_draw_widget, wargs, n);
294 
295   *w = nwidth;
296   *h = nheight;
297 }
298 
299 
300 
301 /*
302  * These are the drawing area "draw" functions.  You use these functions
303  * to draw into a DrawingArea widget.
304  */
305 
ClearDrawArea(void)306 void ClearDrawArea(void)
307 {
308   XClearWindow(display, window);
309 }
310 
311 
SetColor(int color)312 void SetColor(int color)
313 {
314   if (cur_di == NULL || display == NULL)
315     return;
316 
317   cur_di->foreground = color;
318 
319 
320   if (cur_di->mask != 0xffffffff)
321     XSetPlaneMask(display, drawgc, cur_di->foreground ^ cur_di->background);
322   else
323     XSetForeground(display, drawgc, color);
324 }
325 
326 
DrawPixel(int x1,int y1)327 void DrawPixel(int x1, int y1)
328 {
329   XDrawPoint(display, window, drawgc, x1, y1);
330 }
331 
332 
GetPixel(int x1,int y1)333 int GetPixel(int x1, int y1)
334 {
335   char ch;
336 
337   GetImage(&ch, x1, y1, 1, 1);  /* gag! no other easy way to do it */
338 
339   return (int)ch;
340 }
341 
342 
DrawLine(int x1,int y1,int x2,int y2)343 void DrawLine(int x1, int y1, int x2, int y2)
344 {
345   XDrawLine(display, window, drawgc, x1, y1, x2, y2);
346 }
347 
348 
DrawPolyline(XPoint * points,int n)349 void DrawPolyline(XPoint *points, int n)
350 {
351   XDrawLines(display, window, drawgc, points, n, CoordModeOrigin);
352 }
353 
354 
DrawFilledPolygon(XPoint * points,int n)355 void DrawFilledPolygon (XPoint *points, int n)
356 {
357   XFillPolygon(display, window, drawgc, points, n, Complex, CoordModeOrigin);
358 }
359 
360 
361 
DrawFilledBox(int x,int y,int fwidth,int fheight)362 void DrawFilledBox(int x, int y, int fwidth, int fheight)
363 {
364   if (fwidth < 0)
365    { fwidth  *= -1; x -= fwidth; }
366   if (fheight < 0)
367    { fheight *= -1; y -= fheight; }
368 
369   XFillRectangle(display, window, drawgc, x, y, fwidth, fheight);
370 }
371 
372 
373 
DrawBox(int x,int y,int bwidth,int bheight)374 void DrawBox(int x, int y, int bwidth, int bheight)
375 {
376   if (bwidth < 0)
377    { bwidth  *= -1; x -= bwidth; }
378   if (bheight < 0)
379    { bheight *= -1; y -= bheight; }
380 
381   XDrawRectangle(display, window, drawgc, x, y, bwidth, bheight);
382 }
383 
384 
385 
DrawText(char * string,int x,int y)386 void DrawText(char *string, int x, int y)
387 {
388   XDrawImageString(display, window, drawgc, x, y, string, strlen(string));
389 }
390 
391 
DrawArc(int x,int y,int awidth,int aheight,int angle1,int angle2)392 void DrawArc(int x, int y, int awidth, int aheight, int angle1, int angle2)
393 {
394   angle1 = angle1 * 64;  /* multiply by 64 because X works in 64'ths of a */
395   angle2 = angle2 * 64;  /* a degree and we just want to work in degrees  */
396 
397   if (awidth < 0)
398    { awidth *= -1;  x -= awidth; }
399   if (aheight < 0)
400    { aheight *= -1; y -= aheight; }
401 
402   XDrawArc (display, window, drawgc, x, y,
403 	    awidth, aheight, angle1, angle2);
404 }
405 
406 
DrawFilledArc(int x,int y,int awidth,int aheight,int angle1,int angle2)407 void DrawFilledArc(int x, int y, int awidth, int aheight,
408 		   int angle1, int angle2)
409 {
410   angle1 = angle1 * 64;  /* multiply by 64 because X works in 64'ths of a */
411   angle2 = angle2 * 64;  /* a degree and we just want to work in degrees  */
412 
413   if (awidth < 0)
414    { awidth *= -1;  x -= awidth; }
415   if (aheight < 0)
416    { aheight *= -1; y -= aheight; }
417 
418   XFillArc (display, window, drawgc, x, y, awidth, aheight, angle1, angle2);
419 }
420 
421 
DrawImage(char * data,int x,int y,int width,int height)422 void DrawImage(char *data, int x, int y, int width, int height)
423 {
424   XImage *xi;
425 
426   if (lsx_curwin->toplevel == NULL || data == NULL)
427     return;
428 
429   xi = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)),
430 		    8, ZPixmap, 0, data, width, height,
431 		    XBitmapPad(display),  width);
432   if (xi == NULL)
433     return;
434 
435   XPutImage(display, window, drawgc, xi, 0,0, x,y,  xi->width,xi->height);
436 
437   XFree((char *)xi);
438 }
439 
440 
DrawBitmap(char * data,int x,int y,int width,int height)441 void DrawBitmap(char *data, int x, int y, int width, int height)
442 {
443   Pixmap pix;
444 
445   if (lsx_curwin->toplevel == NULL || data == NULL)
446     return;
447 
448   pix = XCreatePixmapFromBitmapData(display, window, data, width, height,
449 				    cur_di->foreground, cur_di->background,
450 				    DefaultDepth(display, lsx_curwin->screen));
451   if (pix == NULL)
452     return;
453 
454   XCopyArea(display, pix, window, drawgc, 0,0, width, height, x,y);
455 
456   XFreePixmap(display, pix);
457 }
458 
459 
460 /*
461  * This function is kind of gaggy in some respects because it
462  * winds up requiring twice the amount of memory really needed.
463  * It would be possible to return the XImage structure directly,
464  * but that kind of defeats the whole purpose of libsx in addition
465  * to the fact that X packs the data in ways that might not be
466  * what the user wants.  So we unpack the data and just put the
467  * raw bytes of the image in the user's buffer.
468  */
GetImage(char * data,int x,int y,int width,int height)469 void GetImage(char *data, int x, int y, int width, int height)
470 {
471   XImage *xi;
472   int i,j;
473   char *xi_data;
474 
475   if (lsx_curwin->toplevel == NULL || data == NULL)
476     return;
477 
478 
479   xi = XGetImage(display, window, x,y, width,height, ~0, ZPixmap);
480 
481   xi_data = xi->data;
482   for(i=0; i < height; i++)
483    {
484      char *line_start = xi_data;
485 
486      for(j=0; j < width; j++, xi_data++, data++)
487        *data = *xi_data;
488 
489      while((xi_data - line_start) < xi->bytes_per_line)
490        xi_data++;
491    }
492 
493   XFree((char *)xi);
494 }
495 
496 
497 
498 
499 /*
500  * Below are internal callbacks that the drawing area calls.  They in
501  * turn call the user callback functions.
502  */
503 
504 /*
505  * Internal callback routines for the drawing areas that massage
506  * all the X garbage into a more digestable form.
507  */
508 
_redisplay(Widget w,void * data,XADCS * call_data)509 static void _redisplay(Widget w, void *data, XADCS *call_data)
510 {
511   int new_width, new_height;
512   DrawInfo *di = data;
513 
514   if (call_data->event->xexpose.count != 0) /* Wait until last expose event */
515     return;
516 
517   SetDrawArea(w);
518   GetDrawAreaSize(&new_width, &new_height);   /* get the draw area size */
519 
520   if (di->redisplay)
521     di->redisplay(w, new_width, new_height, di->user_data);
522 }
523 
524 
525 
526 /* Called when a DrawingArea is resized.
527  */
_resize(Widget w,void * data,XADCS * call_data)528 static void _resize(Widget w, void *data, XADCS *call_data)
529 {
530   int new_width, new_height;
531   DrawInfo *di = data;
532 
533   if (call_data->event->xexpose.count != 0) /* Wait until last expose event */
534     return;
535 
536   SetDrawArea(w);
537   GetDrawAreaSize(&new_width, &new_height);   /* get the new draw area size */
538 
539   if (di->redisplay)
540     di->redisplay(w, new_width, new_height, di->user_data);
541 }
542 
543 
544 
545 /* Called when a DrawingArea has input (either mouse or keyboard).
546  */
_do_input(Widget w,void * data,XADCS * call_data)547 static void _do_input(Widget w, void *data, XADCS *call_data)
548 {
549   char *input;
550   DrawInfo *di = data;
551 
552   SetDrawArea(w);
553   if (call_data->event->type == ButtonPress)
554    {
555      if (di->button_down)
556        di->button_down(w, call_data->event->xbutton.button,
557 		       call_data->event->xbutton.x,call_data->event->xbutton.y,
558 		       di->user_data);
559    }
560   else if (call_data->event->type == ButtonRelease)
561    {
562      if (di->button_up)
563        di->button_up(w, call_data->event->xbutton.button,
564 		     call_data->event->xbutton.x, call_data->event->xbutton.y,
565 		     di->user_data);
566    }
567   else if (call_data->event->type == KeyPress)
568    {
569      input = TranslateKeyCode(call_data->event);
570 
571      if (input && *input != '\0' && di->keypress)
572        di->keypress(w, input, 0, di->user_data);
573    }
574   else if (call_data->event->type == KeyRelease)
575    {
576      input = TranslateKeyCode(call_data->event);
577 
578      if (input && *input != '\0' && di->keypress)
579        di->keypress(w, input, 1, di->user_data);
580    }
581 }
582 
583 
_do_motion(Widget w,void * data,XADCS * call_data)584 static void _do_motion(Widget w, void *data,  XADCS *call_data)
585 {
586   DrawInfo *di = data;
587 
588   SetDrawArea(w);
589   if (di->motion)
590     di->motion(w, call_data->event->xmotion.x,  call_data->event->xmotion.y,
591 	       di->user_data);
592 }
593 
594 
595 
596 #define KEY_BUFF_SIZE 256
597 static char key_buff[KEY_BUFF_SIZE];
598 
TranslateKeyCode(XEvent * ev)599 static char *TranslateKeyCode(XEvent *ev)
600 {
601   int count;
602   char *tmp;
603   KeySym ks;
604 
605   if (ev)
606    {
607      count = XLookupString((XKeyEvent *)ev, key_buff, KEY_BUFF_SIZE, &ks,NULL);
608      key_buff[count] = '\0';
609      if (count == 0)
610       {
611 	tmp = XKeysymToString(ks);
612 	if (tmp)
613 	  strcpy(key_buff, tmp);
614 	else
615 	  strcpy(key_buff, "");
616       }
617 
618      return key_buff;
619    }
620   else
621     return NULL;
622 }
623 
624 
625 #define ABS(x)     ((x < 0) ? -x : x)
626 #define SWAP(a,b)  { a ^= b; b ^= a; a ^= b; }
627 
ScrollDrawArea(int dx,int dy,int x1,int y1,int x2,int y2)628 void ScrollDrawArea(int dx, int dy, int x1, int y1, int x2, int y2)
629 {
630   int w, h, x3, y3, x4, y4, _dx_, _dy_;
631   Window win=window;   /* window is a static global */
632 
633 
634   if (dx == 0 && dy == 0)
635     return;
636 
637   if (display == NULL)
638     return;
639 
640 
641   if (x2 < x1) SWAP (x1,x2);
642   if (y2 < y1) SWAP (y1,y2);
643 
644   _dx_ = ABS(dx);
645   _dy_ = ABS(dy);
646 
647   x3 = x1 + _dx_;
648   y3 = y1 + _dy_;
649 
650   x4 = x2 - _dx_ +1;
651   y4 = y2 - _dy_ +1;
652 
653   w = x2 - x3 +1;
654   h = y2 - y3 +1;
655 
656 
657   if (dx <= 0)
658    {
659      if (dy <= 0)
660       {
661 	XCopyArea (display,win,win,drawgc, x1, y1, w, h, x3, y3);
662 
663 	if (_dy_)
664 	  XClearArea (display,win, x1, y1, w+_dx_, _dy_, FALSE);
665 
666 	if (_dx_)
667 	  XClearArea (display,win, x1, y1, _dx_, h, FALSE);
668 
669      }
670      else              /* dy > 0 */
671       {
672 	XCopyArea (display,win,win,drawgc, x1, y3, w, h, x3, y1);
673 
674 	XClearArea (display,win, x1, y4, w+_dx_, _dy_, FALSE);
675 
676 	if (_dx_)
677 	  XClearArea (display,win, x1, y1, _dx_, h, FALSE);
678       }
679    }
680   else                 /* dx > 0 */
681    {
682      if (dy <= 0)
683       {
684 	XCopyArea (display,win,win,drawgc, x3, y1, w, h, x1, y3);
685 
686 	if (_dy_)
687 	  XClearArea (display,win, x1, y1, w+_dx_, _dy_, FALSE);
688 
689 	XClearArea (display,win, x4, y3, _dx_, h, FALSE);
690       }
691      else              /* dy > 0 */
692       {
693 	XCopyArea (display,win,win, drawgc, x3, y3, w, h, x1, y1);
694 
695 	XClearArea (display,win, x1, y4, w+_dx_, _dy_, FALSE);
696 
697 	XClearArea (display,win, x4, y1, _dx_, h, FALSE);
698       }
699    }
700 }
701