1 /*
2  *  buttons.c:		Draw MWFA player buttons in X11 window
3  *
4  *  Written by:		Ullrich Hafner
5  *
6  *  This file is part of FIASCO (Fractal Image And Sequence COdec)
7  *  Copyright (C) 1994-2000 Ullrich Hafner
8  */
9 
10 /*
11  *  $Date: 2000/06/15 17:23:11 $
12  *  $Author: hafner $
13  *  $Revision: 5.2 $
14  *  $State: Exp $
15  */
16 
17 #include "config.h"
18 
19 #ifndef X_DISPLAY_MISSING
20 
21 #include <X11/Xlib.h>
22 #include <X11/Intrinsic.h>
23 #include <X11/StringDefs.h>
24 
25 #if STDC_HEADERS
26 #	include <stdlib.h>
27 #endif /* not STDC_HEADERS */
28 
29 #include "types.h"
30 #include "macros.h"
31 
32 #include "display.h"
33 #include "binerror.h"
34 #include "buttons.h"
35 
36 /*****************************************************************************
37 
38 			     local variables
39 
40 *****************************************************************************/
41 
42 static const int EVENT_MASK = (KeyPressMask | ButtonPressMask |
43 			       ButtonReleaseMask | ExposureMask);
44 
45 /*****************************************************************************
46 
47 				prototypes
48 
49 *****************************************************************************/
50 
51 static void
52 draw_progress_bar (x11_info_t *xinfo, binfo_t *binfo, unsigned n,
53 		   unsigned n_frames);
54 static void
55 draw_button (x11_info_t *xinfo, binfo_t *binfo,
56 	     buttons_t button, bool_t pressed);
57 static void
58 draw_control_panel (x11_info_t *xinfo, binfo_t *binfo,
59 		    unsigned n, unsigned n_frames);
60 
61 /*****************************************************************************
62 
63 				public code
64 
65 *****************************************************************************/
66 
67 binfo_t *
init_buttons(x11_info_t * xinfo,unsigned n,unsigned n_frames,unsigned buttons_height,unsigned progbar_height)68 init_buttons (x11_info_t *xinfo, unsigned n, unsigned n_frames,
69 	      unsigned buttons_height, unsigned progbar_height)
70 /*
71  *  Initialize a toolbar with the typical collection of video player
72  *  buttons (pause, play, record, next, etc.) in the window given by 'xinfo'.
73  *  'n' gives the current frame, 'whereas' n_frames is the total number of
74  *  frames of the video stream.
75  *  The size of the button toolbar is given by 'buttons_height',
76  *  the size of the progressbar is given by 'progbar_height'.
77  *
78  *  Return value:
79  *	struct managing the toolbar and progressbar information
80  */
81 {
82    XGCValues  values;
83    XEvent     event;
84    Colormap   cmap;
85    XColor     gray, dgray, lgray, red;
86    XColor     graye, dgraye, lgraye, rede;
87    buttons_t  button;			/* counter */
88    binfo_t   *binfo = calloc (1, sizeof (binfo_t));
89 
90    if (!binfo)
91       error ("Out of memory.");
92 
93    binfo->width            = xinfo->ximage->width;
94    binfo->height           = buttons_height;
95    binfo->progbar_height   = progbar_height;
96    binfo->record_is_rewind = NO;
97 
98    /*
99     *  Generate sub-window for control panel
100     */
101    binfo->window = XCreateSimpleWindow (xinfo->display, xinfo->window,
102 					0, xinfo->ximage->height,
103 					binfo->width, binfo->height, 0,
104 					BlackPixel (xinfo->display,
105 						    xinfo->screen),
106 					WhitePixel (xinfo->display,
107 						    xinfo->screen));
108    XSelectInput(xinfo->display, binfo->window, StructureNotifyMask);
109    XMapWindow (xinfo->display, binfo->window);
110    do
111    {
112       XNextEvent (xinfo->display, &event);
113    }
114    while (event.type != MapNotify || event.xmap.event != binfo->window);
115    XSelectInput (xinfo->display, binfo->window, EVENT_MASK);
116 
117    /*
118     *  Generate graphic contexts for different colors.
119     */
120    cmap = DefaultColormap (xinfo->display, xinfo->screen);
121    XAllocNamedColor (xinfo->display, cmap, "#404040", &dgray, &dgraye);
122    XAllocNamedColor (xinfo->display, cmap, "white", &lgray, &lgraye);
123    XAllocNamedColor (xinfo->display, cmap, "#a8a8a8", &gray, &graye);
124    XAllocNamedColor (xinfo->display, cmap, "red", &red, &rede);
125 
126    values.foreground = BlackPixel (xinfo->display, xinfo->screen);
127    values.background = WhitePixel (xinfo->display, xinfo->screen);
128    binfo->gc [BLACK] = XCreateGC (xinfo->display,
129 				  RootWindow (xinfo->display, xinfo->screen),
130 				  (GCForeground | GCBackground), &values);
131    values.foreground = BlackPixel (xinfo->display, xinfo->screen);
132    values.background = WhitePixel (xinfo->display, xinfo->screen);
133    values.line_width = 3;
134    values.join_style = JoinRound;
135    binfo->gc [THICKBLACK] = XCreateGC (xinfo->display,
136 				       RootWindow (xinfo->display,
137 						   xinfo->screen),
138 				       (GCForeground | GCBackground
139 					| GCLineWidth | GCJoinStyle), &values);
140    values.foreground = gray.pixel;
141    values.background = WhitePixel (xinfo->display, xinfo->screen);
142    binfo->gc [NGRAY] = XCreateGC (xinfo->display,
143 				  RootWindow (xinfo->display, xinfo->screen),
144 				  (GCForeground | GCBackground), &values);
145    values.foreground = lgray.pixel;
146    values.background = WhitePixel (xinfo->display, xinfo->screen);
147    binfo->gc [LGRAY] = XCreateGC (xinfo->display,
148 				  RootWindow (xinfo->display, xinfo->screen),
149 				  (GCForeground | GCBackground), &values);
150    values.foreground = dgray.pixel;
151    values.background = WhitePixel (xinfo->display, xinfo->screen);
152    binfo->gc [DGRAY] = XCreateGC (xinfo->display,
153 				  RootWindow (xinfo->display, xinfo->screen),
154 				  (GCForeground | GCBackground), &values);
155    values.foreground = red.pixel;
156    values.background = WhitePixel (xinfo->display, xinfo->screen);
157    binfo->gc [RED]   = XCreateGC (xinfo->display,
158 				  RootWindow (xinfo->display, xinfo->screen),
159 				  (GCForeground | GCBackground), &values);
160 
161    for (button = 0; button < NO_BUTTON; button++)
162       binfo->pressed [button] = NO;
163 
164    draw_control_panel (xinfo, binfo, n, n_frames);
165 
166    return binfo;
167 }
168 
169 void
wait_for_input(x11_info_t * xinfo)170 wait_for_input (x11_info_t *xinfo)
171 /*
172  *  Wait for key press or mouse click in window 'xinfo'.
173  *  Redraw 'image' if event other then ButtonPress or KeyPress occurs.
174  *  Enlarge or reduce size of image by factor 2^'enlarge_factor'.
175  *
176  *  No return value.
177  *
178  *  Side effect:
179  *	program is terminated after key press or mouse click.
180  */
181 {
182    bool_t leave_loop = NO;
183 
184    XSelectInput (xinfo->display, xinfo->window, EVENT_MASK);
185 
186    while (!leave_loop)
187    {
188       XEvent event;
189 
190       XMaskEvent (xinfo->display, EVENT_MASK, &event);
191       switch (event.type)
192       {
193 	 case ButtonPress:
194 	 case KeyPress:
195 	    leave_loop = YES;
196 	    break;
197 	 default:
198 	    display_image (0, 0, xinfo);
199 	    break;
200       }
201    }
202 }
203 
204 void
check_events(x11_info_t * xinfo,binfo_t * binfo,unsigned n,unsigned n_frames)205 check_events (x11_info_t *xinfo, binfo_t *binfo, unsigned n, unsigned n_frames)
206 /*
207  *  Check the X11 event loop. If the PAUSE buttonin the of panel 'binfo'
208  *  is activated wait until next event occurs.
209  *  Redraw 'image' if event other then ButtonPress or ButtonRelease occurs.
210  *  Enlarge or reduce size of image by factor 2^'enlarge_factor'.
211  *  'n' gives the current frame, 'whereas' n_frames is the total number of
212  *  frames of the video stream.
213  *
214  *  No return values.
215  *
216  *  Side effects:
217  *	status of buttons (binfo->pressed [button]) is changed accordingly.
218  */
219 {
220    bool_t leave_eventloop;
221 
222    leave_eventloop = (!binfo->pressed [PAUSE_BUTTON]
223 		      && binfo->pressed [PLAY_BUTTON])
224 		     || (!binfo->pressed [PAUSE_BUTTON]
225 			 && binfo->record_is_rewind
226 			 && binfo->pressed [RECORD_BUTTON])
227 		     || binfo->pressed [RECORD_BUTTON];
228    draw_progress_bar (xinfo, binfo, n, n_frames);
229 
230    if (binfo->pressed [PAUSE_BUTTON] && binfo->pressed [PLAY_BUTTON])
231    {
232       XFlush (xinfo->display);
233       draw_button (xinfo, binfo, PLAY_BUTTON, NO); /* clear PLAY mode */
234       XFlush (xinfo->display);
235    }
236    if (binfo->pressed [PAUSE_BUTTON]
237        && binfo->record_is_rewind && binfo->pressed [RECORD_BUTTON])
238    {
239       XFlush (xinfo->display);
240       draw_button (xinfo, binfo, RECORD_BUTTON, NO); /* clear PLAY mode */
241       XFlush (xinfo->display);
242    }
243 
244    if (binfo->pressed [STOP_BUTTON])
245    {
246       XFlush (xinfo->display);
247       draw_button (xinfo, binfo, STOP_BUTTON, NO); /* clear STOP button */
248       XFlush (xinfo->display);
249    }
250 
251    do
252    {
253       XEvent event;
254       int    button;
255       bool_t wait_release = NO;
256 
257 
258       if (XCheckMaskEvent (xinfo->display, EVENT_MASK, &event))
259       {
260 	 switch (event.type)
261 	 {
262 	    case ButtonPress:
263 	       wait_release = NO;
264 	       if (!(binfo->pressed [RECORD_BUTTON] &&
265 		     !binfo->record_is_rewind))
266 		  for (button = 0; button < NO_BUTTON; button++)
267 		  {
268 		     int x0, y0, x1, y1; /* button coordinates */
269 
270 		     x0 = button * (binfo->width / NO_BUTTON);
271 		     y0 = binfo->progbar_height;
272 		     x1 = x0 + binfo->width / NO_BUTTON;
273 		     y1 = y0 + binfo->height - binfo->progbar_height - 1;
274 		     if (event.xbutton.x > x0 && event.xbutton.x < x1
275 			 && event.xbutton.y > y0 && event.xbutton.y < y1)
276 		     {
277 			draw_button (xinfo, binfo, button,
278 				     !binfo->pressed [button]);
279 			wait_release = YES;
280 			break;
281 		     }
282 		  }
283 	       break;
284 	    case ButtonRelease:
285 	       wait_release = NO;
286 	       break;
287 	    default:
288 	       wait_release = NO;
289 	       draw_control_panel (xinfo, binfo, n, n_frames);
290 	       display_image (0, 0, xinfo);
291 	       break;
292 	 }
293 	 leave_eventloop = !wait_release
294 			   && (binfo->pressed [PLAY_BUTTON]
295 			       || binfo->pressed [STOP_BUTTON]
296 			       || binfo->pressed [RECORD_BUTTON]
297 			       || binfo->pressed [QUIT_BUTTON]);
298       }
299    } while (!leave_eventloop);
300 
301    if ((binfo->pressed [RECORD_BUTTON] && !binfo->record_is_rewind)
302        && n == n_frames - 1)
303    {
304       binfo->record_is_rewind = YES;
305       draw_button (xinfo, binfo, RECORD_BUTTON, NO);
306    }
307 }
308 
309 /*****************************************************************************
310 
311 				private code
312 
313 *****************************************************************************/
314 
315 static void
draw_control_panel(x11_info_t * xinfo,binfo_t * binfo,unsigned n,unsigned n_frames)316 draw_control_panel (x11_info_t *xinfo, binfo_t *binfo,
317 		    unsigned n, unsigned n_frames)
318 /*
319  *  Draw control panel 'binfo' with all buttons and progressbar in
320  *  the given 'window'.
321  *  'n' gives the current frame, 'whereas' n_frames is the total number of
322  *  frames of the video stream.
323  *
324  *  No return value.
325  */
326 {
327    buttons_t button;
328 
329    XFillRectangle (xinfo->display, binfo->window, binfo->gc [NGRAY],
330 		   0, 0, binfo->width, binfo->height);
331    draw_progress_bar (xinfo, binfo, n, n_frames);
332    for (button = 0; button < NO_BUTTON; button++)
333       draw_button (xinfo, binfo, button, binfo->pressed [button]);
334 }
335 
336 static void
draw_progress_bar(x11_info_t * xinfo,binfo_t * binfo,unsigned n,unsigned n_frames)337 draw_progress_bar (x11_info_t *xinfo, binfo_t *binfo, unsigned n,
338 		   unsigned n_frames)
339 /*
340  *  Draw progressbar of control panel 'binfo' in the given 'window'.
341  *  'n' gives the current frame, whereas 'n_frames' is the total number of
342  *  frames of the video stream.
343  *
344  *  No return value.
345  */
346 {
347    unsigned x, y, width, height;
348 
349    x 	  = 2;
350    y 	  = 1;
351    width  = binfo->width - 5;
352    height = binfo->progbar_height - 3;
353 
354    XDrawLine (xinfo->display, binfo->window, binfo->gc [DGRAY],
355 	      x, y, x + width, y);
356    XDrawLine (xinfo->display, binfo->window, binfo->gc [DGRAY],
357 	      x, y, x, y + height - 1);
358    XDrawLine (xinfo->display, binfo->window, binfo->gc [LGRAY],
359 	      x + width, y + 1, x + width, y + height);
360    XDrawLine (xinfo->display, binfo->window, binfo->gc [LGRAY],
361 	      x, y + height, x + width, y + height);
362 
363    x++; y++; width  -= 2; height -= 2;
364    XFillRectangle (xinfo->display, binfo->window, binfo->gc [NGRAY],
365 		   x, y, width, height);
366 
367    XFillRectangle (xinfo->display, binfo->window, binfo->gc [BLACK],
368 		   x + n * max (1, width / n_frames), y,
369 		   max (1, width / n_frames), height);
370 }
371 
372 static void
draw_button(x11_info_t * xinfo,binfo_t * binfo,buttons_t button,bool_t pressed)373 draw_button (x11_info_t *xinfo, binfo_t *binfo,
374 	     buttons_t button, bool_t pressed)
375 /*
376  *  Draw 'button' of control panel 'binfo' in the given 'window'.
377  *  'pressed' indicates whether the button is pressed or not.
378  *
379  *  No return value.
380  */
381 {
382    grayscale_t top, bottom;		/* index of GC */
383    unsigned    x, y, width, height;	/* coordinates of button */
384 
385    x 	  = button * (binfo->width / NO_BUTTON);
386    y 	  = binfo->progbar_height;
387    width  = binfo->width / NO_BUTTON;
388    height = binfo->height - binfo->progbar_height - 1;
389 
390    if (width < 4 || height < 4)
391       return;
392 
393    if (pressed)
394    {
395       top    = DGRAY;
396       bottom = LGRAY;
397    }
398    else
399    {
400       top    = LGRAY;
401       bottom = DGRAY;
402    }
403 
404    x 	 += 2;
405    width -= 4;
406 
407    XDrawLine (xinfo->display, binfo->window, binfo->gc [top],
408 	      x, y, x + width, y);
409    XDrawLine (xinfo->display, binfo->window, binfo->gc [top],
410 	      x, y, x, y + height - 1);
411    XDrawLine (xinfo->display, binfo->window, binfo->gc [bottom],
412 	      x + width, y + 1, x + width, y + height);
413    XDrawLine (xinfo->display, binfo->window, binfo->gc [bottom],
414 	      x, y + height, x + width, y + height);
415 
416    x++; y++; width  -= 2; height -= 2;
417    XFillRectangle (xinfo->display, binfo->window, binfo->gc [NGRAY],
418 		   x, y, width, height);
419 
420    switch (button)
421    {
422       case STOP_BUTTON:
423 	 XFillRectangle (xinfo->display, binfo->window, binfo->gc [BLACK],
424 			 x + width / 2 - 6, y + height / 2 - 4, 11, 11);
425 	 if (pressed && !binfo->pressed [STOP_BUTTON])
426 	 {
427 	    draw_button (xinfo, binfo, PLAY_BUTTON, NO);
428 	    draw_button (xinfo, binfo, PAUSE_BUTTON, NO);
429 	    draw_button (xinfo, binfo, RECORD_BUTTON, NO);
430 	 }
431 	 break;
432       case PAUSE_BUTTON:
433 	 XFillRectangle (xinfo->display, binfo->window, binfo->gc [BLACK],
434 			 x + width / 2 - 6, y + height / 2 - 4, 5, 11);
435 	 XFillRectangle (xinfo->display, binfo->window, binfo->gc [BLACK],
436 			 x + width / 2 + 1, y + height / 2 - 4, 5, 11);
437 	 break;
438       case PLAY_BUTTON:
439 	 {
440 	    XPoint triangle [3];
441 
442 	    triangle [0].x = x + width / 2 - 5;
443 	    triangle [0].y = y + height / 2 - 5;
444 	    triangle [1].x = 10;
445 	    triangle [1].y = 6;
446 	    triangle [2].x = -10;
447 	    triangle [2].y = 6;
448 
449 	    XFillPolygon (xinfo->display, binfo->window, binfo->gc [BLACK],
450 			  triangle, 3, Convex, CoordModePrevious);
451 	    if (pressed && !binfo->pressed [PLAY_BUTTON]
452 		&& binfo->pressed [RECORD_BUTTON])
453 	       draw_button (xinfo, binfo, RECORD_BUTTON, NO);
454 	 }
455 	 break;
456       case RECORD_BUTTON:
457 	 if (!binfo->record_is_rewind)
458 	 {
459 	    XFillArc (xinfo->display, binfo->window, binfo->gc [RED],
460 		      x + width / 2 - 5, y + height / 2 - 5, 11, 11, 0,
461 		      360 * 64);
462 	    if (pressed && !binfo->pressed [RECORD_BUTTON])
463 	    {
464 	       draw_button (xinfo, binfo, STOP_BUTTON, YES);
465 	       draw_button (xinfo, binfo, PLAY_BUTTON, NO);
466 	       draw_button (xinfo, binfo, PAUSE_BUTTON, NO);
467 	    }
468 	 }
469 	 else
470 	 {
471 	    XPoint triangle [3];
472 
473 	    triangle [0].x = x + width / 2 + 5;
474 	    triangle [0].y = y + height / 2 - 5;
475 	    triangle [1].x = -10;
476 	    triangle [1].y = 6;
477 	    triangle [2].x = 10;
478 	    triangle [2].y = 6;
479 
480 	    XFillPolygon (xinfo->display, binfo->window, binfo->gc [BLACK],
481 			  triangle, 3, Convex, CoordModePrevious);
482 	    if (pressed && !binfo->pressed [RECORD_BUTTON]
483 		&& binfo->pressed [PLAY_BUTTON])
484 	       draw_button (xinfo, binfo, PLAY_BUTTON, NO);
485 	 }
486 	 break;
487       case QUIT_BUTTON:
488 	 {
489 	    XPoint triangle [3];
490 
491 	    triangle [0].x = x + width / 2 - 6;
492 	    triangle [0].y = y + height / 2 + 2;
493 	    triangle [1].x = 6;
494 	    triangle [1].y = -7;
495 	    triangle [2].x = 6;
496 	    triangle [2].y = 7;
497 
498 	    XFillPolygon (xinfo->display, binfo->window, binfo->gc [BLACK],
499 			  triangle, 3, Convex, CoordModePrevious);
500 	    XFillRectangle (xinfo->display, binfo->window, binfo->gc [BLACK],
501 			    x + width / 2 - 5, y + height / 2 + 4, 11, 3);
502 	 }
503 	 break;
504       default:
505 	 break;
506    }
507    binfo->pressed [button] = pressed;
508 }
509 
510 #endif /* not X_DISPLAY_MISSING */
511