1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a unix video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  *
21  */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <stdio.h>
27 #include <errno.h>
28 #include <sys/time.h>
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 #include <X11/keysym.h>
32 #include <pthread.h>
33 
34 #include <xine.h>
35 #include <xine/xineutils.h>
36 
37 #include "common.h"
38 
39 #define WINDOW_WIDTH    250
40 #define WINDOW_HEIGHT   221
41 
42 
43 typedef struct {
44   xitk_window_t        *xwin;
45 
46   int                   x;
47   int                   y;
48 
49   xitk_widget_list_t   *widget_list;
50 
51   struct {
52     xitk_widget_t      *left;
53     xitk_widget_t      *right;
54     xitk_widget_t      *up;
55     xitk_widget_t      *down;
56     xitk_widget_t      *select;
57   } navigator;
58 
59   struct {
60     xitk_widget_t      *menu[7];
61   } menus;
62 
63   struct {
64     xitk_widget_t      *prev;
65     xitk_widget_t      *next;
66   } angles;
67 
68   struct {
69     xitk_widget_t      *number[11];
70   } numbers;
71 
72 
73   int                   running;
74   int                   visible;
75   xitk_register_key_t   widget_key;
76 
77 } _eventer_t;
78 
79 static _eventer_t    *eventer = NULL;
80 
event_sender_sticky_cb(void * data,xine_cfg_entry_t * cfg)81 void event_sender_sticky_cb(void *data, xine_cfg_entry_t *cfg) {
82   gGui_t *gui = data;
83   int old_sticky_value = gui->eventer_sticky;
84 
85   gui->eventer_sticky = cfg->num_value;
86   if(eventer) {
87     if((!old_sticky_value) && gui->eventer_sticky) {
88       int  px, py, pw;
89       xitk_get_window_position (gui->display, gui->panel_window, &px, &py, &pw, NULL);
90       eventer->x = px + pw;
91       eventer->y = py;
92       xitk_window_move_window (gui->imlib_data, eventer->xwin, eventer->x, eventer->y);
93     }
94   }
95 }
96 
event_sender_store_new_position(void * data,int x,int y,int w,int h)97 static void event_sender_store_new_position (void *data, int x, int y, int w, int h) {
98 
99   if(eventer && !gGui->eventer_sticky) {
100     eventer->x = x;
101     eventer->y = y;
102     config_update_num ("gui.eventer_x", x);
103     config_update_num ("gui.eventer_y", y);
104   }
105 }
106 
event_sender_show_tips(int enabled,unsigned long timeout)107 void event_sender_show_tips(int enabled, unsigned long timeout) {
108 
109   if(eventer) {
110     if(enabled)
111       xitk_set_widgets_tips_timeout(eventer->widget_list, timeout);
112     else
113       xitk_disable_widgets_tips(eventer->widget_list);
114   }
115 }
116 
event_sender_update_tips_timeout(unsigned long timeout)117 void event_sender_update_tips_timeout(unsigned long timeout) {
118   if(eventer)
119     xitk_set_widgets_tips_timeout(eventer->widget_list, timeout);
120 }
121 
122 /* Send given event to xine engine */
event_sender_send(int event)123 void event_sender_send (int event) {
124   xine_event_t   xine_event;
125 
126   if (!event)
127     return;
128 
129   xine_event.type        = event;
130   xine_event.data_length = 0;
131   xine_event.data        = NULL;
132   xine_event.stream      = gGui->stream;
133   gettimeofday(&xine_event.tv, NULL);
134   xine_event_send (gGui->stream, &xine_event);
135 }
136 
event_sender_send2(xitk_widget_t * w,void * data)137 static void event_sender_send2 (xitk_widget_t *w, void *data) {
138   int event = (long int)data;
139   xine_event_t   xine_event;
140 
141   xine_event.type        = event;
142   xine_event.data_length = 0;
143   xine_event.data        = NULL;
144   xine_event.stream      = gGui->stream;
145   gettimeofday(&xine_event.tv, NULL);
146   xine_event_send (gGui->stream, &xine_event);
147 }
148 
149 static void event_sender_exit(xitk_widget_t *, void *);
150 
event_sender_handle_event(XEvent * event,void * data)151 static void event_sender_handle_event(XEvent *event, void *data) {
152   xine_event_t  xine_event;
153 
154   switch(event->type) {
155 
156   case ButtonRelease:
157     /*
158      * If we tried to move sticky window, move it back to stored position.
159      */
160     if(eventer && gGui->eventer_sticky) {
161       if (panel_is_visible (gGui->panel)) {
162 	int  x, y;
163 	xitk_window_get_window_position(gGui->imlib_data, eventer->xwin, &x, &y, NULL, NULL);
164 	if((x != eventer->x) || (y != eventer->y))
165 	  xitk_window_move_window(gGui->imlib_data, eventer->xwin, eventer->x, eventer->y);
166       }
167     }
168     return;
169 
170   case KeyPress:
171     {
172       XKeyEvent      mykeyevent;
173       KeySym         key;
174       char           kbuf[256];
175 
176       mykeyevent = event->xkey;
177 
178       gGui->x_lock_display (gGui->display);
179       XLookupString(&mykeyevent, kbuf, sizeof(kbuf), &key, NULL);
180       gGui->x_unlock_display (gGui->display);
181 
182       switch(key) {
183       case XK_Up:
184         xine_event.type = XINE_EVENT_INPUT_UP;
185 	break;
186       case XK_Down:
187         xine_event.type = XINE_EVENT_INPUT_DOWN;
188 	break;
189       case XK_Left:
190         xine_event.type = XINE_EVENT_INPUT_LEFT;
191 	break;
192       case XK_Right:
193         xine_event.type = XINE_EVENT_INPUT_RIGHT;
194 	break;
195 
196       case XK_Return:
197       case XK_KP_Enter:
198       case XK_ISO_Enter:
199         xine_event.type = XINE_EVENT_INPUT_SELECT;
200 	break;
201 
202       case XK_0:
203         xine_event.type = XINE_EVENT_INPUT_NUMBER_0;
204 	break;
205       case XK_1:
206         xine_event.type = XINE_EVENT_INPUT_NUMBER_1;
207 	break;
208       case XK_2:
209         xine_event.type = XINE_EVENT_INPUT_NUMBER_2;
210 	break;
211       case XK_3:
212         xine_event.type = XINE_EVENT_INPUT_NUMBER_3;
213 	break;
214       case XK_4:
215         xine_event.type = XINE_EVENT_INPUT_NUMBER_4;
216 	break;
217       case XK_5:
218         xine_event.type = XINE_EVENT_INPUT_NUMBER_5;
219 	break;
220       case XK_6:
221         xine_event.type = XINE_EVENT_INPUT_NUMBER_6;
222 	break;
223       case XK_7:
224         xine_event.type = XINE_EVENT_INPUT_NUMBER_7;
225 	break;
226       case XK_8:
227         xine_event.type = XINE_EVENT_INPUT_NUMBER_8;
228 	break;
229       case XK_9:
230         xine_event.type = XINE_EVENT_INPUT_NUMBER_9;
231 	break;
232 
233       case XK_plus:
234       case XK_KP_Add:
235         xine_event.type = XINE_EVENT_INPUT_NUMBER_10_ADD;
236 	break;
237 
238       case XK_Escape:
239 	event_sender_exit(NULL, NULL);
240 	return;
241 
242       default:
243         gui_handle_event (event, gGui);
244 	return;
245       }
246     }
247     break;
248 
249     default:
250       return;
251   }
252 
253   xine_event.data_length = 0;
254   xine_event.data        = NULL;
255   xine_event.stream      = gGui->stream;
256   gettimeofday(&xine_event.tv, NULL);
257   xine_event_send (gGui->stream, &xine_event);
258 }
259 
event_sender_update_menu_buttons(void)260 void event_sender_update_menu_buttons(void) {
261 
262   if(eventer) {
263     if((!strncmp(gGui->mmk.mrl, "dvd:/", 5)) || (!strncmp(gGui->mmk.mrl, "dvdnav:/", 8))) {
264       xitk_labelbutton_change_label(eventer->menus.menu[0], _("Menu toggle"));
265       xitk_labelbutton_change_label(eventer->menus.menu[1], _("Title"));
266       xitk_labelbutton_change_label(eventer->menus.menu[2], _("Root"));
267       xitk_labelbutton_change_label(eventer->menus.menu[3], _("Subpicture"));
268       xitk_labelbutton_change_label(eventer->menus.menu[4], _("Audio"));
269       xitk_labelbutton_change_label(eventer->menus.menu[5], _("Angle"));
270       xitk_labelbutton_change_label(eventer->menus.menu[6], _("Part"));
271     }
272     else if(!strncmp(gGui->mmk.mrl, "bd:/", 4)) {
273       xitk_labelbutton_change_label(eventer->menus.menu[0], _("Top Menu"));
274       xitk_labelbutton_change_label(eventer->menus.menu[1], _("Popup Menu"));
275       xitk_labelbutton_change_label(eventer->menus.menu[2], _("Menu 3"));
276       xitk_labelbutton_change_label(eventer->menus.menu[3], _("Menu 4"));
277       xitk_labelbutton_change_label(eventer->menus.menu[4], _("Menu 5"));
278       xitk_labelbutton_change_label(eventer->menus.menu[5], _("Menu 6"));
279       xitk_labelbutton_change_label(eventer->menus.menu[6], _("Menu 7"));
280     } else {
281       xitk_labelbutton_change_label(eventer->menus.menu[0], _("Menu 1"));
282       xitk_labelbutton_change_label(eventer->menus.menu[1], _("Menu 2"));
283       xitk_labelbutton_change_label(eventer->menus.menu[2], _("Menu 3"));
284       xitk_labelbutton_change_label(eventer->menus.menu[3], _("Menu 4"));
285       xitk_labelbutton_change_label(eventer->menus.menu[4], _("Menu 5"));
286       xitk_labelbutton_change_label(eventer->menus.menu[5], _("Menu 6"));
287       xitk_labelbutton_change_label(eventer->menus.menu[6], _("Menu 7"));
288     }
289   }
290 }
291 
event_sender_exit(xitk_widget_t * w,void * data)292 static void event_sender_exit(xitk_widget_t *w, void *data) {
293 
294   if(eventer) {
295     window_info_t wi;
296 
297     eventer->running = 0;
298     eventer->visible = 0;
299 
300     if((xitk_get_window_info(eventer->widget_key, &wi))) {
301       config_update_num ("gui.eventer_x", wi.x);
302       config_update_num ("gui.eventer_y", wi.y);
303       WINDOW_INFO_ZERO(&wi);
304     }
305 
306     xitk_unregister_event_handler(&eventer->widget_key);
307 
308     xitk_destroy_widgets(eventer->widget_list);
309     xitk_window_destroy_window(gGui->imlib_data, eventer->xwin);
310 
311     eventer->xwin = NULL;
312     /* xitk_dlist_init (&eventer->widget_list->list); */
313 
314     gGui->x_lock_display (gGui->display);
315     XFreeGC(gGui->display, (XITK_WIDGET_LIST_GC(eventer->widget_list)));
316     gGui->x_unlock_display (gGui->display);
317 
318     XITK_WIDGET_LIST_FREE(eventer->widget_list);
319 
320     free(eventer);
321     eventer = NULL;
322 
323     try_to_set_input_focus(gGui->video_window);
324   }
325 }
326 
event_sender_is_visible(void)327 int event_sender_is_visible(void) {
328 
329   if(eventer != NULL) {
330     if(gGui->use_root_window)
331       return xitk_is_window_visible(gGui->display, xitk_window_get_window(eventer->xwin));
332     else
333       return eventer->visible && xitk_is_window_visible(gGui->display, xitk_window_get_window(eventer->xwin));
334   }
335 
336   return 0;
337 }
338 
event_sender_is_running(void)339 int event_sender_is_running(void) {
340 
341   if(eventer != NULL)
342     return eventer->running;
343 
344   return 0;
345 }
346 
event_sender_raise_window(void)347 void event_sender_raise_window(void) {
348   if(eventer != NULL)
349     raise_window(xitk_window_get_window(eventer->xwin), eventer->visible, eventer->running);
350 }
351 
event_sender_toggle_visibility(xitk_widget_t * w,void * data)352 void event_sender_toggle_visibility(xitk_widget_t *w, void *data) {
353   if(eventer != NULL)
354     toggle_window(xitk_window_get_window(eventer->xwin), eventer->widget_list,
355 		  &eventer->visible, eventer->running);
356 }
357 
event_sender_move(int x,int y)358 void event_sender_move(int x, int y) {
359 
360   if(eventer != NULL) {
361     if(gGui->eventer_sticky) {
362       eventer->x = x;
363       eventer->y = y;
364       config_update_num ("gui.eventer_x", x);
365       config_update_num ("gui.eventer_y", y);
366       xitk_window_move_window(gGui->imlib_data, eventer->xwin, x, y);
367     }
368   }
369 }
370 
event_sender_end(void)371 void event_sender_end(void) {
372   event_sender_exit(NULL, NULL);
373 }
374 
event_sender_reparent(void)375 void event_sender_reparent(void) {
376   if(eventer)
377     reparent_window((xitk_window_get_window(eventer->xwin)));
378 }
379 
event_sender_panel(void)380 void event_sender_panel(void) {
381   GC                         gc;
382   xitk_labelbutton_widget_t  lb;
383   int                        x, y, i;
384   xitk_widget_t             *w;
385 
386   eventer = (_eventer_t *) calloc(1, sizeof(_eventer_t));
387 
388   eventer->x = xine_config_register_num (__xineui_global_xine_instance, "gui.eventer_x",
389 					 80,
390 					 CONFIG_NO_DESC,
391 					 CONFIG_NO_HELP,
392 					 CONFIG_LEVEL_DEB,
393 					 CONFIG_NO_CB,
394 					 CONFIG_NO_DATA);
395   eventer->y = xine_config_register_num (__xineui_global_xine_instance, "gui.eventer_y",
396 					 80,
397 					 CONFIG_NO_DESC,
398 					 CONFIG_NO_HELP,
399 					 CONFIG_LEVEL_DEB,
400 					 CONFIG_NO_CB,
401 					 CONFIG_NO_DATA);
402 
403   if (gGui->eventer_sticky && panel_is_visible (gGui->panel)) {
404     int  px, py, pw;
405     xitk_get_window_position(gGui->display, gGui->panel_window, &px, &py, &pw, NULL);
406     eventer->x = px + pw;
407     eventer->y = py;
408   }
409 
410   /* Create window */
411   eventer->xwin = xitk_window_create_simple_window(gGui->imlib_data, eventer->x, eventer->y, WINDOW_WIDTH, WINDOW_HEIGHT);
412 
413   set_window_states_start((xitk_window_get_window(eventer->xwin)));
414 
415   gGui->x_lock_display (gGui->display);
416   XStoreName(gGui->display, (xitk_window_get_window(eventer->xwin)), _("xine Event Sender"));
417   gc = XCreateGC(gGui->display,
418 		 (xitk_window_get_window(eventer->xwin)), None, None);
419   gGui->x_unlock_display (gGui->display);
420 
421   eventer->widget_list = xitk_widget_list_new();
422   xitk_widget_list_set(eventer->widget_list,
423 		       WIDGET_LIST_WINDOW, (void *) (xitk_window_get_window(eventer->xwin)));
424   xitk_widget_list_set(eventer->widget_list, WIDGET_LIST_GC, gc);
425 
426   XITK_WIDGET_INIT(&lb, gGui->imlib_data);
427 
428   lb.button_type       = CLICK_BUTTON;
429   lb.align             = ALIGN_CENTER;
430   lb.callback          = event_sender_send2;
431   lb.state_callback    = NULL;
432   lb.skin_element_name = NULL;
433 
434 #define I2PTR(n) (void *)((long int)(n))
435 
436   x = 80 + 5 + 5;
437   y = 5 + 23 * 3 + 5 - 40;
438 
439   lb.label             = _("Up");
440   lb.userdata          = I2PTR (XINE_EVENT_INPUT_UP);
441   eventer->navigator.up = xitk_noskin_labelbutton_create (eventer->widget_list,
442     &lb, x, y, 70, 40, "Black", "Black", "White", hboldfontname);
443   xitk_add_widget (eventer->widget_list, eventer->navigator.up);
444 
445   xitk_enable_and_show_widget(eventer->navigator.up);
446 
447   x -= 70;
448   y += 40;
449 
450   lb.label             = _("Left");
451   lb.userdata          = I2PTR (XINE_EVENT_INPUT_LEFT);
452   eventer->navigator.left = xitk_noskin_labelbutton_create (eventer->widget_list,
453     &lb, x, y, 70, 40, "Black", "Black", "White", hboldfontname);
454   xitk_add_widget (eventer->widget_list, eventer->navigator.left);
455 
456   xitk_enable_and_show_widget(eventer->navigator.left);
457 
458   x += 75;
459   y += 5;
460 
461   lb.label             = _("Select");
462   lb.userdata          = I2PTR (XINE_EVENT_INPUT_SELECT);
463   eventer->navigator.select = xitk_noskin_labelbutton_create (eventer->widget_list,
464     &lb, x, y, 60, 30, "Black", "Black", "White", hboldfontname);
465   xitk_add_widget (eventer->widget_list, eventer->navigator.select);
466 
467   xitk_enable_and_show_widget(eventer->navigator.select);
468 
469   x += 65;
470   y -= 5;
471 
472   lb.label             = _("Right");
473   lb.userdata          = I2PTR (XINE_EVENT_INPUT_RIGHT);
474   eventer->navigator.right = xitk_noskin_labelbutton_create (eventer->widget_list,
475     &lb, x, y, 70, 40, "Black", "Black", "White", hboldfontname);
476   xitk_add_widget (eventer->widget_list, eventer->navigator.right);
477 
478   xitk_enable_and_show_widget(eventer->navigator.right);
479 
480   x -= 70;
481   y += 40;
482 
483   lb.label             = _("Down");
484   lb.userdata          = I2PTR (XINE_EVENT_INPUT_DOWN);
485   eventer->navigator.down = xitk_noskin_labelbutton_create (eventer->widget_list,
486     &lb, x, y, 70, 40, "Black", "Black", "White", hboldfontname);
487   xitk_add_widget (eventer->widget_list, eventer->navigator.down);
488 
489   xitk_enable_and_show_widget(eventer->navigator.down);
490 
491   x = 23 * 2 + 5 + (80 - 23 * 3) / 2 + 1; /* (+1 to round up) */
492   y = 5 + 23 * 3 + 5 + 40 + 5;
493 
494   for(i = 9; i >= 0; i--) {
495     char number[3];
496 
497     snprintf(number, sizeof(number), "%d", i);
498 
499     lb.label             = number;
500     lb.userdata          = I2PTR (XINE_EVENT_INPUT_NUMBER_0 + i);
501 
502     if(!i)
503       x -= 46;
504 
505     eventer->numbers.number[i] = xitk_noskin_labelbutton_create (eventer->widget_list,
506       &lb, x, y, 23, 23, "Black", "Black", "White", hboldfontname);
507     xitk_add_widget (eventer->widget_list, eventer->numbers.number[i]);
508 
509     xitk_enable_and_show_widget(eventer->numbers.number[i]);
510 
511     if(!((i - 1) % 3)) {
512       y += 23;
513       x += 46;
514     }
515     else
516       x -= 23;
517 
518   }
519 
520   x += 46;
521 
522   {
523     char number[5];
524 
525     i = 10;
526 
527     snprintf(number, sizeof(number), "+ %d", i);
528 
529     lb.label             = number;
530     lb.userdata          = I2PTR (XINE_EVENT_INPUT_NUMBER_10_ADD);
531 
532     eventer->numbers.number[i] = xitk_noskin_labelbutton_create (eventer->widget_list,
533       &lb, x, y, 46, 23, "Black", "Black", "White", hboldfontname);
534     xitk_add_widget (eventer->widget_list, eventer->numbers.number[i]);
535 
536     xitk_enable_and_show_widget(eventer->numbers.number[i]);
537   }
538 
539   x = WINDOW_WIDTH - 80 - 5;
540   y = 5 + 23 * 3 + 5 + 40 + 5;
541 
542   lb.label             = _("Angle +");
543   lb.userdata          = I2PTR (XINE_EVENT_INPUT_ANGLE_NEXT);
544   eventer->angles.next = xitk_noskin_labelbutton_create (eventer->widget_list,
545     &lb, x, y, 80, 23, "Black", "Black", "White", hboldfontname);
546   xitk_add_widget (eventer->widget_list, eventer->angles.next);
547 
548   xitk_enable_and_show_widget(eventer->angles.next);
549 
550   y += 23;
551 
552   lb.label             = _("Angle -");
553   lb.userdata          = I2PTR (XINE_EVENT_INPUT_ANGLE_PREVIOUS);
554   eventer->angles.prev = xitk_noskin_labelbutton_create (eventer->widget_list,
555     &lb, x, y, 80, 23, "Black", "Black", "White", hboldfontname);
556   xitk_add_widget (eventer->widget_list, eventer->angles.prev);
557 
558   xitk_enable_and_show_widget(eventer->angles.prev);
559 
560 
561   x = 5;
562   y = 5;
563 
564   for(i = 0; i < 7; i++) {
565     lb.label             = "";
566     lb.userdata          = I2PTR (XINE_EVENT_INPUT_MENU1 + i);
567     eventer->menus.menu[i] = xitk_noskin_labelbutton_create (eventer->widget_list,
568       &lb, x, y, 80, 23, "Black", "Black", "White", hboldfontname);
569     xitk_add_widget (eventer->widget_list, eventer->menus.menu[i]);
570 
571     xitk_enable_and_show_widget(eventer->menus.menu[i]);
572 
573     if(i == 2) {
574       x += 80;
575       y -= (23 * 3);
576     }
577     if(i == 3) {
578       x += 80;
579       y -= 23;
580     }
581 
582     y += 23;
583   }
584   event_sender_update_menu_buttons();
585 
586   x = WINDOW_WIDTH - 70 - 5;
587   y = WINDOW_HEIGHT - 23 - 5;
588 
589   lb.label             = _("Close");
590   lb.callback          = event_sender_exit;
591   lb.userdata          = NULL;
592   w = xitk_noskin_labelbutton_create (eventer->widget_list,
593     &lb, x, y, 70, 23, "Black", "Black", "White", hboldfontname);
594   xitk_add_widget (eventer->widget_list, w);
595 
596   xitk_enable_and_show_widget(w);
597   event_sender_show_tips (panel_get_tips_enable (gGui->panel), panel_get_tips_timeout (gGui->panel));
598 
599   eventer->widget_key = xitk_register_event_handler("eventer",
600 						    (xitk_window_get_window(eventer->xwin)),
601 						    event_sender_handle_event,
602 						    event_sender_store_new_position,
603 						    NULL,
604 						    eventer->widget_list,
605 						    NULL);
606 
607 
608   eventer->visible = 1;
609   eventer->running = 1;
610   event_sender_raise_window();
611 
612   try_to_set_input_focus(xitk_window_get_window(eventer->xwin));
613 }
614