diff options
Diffstat (limited to 'plugins/gtkui/gtkui.c')
-rw-r--r-- | plugins/gtkui/gtkui.c | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c new file mode 100644 index 00000000..eac8bdc9 --- /dev/null +++ b/plugins/gtkui/gtkui.c @@ -0,0 +1,455 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009 Alexey Yakovenko + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include "../../deadbeef.h" +#include <gtk/gtk.h> +#include <string.h> +#include "gtkplaylist.h" +#include "search.h" +#include "progress.h" +#include "interface.h" +#include "callbacks.h" +#include "support.h" + +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) + +static DB_gui_t plugin; +static DB_functions_t *deadbeef; + +// main widgets +GtkWidget *mainwin; +GtkWidget *searchwin; +GtkStatusIcon *trayicon; +GtkWidget *traymenu; + +// playlist configuration structures +gtkplaylist_t main_playlist; +gtkplaylist_t search_playlist; + +// update status bar and window title +static int sb_context_id = -1; +static char sb_text[512]; +static float last_songpos = -1; + +static void +update_songinfo (void) { + char sbtext_new[512] = "-"; + float songpos = last_songpos; + + float pl_totaltime = deadbeef->pl_get_totaltime (); + int daystotal = (int)pl_totaltime / (3600*24); + int hourtotal = ((int)pl_totaltime / 3600) % 24; + int mintotal = ((int)pl_totaltime/60) % 60; + int sectotal = ((int)pl_totaltime) % 60; + + char totaltime_str[512] = ""; + if (daystotal == 0) + snprintf (totaltime_str, sizeof (totaltime_str), "%d:%02d:%02d", hourtotal, mintotal, sectotal); + + else if (daystotal == 1) + snprintf (totaltime_str, sizeof (totaltime_str), "1 day %d:%02d:%02d", hourtotal, mintotal, sectotal); + + else + snprintf (totaltime_str, sizeof (totaltime_str), "%d days %d:%02d:%02d", daystotal, hourtotal, mintotal, sectotal); + + + + DB_playItem_t *track = deadbeef->streamer_get_playing_track (); + float duration = deadbeef->pl_get_item_duration (track); + + if (deadbeef->playback_isstopped ()) { + snprintf (sbtext_new, sizeof (sbtext_new), "Stopped | %s total playtime", totaltime_str); + songpos = 0; + } + else if (track->decoder) { +// codec_lock (); + DB_decoder_t *c = track->decoder; + float playpos = deadbeef->streamer_get_playpos (); + int minpos = playpos / 60; + int secpos = playpos - minpos * 60; + int mindur = duration / 60; + int secdur = duration - mindur * 60; + + const char *mode = c->info.channels == 1 ? "Mono" : "Stereo"; + int samplerate = c->info.samplerate; + int bitspersample = c->info.bps; + songpos = playpos; +// codec_unlock (); + + char t[100]; + if (duration >= 0) { + snprintf (t, sizeof (t), "%d:%02d", mindur, secdur); + } + else { + strcpy (t, "-:--"); + } + + char sbitrate[20] = ""; +#if 0 // NOTE: do not enable that for stable branch yet + int bitrate = streamer_get_bitrate (); + if (bitrate > 0) { + snprintf (sbitrate, sizeof (sbitrate), "%d kbps ", bitrate); + } +#endif + const char *spaused = deadbeef->playback_ispaused () ? "Paused | " : ""; + snprintf (sbtext_new, sizeof (sbtext_new), "%s%s %s| %dHz | %d bit | %s | %d:%02d / %s | %d songs | %s total playtime", spaused, track->filetype ? track->filetype:"-", sbitrate, samplerate, bitspersample, mode, minpos, secpos, t, pl_getcount (), totaltime_str); + } + + if (strcmp (sbtext_new, sb_text)) { + strcpy (sb_text, sbtext_new); + + // form statusline + GDK_THREADS_ENTER(); + // FIXME: don't update if window is not visible + GtkStatusbar *sb = GTK_STATUSBAR (lookup_widget (mainwin, "statusbar")); + if (sb_context_id == -1) { + sb_context_id = gtk_statusbar_get_context_id (sb, "msg"); + } + + gtk_statusbar_pop (sb, sb_context_id); + gtk_statusbar_push (sb, sb_context_id, sb_text); + + GDK_THREADS_LEAVE(); + } + + if (songpos != last_songpos) { + void seekbar_draw (GtkWidget *widget); + void seekbar_expose (GtkWidget *widget, int x, int y, int w, int h); + if (mainwin) { + GtkWidget *widget = lookup_widget (mainwin, "seekbar"); + // translate volume to seekbar pixels + songpos /= duration; + songpos *= widget->allocation.width; + if ((int)(songpos*2) != (int)(last_songpos*2)) { + GDK_THREADS_ENTER(); + seekbar_draw (widget); + seekbar_expose (widget, 0, 0, widget->allocation.width, widget->allocation.height); + GDK_THREADS_LEAVE(); + last_songpos = songpos; + } + } + } +} + +gboolean +on_trayicon_scroll_event (GtkWidget *widget, + GdkEventScroll *event, + gpointer user_data) +{ + float vol = deadbeef->volume_get_db (); + if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_RIGHT) { + vol += 1; + } + else if (event->direction == GDK_SCROLL_DOWN || event->direction == GDK_SCROLL_LEFT) { + vol -= 1; + } + if (vol > 0) { + vol = 0; + } + else if (vol < -60) { + vol = -60; + } + deadbeef->volume_set_db (vol); + GtkWidget *volumebar = lookup_widget (mainwin, "volumebar"); + volumebar_draw (volumebar); + volumebar_expose (volumebar, 0, 0, volumebar->allocation.width, volumebar->allocation.height); + return FALSE; +} + +#if GTK_MINOR_VERSION<=14 +gboolean +on_trayicon_activate (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + if (GTK_WIDGET_VISIBLE (mainwin)) { + gtk_widget_hide (mainwin); + } + else { + gtk_widget_show (mainwin); + gtk_window_present (GTK_WINDOW (mainwin)); + } + return FALSE; +} +#endif + +gboolean +on_trayicon_button_press_event (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + if (event->button == 1) { + if (GTK_WIDGET_VISIBLE (mainwin)) { + gtk_widget_hide (mainwin); + } + else { + gtk_widget_show (mainwin); + gtk_window_present (GTK_WINDOW (mainwin)); + } + } + return FALSE; +} + +gboolean +on_trayicon_popup_menu (GtkWidget *widget, + guint button, + guint time, + gpointer user_data) +{ + gtk_menu_popup (GTK_MENU (traymenu), NULL, NULL, gtk_status_icon_position_menu, trayicon, button, time); + return FALSE; +} + +void +guiplug_showwindow (void) { + GDK_THREADS_ENTER(); + gtk_widget_show (mainwin); + gtk_window_present (GTK_WINDOW (mainwin)); + GDK_THREADS_LEAVE(); +} + +void +guiplug_play_current_song (void) { + GDK_THREADS_ENTER(); + gtkpl_playsong (&main_playlist); + GDK_THREADS_LEAVE(); +} + +void +guiplug_shutdown (void) { + GDK_THREADS_ENTER(); + gtk_widget_hide (mainwin); + gtk_main_quit (); + GDK_THREADS_LEAVE(); +} + +void +guiplug_start_current_track (void) { + gtkpl_playsong (&main_playlist); + if (playlist_current_ptr) { + GDK_THREADS_ENTER(); + gtkpl_redraw_pl_row (&main_playlist, pl_get_idx_of (playlist_current_ptr), playlist_current_ptr); + GDK_THREADS_LEAVE(); + } +} + +void +guiplug_start_track (int idx) { + GDK_THREADS_ENTER(); + gtkpl_playsongnum (idx); + GDK_THREADS_LEAVE(); +} + +void +guiplug_start_random (void) { + // <deprecated> + assert (0); +} + +void +guiplug_add_dir (const char *dir) { + // long time processing + // float t1 = (float)clock () / CLOCKS_PER_SEC; + gtkpl_add_dir (&main_playlist, dir); + // float t2 = (float)clock () / CLOCKS_PER_SEC; + // printf ("time: %f\n", t2-t1); +} + +void +guiplug_add_dirs (GSList *dirs) { + // long time processing + // float t1 = (float)clock () / CLOCKS_PER_SEC; + gtkpl_add_dirs (&main_playlist, dirs); + // float t2 = (float)clock () / CLOCKS_PER_SEC; + // printf ("time: %f\n", t2-t1); +} + +void +guiplug_add_files (GSList *files) { + gtkpl_add_files (&main_playlist, files); +} + +void +guiplug_open_files (GSList *files) { + gtkpl_add_files (&main_playlist, files); + gtkpl_playsong (&main_playlist); +} + +void +guiplug_refresh_playlist (void) { + // <deprecated> + assert (0); +} + +void +guiplug_add_fm_dropped_files (char *files, int p1, int p2) { + gtkpl_add_fm_dropped_files (&main_playlist, files, p1, p2); +} + +static int +gtkui_on_activate (DB_event_t *ev, uintptr_t data) { + GDK_THREADS_ENTER(); + gtk_widget_show (mainwin); + gtk_window_present (GTK_WINDOW (mainwin)); + GDK_THREADS_LEAVE(); +} + +static int +gtkui_on_songchanged (DB_event_trackchange_t *ev, uintptr_t data) { + gtkpl_songchanged_wrapper (ev->from, ev->to); +} + +static int +gtkui_on_trackinfochanged (DB_event_track_t *ev, uintptr_t data) { + GDK_THREADS_ENTER(); + gtkpl_redraw_pl_row (&main_playlist, ev->index, ev->track); + if (ev->track == playlist_current_ptr) { + gtkpl_current_track_changed (ev->track); + } + GDK_THREADS_LEAVE(); +} + +static int +gtkui_on_paused (DB_event_state_t *ev, uintptr_t data) { + GDK_THREADS_ENTER(); + gtkpl_redraw_pl_row (&main_playlist, pl_get_idx_of (playlist_current_ptr), playlist_current_ptr); + GDK_THREADS_LEAVE(); +} + +static int +gtkui_on_playlistchanged (DB_event_t *ev, uintptr_t data) { + GDK_THREADS_ENTER(); + playlist_refresh (); + search_refresh (); + GDK_THREADS_LEAVE(); +} + +static int +gtkui_on_frameupdate (DB_event_t *ev, uintptr_t data) { + update_songinfo (); +} + +static int +gtkui_on_volumechanged (DB_event_t *ev, uintptr_t data) { + void volumebar_notify_changed (void); // FIXME: do it properly + volumebar_notify_changed (); +} + +static int +gtkui_start (void) { + // let's start some gtk + g_thread_init (NULL); + add_pixmap_directory (PREFIX "/share/deadbeef/pixmaps"); + gdk_threads_init (); + gdk_threads_enter (); + gtk_set_locale (); + gtk_init (&argc, &argv); + + // system tray icon + traymenu = create_traymenu (); + GdkPixbuf *trayicon_pixbuf = create_pixbuf ("play_24.png"); + trayicon = gtk_status_icon_new_from_pixbuf (trayicon_pixbuf); + set_tray_tooltip ("DeaDBeeF"); + //gtk_status_icon_set_title (GTK_STATUS_ICON (trayicon), "DeaDBeeF"); +#if GTK_MINOR_VERSION <= 14 + g_signal_connect ((gpointer)trayicon, "activate", G_CALLBACK (on_trayicon_activate), NULL); +#else + g_signal_connect ((gpointer)trayicon, "scroll_event", G_CALLBACK (on_trayicon_scroll_event), NULL); + g_signal_connect ((gpointer)trayicon, "button_press_event", G_CALLBACK (on_trayicon_button_press_event), NULL); +#endif + g_signal_connect ((gpointer)trayicon, "popup_menu", G_CALLBACK (on_trayicon_popup_menu), NULL); + + gtkpl_init (); + + mainwin = create_mainwin (); + GdkPixbuf *mainwin_icon_pixbuf; + mainwin_icon_pixbuf = create_pixbuf ("play_24.png"); + if (mainwin_icon_pixbuf) + { + gtk_window_set_icon (GTK_WINDOW (mainwin), mainwin_icon_pixbuf); + gdk_pixbuf_unref (mainwin_icon_pixbuf); + } + session_restore_window_attrs ((uintptr_t)mainwin); + // order and looping + const char *orderwidgets[3] = { "order_linear", "order_shuffle", "order_random" }; + const char *loopingwidgets[3] = { "loop_all", "loop_disable", "loop_single" }; + const char *w; + w = orderwidgets[conf_get_int ("playback.order", 0)]; + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, w)), TRUE); + w = loopingwidgets[conf_get_int ("playback.loop", 0)]; + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, w)), TRUE); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, "scroll_follows_playback")), conf_get_int ("playlist.scroll.followplayback", 0) ? TRUE : FALSE); + + searchwin = create_searchwin (); + gtk_window_set_transient_for (GTK_WINDOW (searchwin), GTK_WINDOW (mainwin)); + extern void main_playlist_init (GtkWidget *widget); + main_playlist_init (lookup_widget (mainwin, "playlist")); + extern void search_playlist_init (GtkWidget *widget); + search_playlist_init (lookup_widget (searchwin, "searchlist")); + + progress_init (); + gtk_widget_show (mainwin); + + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_ACTIVATE, DB_CALLBACK (gtkui_on_activate), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_SONGCHANGED, DB_CALLBACK (gtkui_on_songchanged), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_TRACKINFOCHANGED, DB_CALLBACK (gtkui_on_trackinfochanged), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_PAUSED, DB_CALLBACK (gtkui_on_paused), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_PLAYLISTCHANGED, DB_CALLBACK (gtkui_on_playlistchanged), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_FRAMEUPDATE, DB_CALLBACK (gtkui_on_frameupdate), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_VOLUMECHANGED, DB_CALLBACK (gtkui_on_volumechanged), 0); + gtk_main (); + + return 0; +} + +static int +gtkui_stop (void) { + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_ACTIVATE, DB_CALLBACK (gtkui_on_activate), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_SONGCHANGED, DB_CALLBACK (gtkui_on_songchanged), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_TRACKINFOCHANGED, DB_CALLBACK (gtkui_on_trackinfochanged), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_PAUSED, DB_CALLBACK (gtkui_on_paused), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_PLAYLISTCHANGED, DB_CALLBACK (gtkui_on_playlistchanged), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_FRAMEUPDATE, DB_CALLBACK (gtkui_on_frameupdate), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_VOLUMECHANGED, DB_CALLBACK (gtkui_on_volumechanged), 0); + gtkpl_free (&main_playlist); + gtkpl_free (&search_playlist); + gdk_threads_leave (); + return 0; +} + +DB_plugin_t * +gtkui_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} + +// define plugin interface +static DB_gui_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.type = DB_PLUGIN_MISC, + .plugin.name = "Standard GTK2 user interface", + .plugin.descr = "", + .plugin.author = "Alexey Yakovenko", + .plugin.email = "waker@users.sourceforge.net", + .plugin.website = "http://deadbeef.sf.net", + .plugin.start = gtkui_start, + .plugin.stop = gtkui_stop +}; |