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