1 /*****************************************************************
2  * gmerlin - a general purpose multimedia framework and applications
3  *
4  * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5  * gmerlin-general@lists.sourceforge.net
6  * http://gmerlin.sourceforge.net
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  * *****************************************************************/
21 
22 #include <string.h>
23 
24 #include <config.h>
25 #include <gmerlin/translation.h>
26 #include <gmerlin/pluginregistry.h>
27 #include <gmerlin/cfg_dialog.h>
28 
29 #include <gtk/gtk.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <gui_gtk/aboutwindow.h>
32 #include <gui_gtk/gtkutils.h>
33 #include <gui_gtk/audio.h>
34 #include <gui_gtk/plugin.h>
35 #include <gui_gtk/logwindow.h>
36 #include <gmerlin/utils.h>
37 #include <gmerlin/visualize.h>
38 
39 #include <gdk/gdkx.h>
40 
41 #include <gmerlin/log.h>
42 #define LOG_DOMAIN "gmerlin_visualizer"
43 
44 #define TOOLBAR_TRIGGER_KEY   (1<<0)
45 #define TOOLBAR_TRIGGER_MOUSE (1<<1)
46 
47 #define TOOLBAR_LOCATION_TOP    0
48 #define TOOLBAR_LOCATION_BOTTOM 1
49 
50 extern void
51 gtk_decorated_window_move_resize_window(GtkWindow*, int, int, int, int);
52 
53 
54 typedef struct
55   {
56   GtkWidget * window;
57   GtkWidget * box;
58   GtkWidget * socket;
59   } window_t;
60 
61 typedef struct
62   {
63   GtkWidget * window;
64   bg_gtk_plugin_widget_single_t * ra_plugins;
65   bg_gtk_plugin_widget_single_t * ov_plugins;
66   GtkWidget * close_button;
67   } plugin_window_t;
68 
69 typedef struct
70   {
71   GtkWidget * config_button;
72   GtkWidget * quit_button;
73   GtkWidget * plugin_button;
74   GtkWidget * restart_button;
75   GtkWidget * log_button;
76   GtkWidget * about_button;
77   GtkWidget * help_button;
78 
79   guint log_id;
80   guint about_id;
81 
82   GtkWidget * fullscreen_button;
83   GtkWidget * nofullscreen_button;
84 
85   GtkWidget * toolbar; /* A GtkEventBox actually... */
86   GtkWidget * fps;
87 
88   bg_gtk_plugin_widget_single_t * vis_plugins;
89 
90   /* Windows are created by gtk and the x11 plugin
91      is embedded inside after */
92 
93   window_t normal_window;
94   window_t fullscreen_window;
95 
96   window_t *current_window;
97 
98   plugin_window_t plugin_window;
99 
100   bg_gtk_vumeter_t * vumeter;
101 
102   /* Core stuff */
103 
104   gavl_audio_frame_t * audio_frame;
105   gavl_audio_format_t audio_format;
106 
107   bg_visualizer_t * visualizer;
108 
109   bg_plugin_registry_t * plugin_reg;
110   bg_cfg_registry_t * cfg_reg;
111 
112   bg_plugin_handle_t * ra_handle;
113   bg_recorder_plugin_t * ra_plugin;
114 
115   const bg_plugin_info_t * ov_info;
116   const bg_plugin_info_t * ra_info;
117   /* Config stuff */
118 
119   //  bg_cfg_section_t * vumeter_section;
120   bg_cfg_section_t * visualizer_section;
121   bg_cfg_section_t * general_section;
122   bg_cfg_section_t * log_section;
123   bg_dialog_t * cfg_dialog;
124 
125   int audio_open;
126   int vis_open;
127 
128   int mouse_in_toolbar;
129   int do_hide_toolbar;
130   int toolbar_visible;
131 
132   int toolbar_location;
133   int toolbar_trigger;
134 
135   int x, y, width, height;
136 
137   bg_gtk_log_window_t * log_window;
138 
139   bg_ov_callbacks_t cb;
140   } visualizer_t;
141 
configure_callback(GtkWidget * w,GdkEventConfigure * event,gpointer data)142 static gboolean configure_callback(GtkWidget * w, GdkEventConfigure *event,
143                                    gpointer data)
144   {
145   visualizer_t * win;
146 
147   win = (visualizer_t*)data;
148   win->x = event->x;
149   win->y = event->y;
150   win->width = event->width;
151   win->height = event->height;
152   gdk_window_get_root_origin(win->current_window->window->window,
153                              &win->x, &win->y);
154   return FALSE;
155   }
156 
157 
get_display_string(visualizer_t * v)158 static char * get_display_string(visualizer_t * v)
159   {
160   char * ret;
161   GdkDisplay * dpy;
162   /* Get the display string */
163 
164   gtk_widget_realize(v->normal_window.socket);
165   gtk_widget_realize(v->fullscreen_window.socket);
166   dpy = gdk_display_get_default();
167 
168   //  ret = bg_sprintf("%s:%08lx:%08lx", gdk_display_get_name(dpy),
169   //                   GDK_WINDOW_XID(v->normal_window.window->window),
170   //                   GDK_WINDOW_XID(v->fullscreen_window.window->window));
171 
172   ret =
173     bg_sprintf("%s:%08lx:%08lx", gdk_display_get_name(dpy),
174                (long unsigned int)gtk_socket_get_id(GTK_SOCKET(v->normal_window.socket)),
175                (long unsigned int)gtk_socket_get_id(GTK_SOCKET(v->fullscreen_window.socket)));
176 
177   return ret;
178   }
179 
hide_toolbar(visualizer_t * v)180 static void hide_toolbar(visualizer_t * v)
181   {
182   gtk_widget_hide(v->toolbar);
183   v->toolbar_visible = 0;
184   }
185 
toolbar_timeout(void * data)186 static gboolean toolbar_timeout(void * data)
187   {
188   visualizer_t * v;
189   v = (visualizer_t *)data;
190   if(!v->toolbar_visible)
191     return TRUE;
192 
193   /* Maybe the toolbar will be hidden next time */
194   if(!v->do_hide_toolbar)
195     {
196     v->do_hide_toolbar = 1;
197     return TRUE;
198     }
199 
200   if(!v->mouse_in_toolbar)
201     {
202     hide_toolbar(v);
203     }
204   return TRUE;
205   }
206 
fps_timeout(void * data)207 static gboolean fps_timeout(void * data)
208   {
209   float fps;
210   char * tmp_string;
211   visualizer_t * v;
212   v = (visualizer_t *)data;
213 
214   if(!v->toolbar_visible || !v->audio_open)
215     return TRUE;
216 
217   fps = bg_visualizer_get_fps(v->visualizer);
218   if(fps >= 0.0)
219     {
220     tmp_string = bg_sprintf("Fps: %.2f", fps);
221     gtk_label_set_text(GTK_LABEL(v->fps), tmp_string);
222     free(tmp_string);
223     }
224 
225   return TRUE;
226   }
227 
show_toolbar(visualizer_t * v)228 static void show_toolbar(visualizer_t * v)
229   {
230   if(!v->toolbar_visible)
231     {
232     gtk_widget_show(v->toolbar);
233     v->toolbar_visible = 1;
234     }
235   v->do_hide_toolbar = 0;
236   }
237 
attach_toolbar(visualizer_t * v,window_t * win)238 static void attach_toolbar(visualizer_t * v, window_t * win)
239   {
240   if(v->toolbar_location == TOOLBAR_LOCATION_TOP)
241     gtk_box_pack_start(GTK_BOX(win->box), v->toolbar,
242                        FALSE, FALSE, 0);
243   else
244     gtk_box_pack_end(GTK_BOX(win->box), v->toolbar,
245                      FALSE, FALSE, 0);
246 
247 
248   }
249 
toggle_fullscreen(visualizer_t * v)250 static void toggle_fullscreen(visualizer_t * v)
251   {
252   if(v->current_window == &v->normal_window)
253     {
254     /* Reparent toolbar */
255     gtk_container_remove(GTK_CONTAINER(v->normal_window.box),
256                          v->toolbar);
257     attach_toolbar(v, &v->fullscreen_window);
258     /* Hide normal window, show fullscreen window */
259     gtk_widget_show(v->fullscreen_window.window);
260     gtk_widget_hide(v->normal_window.window);
261 
262     gtk_window_fullscreen(GTK_WINDOW(v->fullscreen_window.window));
263     /* Update toolbar */
264     gtk_widget_show(v->nofullscreen_button);
265     gtk_widget_hide(v->fullscreen_button);
266 
267     gtk_widget_hide(v->config_button);
268     gtk_widget_hide(v->plugin_button);
269 
270     bg_gtk_plugin_widget_single_show_buttons(v->vis_plugins, 0);
271 
272     v->current_window = &v->fullscreen_window;
273     }
274   else
275     {
276     /* Reparent toolbar */
277     gtk_container_remove(GTK_CONTAINER(v->fullscreen_window.box),
278                          v->toolbar);
279     attach_toolbar(v, &v->normal_window);
280 
281     /* Hide normal window, show fullscreen window */
282     gtk_widget_show(v->normal_window.window);
283     gtk_widget_hide(v->fullscreen_window.window);
284 
285     /* Update toolbar */
286     gtk_widget_show(v->fullscreen_button);
287     gtk_widget_hide(v->nofullscreen_button);
288 
289     gtk_widget_show(v->config_button);
290     gtk_widget_show(v->plugin_button);
291     bg_gtk_plugin_widget_single_show_buttons(v->vis_plugins, 1);
292 
293     v->current_window = &v->normal_window;
294     }
295   hide_toolbar(v);
296   v->mouse_in_toolbar = 0;
297   }
298 
open_audio(visualizer_t * v)299 static void open_audio(visualizer_t * v)
300   {
301   int i;
302   int was_open;
303   gavl_time_t delay_time = GAVL_TIME_SCALE / 20; /* 50 ms */
304   memset(&v->audio_format, 0, sizeof(v->audio_format));
305   v->audio_format.num_channels = 2;
306   v->audio_format.samplerate = 44100;
307   v->audio_format.sample_format = GAVL_SAMPLE_S16;
308   gavl_set_channel_setup(&v->audio_format);
309 
310   if(v->audio_frame)
311     {
312     gavl_audio_frame_destroy(v->audio_frame);
313     v->ra_plugin->close(v->ra_handle->priv);
314     v->audio_frame = NULL;
315     bg_plugin_unref(v->ra_handle);
316     was_open = 1;
317     }
318   else
319     was_open = 0;
320 
321   v->audio_open = 0;
322 
323   v->ra_handle = bg_plugin_load(v->plugin_reg,
324                                 v->ra_info);
325   v->ra_plugin = (bg_recorder_plugin_t*)(v->ra_handle->plugin);
326 
327   /* The soundcard might be busy from last time,
328      give the kernel some time to free the device */
329 
330   if(!v->ra_plugin->open(v->ra_handle->priv, &v->audio_format, NULL))
331     {
332     if(!was_open)
333       {
334       bg_log(BG_LOG_ERROR, LOG_DOMAIN,
335              "Opening audio device failed, fix settings and click restart");
336       gtk_label_set_text(GTK_LABEL(v->fps), TR("No audio"));
337       return;
338       }
339     for(i = 0; i < 20; i++)
340       {
341       gavl_time_delay(&delay_time);
342 
343       if(v->ra_plugin->open(v->ra_handle->priv, &v->audio_format, NULL))
344         {
345         v->audio_open = 1;
346         break;
347         }
348       }
349     }
350   else
351     v->audio_open = 1;
352 
353   if(v->audio_open)
354     {
355     v->audio_frame = gavl_audio_frame_create(&v->audio_format);
356     bg_gtk_vumeter_set_format(v->vumeter, &v->audio_format);
357     }
358   else
359     {
360     bg_log(BG_LOG_ERROR, LOG_DOMAIN,
361            "Opening audio device failed, fix settings and click restart");
362     gtk_label_set_text(GTK_LABEL(v->fps), TR("No audio"));
363     }
364   }
365 
open_vis(visualizer_t * v)366 static void open_vis(visualizer_t * v)
367   {
368   char * display_string = get_display_string(v);
369   bg_visualizer_open_id(v->visualizer, &v->audio_format,
370                         v->ov_info, display_string);
371   free(display_string);
372 
373 
374   v->vis_open = 1;
375   }
376 
close_vis(visualizer_t * v)377 static void close_vis(visualizer_t * v)
378   {
379   bg_visualizer_close(v->visualizer);
380   v->vis_open = 0;
381   }
382 
grab_notify_callback(GtkWidget * widget,gboolean was_grabbed,gpointer data)383 static void grab_notify_callback(GtkWidget *widget,
384                                  gboolean   was_grabbed,
385                                  gpointer   data)
386   {
387   visualizer_t * win = (visualizer_t*)data;
388   if(!was_grabbed)
389     {
390     bg_gtk_widget_set_can_focus(win->current_window->socket, TRUE);
391     gtk_widget_grab_focus(win->current_window->socket);
392     }
393   }
394 
about_window_close_callback(bg_gtk_about_window_t * win,void * data)395 static void about_window_close_callback(bg_gtk_about_window_t* win, void* data)
396   {
397   visualizer_t * v;
398 
399   v = (visualizer_t*)data;
400   gtk_widget_set_sensitive(v->about_button, 1);
401   }
402 
button_callback(GtkWidget * w,gpointer data)403 static void button_callback(GtkWidget * w, gpointer data)
404   {
405   visualizer_t * win = (visualizer_t*)data;
406 
407   if((w == win->quit_button) ||
408      (w == win->normal_window.window))
409     gtk_main_quit();
410   else if(w == win->config_button)
411     bg_dialog_show(win->cfg_dialog, win->current_window->window);
412   else if((w == win->fullscreen_button) ||
413           (w == win->nofullscreen_button))
414     toggle_fullscreen(win);
415   else if(w == win->plugin_button)
416     {
417     gtk_widget_show(win->plugin_window.window);
418     gtk_widget_set_sensitive(win->plugin_button, 0);
419     }
420   else if(w == win->restart_button)
421     {
422     if(win->vis_open)
423       {
424       close_vis(win);
425       open_audio(win);
426       open_vis(win);
427       hide_toolbar(win);
428       }
429     }
430   else if(w == win->log_button)
431     {
432     gtk_widget_set_sensitive(win->log_button, 0);
433     bg_gtk_log_window_show(win->log_window);
434     }
435   else if(w == win->about_button)
436     {
437     gtk_widget_set_sensitive(win->about_button, 0);
438     bg_gtk_about_window_create("Gmerlin visualizer", VERSION,
439                                "visualizer_icon.png",
440                                about_window_close_callback,
441                                win);
442     }
443   else if(w == win->help_button)
444     bg_display_html_help("userguide/Visualizer.html");
445   }
446 
plug_removed_callback(GtkWidget * w,gpointer data)447 static gboolean plug_removed_callback(GtkWidget * w, gpointer data)
448   {
449   /* Reuse socket */
450   return TRUE;
451   }
452 
plug_added_callback(GtkWidget * w,gpointer data)453 static void plug_added_callback(GtkWidget * w, gpointer data)
454   {
455   visualizer_t * v;
456   v = (visualizer_t *)data;
457   gtk_widget_hide(v->toolbar);
458 
459   /* Seems that this is switched off, when an earlier client exited */
460   bg_gtk_widget_set_can_focus(w, TRUE);
461   gtk_widget_grab_focus(w);
462   }
463 
464 static gboolean
delete_callback(GtkWidget * w,GdkEventAny * evt,gpointer data)465 delete_callback(GtkWidget * w,
466                 GdkEventAny * evt, gpointer data)
467   {
468   button_callback(w, data);
469   return TRUE;
470   }
471 
472 
plugin_window_button_callback(GtkWidget * w,gpointer data)473 static void plugin_window_button_callback(GtkWidget * w, gpointer data)
474   {
475   visualizer_t * v;
476   v = (visualizer_t *)data;
477   gtk_widget_set_sensitive(v->plugin_button, 1);
478   gtk_widget_hide(v->plugin_window.window);
479   }
480 
plugin_window_delete_callback(GtkWidget * w,GdkEventAny * evt,gpointer data)481 static gboolean plugin_window_delete_callback(GtkWidget * w,
482                                           GdkEventAny * evt,
483                                           gpointer data)
484   {
485   plugin_window_button_callback(w, data);
486   return TRUE;
487   }
488 
set_ra_plugin(const bg_plugin_info_t * plugin,void * data)489 static void set_ra_plugin(const bg_plugin_info_t * plugin,
490                           void * data)
491   {
492   visualizer_t * v = (visualizer_t*)data;
493   bg_plugin_registry_set_default(v->plugin_reg, BG_PLUGIN_RECORDER_AUDIO, BG_PLUGIN_RECORDER,
494                                  plugin->name);
495 
496   v->ra_info = plugin;
497   bg_log(BG_LOG_INFO, LOG_DOMAIN,
498          "Changed recording plugin to %s", v->ra_info->long_name);
499   close_vis(v);
500   open_audio(v);
501   open_vis(v);
502   }
503 
504 
plugin_window_init(plugin_window_t * win,visualizer_t * v)505 static void plugin_window_init(plugin_window_t * win, visualizer_t * v)
506   {
507   int row = 0, num_columns = 4;
508   GtkWidget * table;
509   win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
510   gtk_window_set_modal(GTK_WINDOW(win->window), TRUE);
511   gtk_window_set_transient_for(GTK_WINDOW(win->window),
512                                GTK_WINDOW(v->normal_window.window));
513 
514   win->ra_plugins =
515     bg_gtk_plugin_widget_single_create(TR("Audio recorder"),
516                                        v->plugin_reg,
517                                        BG_PLUGIN_RECORDER_AUDIO,
518                                        BG_PLUGIN_ALL);
519 
520   bg_gtk_plugin_widget_single_set_change_callback(win->ra_plugins,
521                                                   set_ra_plugin,
522                                                   v);
523 
524   win->ov_plugins =
525     bg_gtk_plugin_widget_single_create(TR("Video output"),
526                                        v->plugin_reg,
527                                        BG_PLUGIN_OUTPUT_VIDEO,
528                                        BG_PLUGIN_ALL);
529 
530   win->close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
531   g_signal_connect(win->close_button, "clicked",
532                    G_CALLBACK(plugin_window_button_callback),
533                    v);
534   g_signal_connect(win->window, "delete-event",
535                    G_CALLBACK(plugin_window_delete_callback),
536                    v);
537 
538   gtk_widget_show(win->close_button);
539 
540   table = gtk_table_new(3, num_columns, 0);
541   gtk_table_set_row_spacings(GTK_TABLE(table), 5);
542   gtk_table_set_col_spacings(GTK_TABLE(table), 5);
543   gtk_container_set_border_width(GTK_CONTAINER(table), 5);
544   gtk_window_set_position(GTK_WINDOW(win->window), GTK_WIN_POS_CENTER);
545 
546   bg_gtk_plugin_widget_single_attach(win->ra_plugins, table,
547                                      &row, &num_columns);
548   bg_gtk_plugin_widget_single_attach(win->ov_plugins, table,
549                                      &row, &num_columns);
550 
551   gtk_table_attach(GTK_TABLE(table), win->close_button, 0, num_columns,
552                    row, row+1,
553                    GTK_SHRINK, GTK_SHRINK, 0, 0);
554 
555   gtk_widget_show(table);
556   gtk_container_add(GTK_CONTAINER(win->window),  table);
557   }
558 
motion_callback(void * data,int x,int y,int mask)559 static int motion_callback(void * data, int x, int y, int mask)
560   {
561   visualizer_t * v = (visualizer_t*)data;
562   if(v->toolbar_trigger & TOOLBAR_TRIGGER_MOUSE)
563     show_toolbar(v);
564   return FALSE;
565   }
566 
key_callback(GtkWidget * w,GdkEventKey * evt,gpointer data)567 static gboolean key_callback(GtkWidget * w,
568                              GdkEventKey * evt,
569                              gpointer data)
570   {
571   visualizer_t * v = (visualizer_t*)data;
572 
573   //  gtk_widget_show(v->toolbar);
574   //  g_timeout_add(2000, toolbar_timeout, v);
575   switch(evt->keyval)
576     {
577     case GDK_Tab:
578     case GDK_f:
579       toggle_fullscreen(v);
580       return TRUE;
581       break;
582     case GDK_Escape:
583       if(v->current_window == &v->fullscreen_window)
584         toggle_fullscreen(v);
585       return TRUE;
586       break;
587     case GDK_Menu:
588       if(v->toolbar_trigger & TOOLBAR_TRIGGER_KEY)
589         show_toolbar(v);
590       return TRUE;
591       break;
592     }
593   return FALSE;
594   }
595 
window_motion_callback(GtkWidget * widget,GdkEventMotion * event,gpointer data)596 static gboolean window_motion_callback(GtkWidget      *widget,
597                                        GdkEventMotion *event,
598                                        gpointer        data)
599   {
600   visualizer_t * v = (visualizer_t*)data;
601   if(v->toolbar_trigger & TOOLBAR_TRIGGER_MOUSE)
602     show_toolbar(v);
603   return FALSE;
604   }
605 
window_init(visualizer_t * v,window_t * w,int fullscreen)606 static void window_init(visualizer_t * v,
607                         window_t * w, int fullscreen)
608   {
609   GtkWidget * table;
610   w->window = bg_gtk_window_new(GTK_WINDOW_TOPLEVEL);
611 
612   if(!fullscreen)
613     g_signal_connect(G_OBJECT(w->window), "configure-event",
614                      G_CALLBACK(configure_callback), (gpointer)v);
615 
616   gtk_window_set_title(GTK_WINDOW(w->window), "Gmerlin visualizer");
617   w->socket = gtk_socket_new();
618   w->box = gtk_vbox_new(0, 0);
619   gtk_widget_show(w->box);
620 
621   table = gtk_table_new(1, 1, 0);
622 
623   gtk_table_attach_defaults(GTK_TABLE(table),w->socket,
624                             0, 1, 0, 1);
625   gtk_table_attach_defaults(GTK_TABLE(table),w->box,
626                             0, 1, 0, 1);
627 
628   gtk_widget_show(table);
629 
630   gtk_widget_set_events(w->window,
631                         GDK_POINTER_MOTION_MASK);
632 
633   gtk_widget_set_events(w->socket,
634                         GDK_KEY_PRESS_MASK |
635                         GDK_BUTTON_PRESS_MASK );
636 
637   //  gtk_window_set_focus_on_map(w->window, 0);
638 
639   gtk_container_add(GTK_CONTAINER(w->window), table);
640 
641   g_signal_connect(G_OBJECT(w->window), "delete_event",
642                    G_CALLBACK(delete_callback),
643                    v);
644 
645   g_signal_connect(G_OBJECT(w->socket), "plug-removed",
646                    G_CALLBACK(plug_removed_callback),
647                    v);
648 
649   g_signal_connect(G_OBJECT(w->socket), "grab-notify",
650                    G_CALLBACK(grab_notify_callback),
651                    v);
652 
653   g_signal_connect(G_OBJECT(w->socket), "plug-added",
654                    G_CALLBACK(plug_added_callback),
655                    v);
656 
657   g_signal_connect(G_OBJECT(w->socket), "key-press-event",
658                    G_CALLBACK(key_callback),
659                    v);
660 
661   g_signal_connect(G_OBJECT(w->window), "motion-notify-event",
662                    G_CALLBACK(window_motion_callback),
663                    v);
664 
665   gtk_widget_show(w->socket);
666 
667   if(fullscreen)
668     gtk_window_fullscreen(GTK_WINDOW(w->window));
669   //  else
670   //    gtk_widget_set_size_request(w->window, 640, 480);
671 
672   }
673 
create_pixmap_button(visualizer_t * w,const char * filename,const char * tooltip)674 static GtkWidget * create_pixmap_button(visualizer_t * w,
675                                         const char * filename,
676                                         const char * tooltip)
677   {
678   GtkWidget * button;
679   GtkWidget * image;
680   char * path;
681   path = bg_search_file_read("icons", filename);
682   if(path)
683     {
684     image = gtk_image_new_from_file(path);
685     free(path);
686     }
687   else
688     image = gtk_image_new();
689 
690   gtk_widget_show(image);
691   button = gtk_button_new();
692   gtk_container_add(GTK_CONTAINER(button), image);
693 
694   g_signal_connect(G_OBJECT(button), "clicked",
695                    G_CALLBACK(button_callback), w);
696 
697   gtk_widget_show(button);
698 
699   bg_gtk_tooltips_set_tip(button, tooltip, PACKAGE);
700 
701   return button;
702   }
703 #if 0
704 static GtkWidget * create_pixmap_toggle_button(visualizer_t * w,
705                                                const char * filename,
706                                                const char * tooltip,
707                                                guint * id)
708   {
709   GtkWidget * button;
710   GtkWidget * image;
711   char * path;
712   path = bg_search_file_read("icons", filename);
713   if(path)
714     {
715     image = gtk_image_new_from_file(path);
716     free(path);
717     }
718   else
719     image = gtk_image_new();
720 
721   gtk_widget_show(image);
722   button = gtk_toggle_button_new();
723   gtk_container_add(GTK_CONTAINER(button), image);
724 
725   *id = g_signal_connect(G_OBJECT(button), "toggled",
726                          G_CALLBACK(button_callback), w);
727 
728   gtk_widget_show(button);
729 
730   bg_gtk_tooltips_set_tip(button, tooltip, PACKAGE);
731 
732   return button;
733   }
734 #endif
735 
set_vis_param(void * data,const char * name,const bg_parameter_value_t * val)736 static void set_vis_param(void * data, const char * name,
737                           const bg_parameter_value_t * val)
738   {
739   visualizer_t * v;
740   v = (visualizer_t *)data;
741   bg_visualizer_set_parameter(v->visualizer, name, val);
742 
743   if(bg_visualizer_need_restart(v->visualizer) && v->vis_open &&
744      !name)
745     {
746     gtk_widget_hide(v->toolbar);
747     close_vis(v);
748     open_vis(v);
749     }
750   }
751 
set_general_parameter(void * data,const char * name,const bg_parameter_value_t * val)752 static void set_general_parameter(void * data, const char * name,
753                                   const bg_parameter_value_t * val)
754   {
755   visualizer_t * v;
756   int i_tmp;
757   v = (visualizer_t *)data;
758 
759   if(!name)
760     return;
761 
762   if(!strcmp(name, "toolbar_location"))
763     {
764     if(!strcmp(val->val_str, "top"))
765       i_tmp = TOOLBAR_LOCATION_TOP;
766     else
767       i_tmp = TOOLBAR_LOCATION_BOTTOM;
768 
769     if(v->toolbar_location != i_tmp)
770       {
771       v->toolbar_location = i_tmp;
772       /* Reparent toolbar */
773       gtk_container_remove(GTK_CONTAINER(v->current_window->box),
774                            v->toolbar);
775       attach_toolbar(v, v->current_window);
776       }
777     }
778   else if(!strcmp(name, "toolbar_trigger"))
779     {
780     if(!strcmp(val->val_str, "mouse"))
781       {
782       v->toolbar_trigger =
783         TOOLBAR_TRIGGER_MOUSE;
784       }
785     else if(!strcmp(val->val_str, "key"))
786       {
787       v->toolbar_trigger = TOOLBAR_TRIGGER_KEY;
788       }
789     else if(!strcmp(val->val_str, "mousekey"))
790       {
791       v->toolbar_trigger =
792         TOOLBAR_TRIGGER_MOUSE |
793         TOOLBAR_TRIGGER_KEY;
794       }
795     }
796   else if(!strcmp(name, "x"))
797     v->x = val->val_i;
798   else if(!strcmp(name, "y"))
799     v->y = val->val_i;
800   else if(!strcmp(name, "width"))
801     v->width = val->val_i;
802   else if(!strcmp(name, "height"))
803     v->height = val->val_i;
804   }
805 
get_general_parameter(void * data,const char * name,bg_parameter_value_t * val)806 static int get_general_parameter(void * data, const char * name,
807                                  bg_parameter_value_t * val)
808   {
809   visualizer_t * v;
810   v = (visualizer_t *)data;
811   if(!strcmp(name, "x"))
812     {
813     val->val_i = v->x;
814     return 1;
815     }
816   else if(!strcmp(name, "y"))
817     {
818     val->val_i = v->y;
819     return 1;
820     }
821   else if(!strcmp(name, "width"))
822     {
823     val->val_i = v->width;
824     return 1;
825     }
826   else if(!strcmp(name, "height"))
827     {
828     val->val_i = v->height;
829     return 1;
830     }
831   return 0;
832   }
833 
834 bg_parameter_info_t parameters[] =
835   {
836     {
837       .name =       "toolbar_location",
838       .long_name =  "Toolbar location",
839       .type =       BG_PARAMETER_STRINGLIST,
840       .flags =      BG_PARAMETER_SYNC,
841       .val_default = { .val_str = "top" },
842       .multi_names = (char const *[]){ "top", "bottom", NULL },
843       .multi_labels = (char const *[]){ "Top", "Bottom", NULL },
844     },
845     {
846       .name =       "toolbar_trigger",
847       .long_name =  "Toolbar trigger",
848       .type =       BG_PARAMETER_STRINGLIST,
849       .val_default = { .val_str = "mousekey" },
850       .multi_names = (char const *[]){ "mouse", "key", "mousekey", NULL },
851       .multi_labels = (char const *[]){ "Mouse motion",
852                                "Menu key", "Mouse motion & Menu key",
853                                NULL },
854     },
855     {
856       .name =       "x",
857       .long_name =  "X",
858       .type =       BG_PARAMETER_INT,
859       .flags =      BG_PARAMETER_HIDE_DIALOG,
860     },
861     {
862       .name =       "y",
863       .long_name =  "y",
864       .type =       BG_PARAMETER_INT,
865       .flags =      BG_PARAMETER_HIDE_DIALOG,
866     },
867     {
868       .name =       "width",
869       .long_name =  "width",
870       .type =       BG_PARAMETER_INT,
871       .flags =      BG_PARAMETER_HIDE_DIALOG,
872     },
873     {
874       .name =       "height",
875       .long_name =  "height",
876       .type =       BG_PARAMETER_INT,
877       .flags =      BG_PARAMETER_HIDE_DIALOG,
878     },
879     { /* End of parameters */ },
880 
881   };
882 
create_cfg_dialog(visualizer_t * win)883 static bg_dialog_t * create_cfg_dialog(visualizer_t * win)
884   {
885   const bg_parameter_info_t * info;
886   bg_dialog_t * ret;
887   ret = bg_dialog_create_multi(TR("Visualizer configuration"));
888 
889   bg_dialog_add(ret,
890                 TR("General"),
891                 win->general_section,
892                 set_general_parameter,
893                 NULL,
894                 (void*)(win),
895                 parameters);
896 
897   info = bg_visualizer_get_parameters(win->visualizer);
898   bg_dialog_add(ret,
899                 TR("Visualizer"),
900                 win->visualizer_section,
901                 set_vis_param,
902                 NULL,
903                 (void*)(win),
904                 info);
905 
906   info = bg_gtk_log_window_get_parameters(win->log_window);
907   bg_dialog_add(ret,
908                 TR("Log window"),
909                 win->log_section,
910                 bg_gtk_log_window_set_parameter,
911                 NULL,
912                 (void*)(win->log_window),
913                 info);
914 
915   return ret;
916   }
917 
918 
apply_config(visualizer_t * v)919 static void apply_config(visualizer_t * v)
920   {
921   const bg_parameter_info_t * info;
922 
923   info = bg_visualizer_get_parameters(v->visualizer);
924 
925   bg_cfg_section_apply(v->visualizer_section, info,
926                        bg_visualizer_set_parameter,
927                        (void*)(v->visualizer));
928 
929   bg_cfg_section_apply(v->general_section, parameters,
930                        set_general_parameter,
931                        (void*)(v));
932 
933   info = bg_gtk_log_window_get_parameters(v->log_window);
934 
935   bg_cfg_section_apply(v->log_section, info,
936                        bg_gtk_log_window_set_parameter,
937                        (void*)(v->log_window));
938 
939   }
940 
get_config(visualizer_t * v)941 static void get_config(visualizer_t * v)
942   {
943   const bg_parameter_info_t * info;
944 
945   bg_cfg_section_get(v->general_section, parameters,
946                      get_general_parameter,
947                      (void*)(v));
948 
949   info = bg_gtk_log_window_get_parameters(v->log_window);
950 
951   bg_cfg_section_get(v->log_section, info,
952                      bg_gtk_log_window_get_parameter,
953                      (void*)(v->log_window));
954 
955   }
956 
idle_func(void * data)957 static gboolean idle_func(void * data)
958   {
959   visualizer_t * v;
960   v = (visualizer_t *)data;
961   if(v->audio_open)
962     {
963     v->ra_plugin->read_audio(v->ra_handle->priv,
964                              v->audio_frame, 0, v->audio_format.samples_per_frame);
965     bg_visualizer_update(v->visualizer, v->audio_frame);
966 
967     if(v->toolbar_visible)
968       bg_gtk_vumeter_update(v->vumeter, v->audio_frame);
969 
970     //    bg_visualizer_update(v->visualizer, v->audio_frame);
971     }
972   return TRUE;
973   }
974 
975 
crossing_callback(GtkWidget * widget,GdkEventCrossing * event,gpointer data)976 static gboolean crossing_callback(GtkWidget *widget,
977                                   GdkEventCrossing *event,
978                                   gpointer data)
979   {
980   visualizer_t * v = (visualizer_t*)data;
981 
982   if(event->detail == GDK_NOTIFY_INFERIOR)
983     return FALSE;
984 
985   v->mouse_in_toolbar = (event->type == GDK_ENTER_NOTIFY) ? 1 : 0;
986   return FALSE;
987   }
988 
set_vis_plugin(const bg_plugin_info_t * plugin,void * data)989 static void set_vis_plugin(const bg_plugin_info_t * plugin,
990                            void * data)
991   {
992   visualizer_t * v = (visualizer_t*)data;
993   bg_visualizer_set_vis_plugin(v->visualizer, plugin);
994   bg_plugin_registry_set_default(v->plugin_reg, BG_PLUGIN_VISUALIZATION,
995                                  BG_PLUGIN_VISUALIZE_FRAME | BG_PLUGIN_VISUALIZE_GL,
996                                  plugin->name);
997   if(bg_visualizer_need_restart(v->visualizer))
998     {
999     close_vis(v);
1000     open_vis(v);
1001     }
1002   hide_toolbar(v);
1003   }
1004 
set_vis_parameter(void * data,const char * name,const bg_parameter_value_t * val)1005 static void set_vis_parameter(void * data, const char * name,
1006                               const bg_parameter_value_t * val)
1007   {
1008   visualizer_t * v = (visualizer_t*)data;
1009   bg_visualizer_set_vis_parameter(v->visualizer, name, val);
1010   }
1011 
log_close_callback(bg_gtk_log_window_t * w,void * data)1012 static void log_close_callback(bg_gtk_log_window_t * w, void * data)
1013   {
1014   visualizer_t * v = (visualizer_t*)data;
1015   gtk_widget_set_sensitive(v->log_button, 1);
1016   }
1017 
visualizer_create()1018 static visualizer_t * visualizer_create()
1019   {
1020   const bg_plugin_info_t * info;
1021   char * tmp_path;
1022   int row, col;
1023 
1024   GtkWidget * main_table;
1025   GtkWidget * table;
1026   GtkWidget * box;
1027 
1028   visualizer_t * ret;
1029   bg_cfg_section_t * cfg_section;
1030 
1031   ret = calloc(1, sizeof(*ret));
1032 
1033   ret->cb.motion_callback = motion_callback;
1034   ret->cb.data = ret;
1035 
1036   window_init(ret, &ret->normal_window, 0);
1037   window_init(ret, &ret->fullscreen_window, 1);
1038   ret->current_window = &ret->normal_window;
1039 
1040   ret->log_window = bg_gtk_log_window_create(log_close_callback, ret,
1041                                              TR("Gmerlin visualizer"));
1042 
1043   ret->config_button =
1044     create_pixmap_button(ret, "config_16.png", TRS("Configure"));
1045   ret->plugin_button =
1046     create_pixmap_button(ret, "plugin_16.png", TRS("Recording and display plugins"));
1047   ret->restart_button =
1048     create_pixmap_button(ret, "refresh_16.png", TRS("Restart visualization"));
1049   ret->quit_button =
1050     create_pixmap_button(ret, "quit_16.png", TRS("Quit"));
1051   ret->fullscreen_button =
1052     create_pixmap_button(ret, "fullscreen_16.png", TRS("Fullscreen mode"));
1053   ret->nofullscreen_button =
1054     create_pixmap_button(ret, "windowed_16.png", TRS("Leave fullscreen mode"));
1055 
1056   ret->log_button = create_pixmap_button(ret,
1057                                          "log_16.png",
1058                                          TRS("Show log window"));
1059   ret->about_button = create_pixmap_button(ret,
1060                                            "about_16.png",
1061                                            TRS("About Gmerlin visualizer"));
1062   ret->help_button = create_pixmap_button(ret,
1063                                           "help_16.png",
1064                                           TRS("Launch help in a webwroswer"));
1065 
1066   ret->fps = gtk_label_new("Fps: --:--");
1067   gtk_misc_set_alignment(GTK_MISC(ret->fps), 0.0, 0.5);
1068   gtk_widget_show(ret->fps);
1069 
1070   gtk_widget_hide(ret->nofullscreen_button);
1071 
1072   //  bg_gtk_box_pack_start_defaults(GTK_BOX(mainbox),
1073   //                              bg_gtk_vumeter_get_widget(ret->vumeter));
1074 
1075   ret->toolbar = gtk_event_box_new();
1076 
1077   gtk_widget_set_events(ret->toolbar,
1078                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1079 
1080   g_signal_connect(G_OBJECT(ret->toolbar), "enter-notify-event",
1081                    G_CALLBACK(crossing_callback), (gpointer*)ret);
1082 
1083   g_signal_connect(G_OBJECT(ret->toolbar), "leave-notify-event",
1084                    G_CALLBACK(crossing_callback), (gpointer*)ret);
1085 
1086 
1087   g_object_ref(ret->toolbar); /* Must be done for widgets, which get
1088                                  reparented after */
1089 
1090   ret->vumeter = bg_gtk_vumeter_create(2, 0);
1091 
1092   /* Create actual objects */
1093 
1094   /* Create plugin regsitry */
1095   ret->cfg_reg = bg_cfg_registry_create();
1096   tmp_path =  bg_search_file_read("visualizer", "config.xml");
1097   bg_cfg_registry_load(ret->cfg_reg, tmp_path);
1098   if(tmp_path)
1099     free(tmp_path);
1100 
1101   cfg_section = bg_cfg_registry_find_section(ret->cfg_reg, "plugins");
1102   ret->plugin_reg = bg_plugin_registry_create(cfg_section);
1103   ret->visualizer = bg_visualizer_create(ret->plugin_reg);
1104 
1105   bg_visualizer_set_callbacks(ret->visualizer, &ret->cb);
1106 
1107   /* Create vis plugin widget */
1108 
1109   ret->vis_plugins =
1110     bg_gtk_plugin_widget_single_create(TR("Visualization"),
1111                                        ret->plugin_reg,
1112                                        BG_PLUGIN_VISUALIZATION,
1113                                        BG_PLUGIN_VISUALIZE_FRAME |
1114                                        BG_PLUGIN_VISUALIZE_GL);
1115 
1116   bg_gtk_plugin_widget_single_set_change_callback(ret->vis_plugins,
1117                                                   set_vis_plugin,
1118                                                   ret);
1119 
1120   bg_gtk_plugin_widget_single_set_parameter_callback(ret->vis_plugins,
1121                                                      set_vis_parameter,
1122                                                      ret);
1123 
1124   /* Create audio and video plugin widgets */
1125 
1126   plugin_window_init(&ret->plugin_window, ret);
1127 
1128   /* Get ov info */
1129 
1130   ret->ov_info =
1131     bg_gtk_plugin_widget_single_get_plugin(ret->plugin_window.ov_plugins);
1132 
1133   /* Load recording plugin */
1134   ret->ra_info =
1135     bg_gtk_plugin_widget_single_get_plugin(ret->plugin_window.ra_plugins);
1136 
1137   /* Create config stuff */
1138 
1139   ret->visualizer_section =
1140     bg_cfg_registry_find_section(ret->cfg_reg, "visualizer");
1141   ret->general_section =
1142     bg_cfg_registry_find_section(ret->cfg_reg, "general");
1143   ret->log_section =
1144     bg_cfg_registry_find_section(ret->cfg_reg, "log");
1145 
1146   ret->cfg_dialog = create_cfg_dialog(ret);
1147 
1148 
1149 
1150   /* Pack everything */
1151 
1152   main_table = gtk_table_new(2, 2, 0);
1153 
1154   gtk_table_set_row_spacings(GTK_TABLE(main_table), 5);
1155   gtk_table_set_col_spacings(GTK_TABLE(main_table), 5);
1156   gtk_container_set_border_width(GTK_CONTAINER(main_table), 5);
1157 
1158   table = gtk_table_new(1, 4, 0);
1159 
1160   gtk_table_set_row_spacings(GTK_TABLE(table), 5);
1161   gtk_table_set_col_spacings(GTK_TABLE(table), 5);
1162   gtk_container_set_border_width(GTK_CONTAINER(table), 5);
1163 
1164 
1165   row = 0;
1166   col = 0;
1167   bg_gtk_plugin_widget_single_attach(ret->vis_plugins,
1168                                      table,
1169                                      &row, &col);
1170   gtk_widget_show(table);
1171 
1172   gtk_table_attach(GTK_TABLE(main_table), table, 0, 1, 0, 1,
1173                    GTK_FILL, GTK_SHRINK, 0, 0);
1174 
1175   box = gtk_hbox_new(0, 0);
1176   gtk_box_pack_start(GTK_BOX(box), ret->plugin_button,
1177                      FALSE, FALSE, 0);
1178   gtk_box_pack_start(GTK_BOX(box), ret->config_button,
1179                      FALSE, FALSE, 0);
1180   gtk_box_pack_start(GTK_BOX(box), ret->restart_button,
1181                      FALSE, FALSE, 0);
1182   gtk_box_pack_start(GTK_BOX(box), ret->fullscreen_button,
1183                      FALSE, FALSE, 0);
1184   gtk_box_pack_start(GTK_BOX(box), ret->nofullscreen_button,
1185                      FALSE, FALSE, 0);
1186   gtk_box_pack_start(GTK_BOX(box), ret->log_button,
1187                      FALSE, FALSE, 0);
1188   gtk_box_pack_start(GTK_BOX(box), ret->about_button,
1189                      FALSE, FALSE, 0);
1190   gtk_box_pack_start(GTK_BOX(box), ret->help_button,
1191                      FALSE, FALSE, 0);
1192   gtk_box_pack_start(GTK_BOX(box), ret->quit_button,
1193                      FALSE, FALSE, 0);
1194   gtk_box_pack_start(GTK_BOX(box), ret->fps, TRUE, TRUE, 5);
1195   gtk_widget_show(box);
1196 
1197   gtk_table_attach(GTK_TABLE(main_table), box, 0, 1, 1, 2,
1198                    GTK_FILL, GTK_SHRINK, 0, 0);
1199 
1200   gtk_table_attach(GTK_TABLE(main_table),
1201                    bg_gtk_vumeter_get_widget(ret->vumeter),
1202                    1, 2, 0, 2,
1203                    GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
1204 
1205 
1206   gtk_widget_show(main_table);
1207 
1208   gtk_container_add(GTK_CONTAINER(ret->toolbar), main_table);
1209 
1210   //  gtk_widget_show(ret->toolbar);
1211 
1212   /* Start with non-fullscreen mode */
1213   attach_toolbar(ret, &ret->normal_window);
1214 
1215   apply_config(ret);
1216 
1217   /* Get visualization plugin */
1218   info = bg_gtk_plugin_widget_single_get_plugin(ret->vis_plugins);
1219   bg_visualizer_set_vis_plugin(ret->visualizer, info);
1220 
1221   /* Initialize stuff */
1222   open_audio(ret);
1223   open_vis(ret);
1224 
1225 
1226   gtk_widget_show(ret->current_window->window);
1227 
1228   if(ret->width && ret->height)
1229     gtk_decorated_window_move_resize_window(GTK_WINDOW(ret->current_window->window),
1230                                             ret->x, ret->y,
1231                                             ret->width, ret->height);
1232   else
1233     gtk_decorated_window_move_resize_window(GTK_WINDOW(ret->current_window->window),
1234                                             100, 100,
1235                                             640, 480);
1236 
1237   while(gdk_events_pending() || gtk_events_pending())
1238     gtk_main_iteration();
1239 
1240 
1241   g_idle_add(idle_func, ret);
1242 
1243   g_timeout_add(3000, toolbar_timeout, ret);
1244   g_timeout_add(1000, fps_timeout, ret);
1245 
1246   return ret;
1247   }
1248 
visualizer_destroy(visualizer_t * v)1249 static void visualizer_destroy(visualizer_t * v)
1250   {
1251   char * tmp_path;
1252   get_config(v);
1253   tmp_path =  bg_search_file_write("visualizer", "config.xml");
1254   bg_cfg_registry_save(v->cfg_reg, tmp_path);
1255   if(tmp_path)
1256     free(tmp_path);
1257   free(v);
1258   }
1259 
main(int argc,char ** argv)1260 int main(int argc, char ** argv)
1261   {
1262   visualizer_t * win;
1263 
1264   bg_gtk_init(&argc, &argv, "visualizer_icon.png", NULL, NULL);
1265 
1266   win = visualizer_create();
1267   gtk_main();
1268 
1269   if(win->vis_open)
1270     bg_visualizer_close(win->visualizer);
1271   visualizer_destroy(win);
1272   return 0;
1273   }
1274