Update copyrights everywhere.
[lxde/lxpanel.git] / src / plugins / volumealsa / volumealsa.c
index 1bb6582..ffc932e 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2008 LxDE Developers, see the file AUTHORS for details.
+ * Copyright (c) 2008-2014 LxDE Developers, see the file AUTHORS for details.
  *
  * 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
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <alsa/asoundlib.h>
 #include <poll.h>
-#include "panel.h"
-#include "misc.h"
-#include "private.h"
+#include <libfm/fm-gtk.h>
+#include "plugin.h"
 #include "dbg.h"
 
-#define ICONS_VOLUME_HIGH   PACKAGE_DATA_DIR "/lxpanel/images/volume-high.png"
-#define ICONS_VOLUME_MEDIUM PACKAGE_DATA_DIR "/lxpanel/images/volume-medium.png"
-#define ICONS_VOLUME_LOW    PACKAGE_DATA_DIR "/lxpanel/images/volume-low.png"
-#define ICONS_MUTE          PACKAGE_DATA_DIR "/lxpanel/images/mute.png"
+#define ICONS_VOLUME_HIGH   PACKAGE_DATA_DIR "/images/volume-high.png"
+#define ICONS_VOLUME_MEDIUM PACKAGE_DATA_DIR "/images/volume-medium.png"
+#define ICONS_VOLUME_LOW    PACKAGE_DATA_DIR "/images/volume-low.png"
+#define ICONS_MUTE          PACKAGE_DATA_DIR "/images/mute.png"
 
 typedef struct {
 
     /* Graphics. */
-    Plugin * plugin;                           /* Back pointer to plugin */
+    GtkWidget * plugin;                                /* Back pointer to the widget */
+    LXPanel * panel;                           /* Back pointer to panel */
     GtkWidget * tray_icon;                     /* Displayed image */
     GtkWidget * popup_window;                  /* Top level window for popup */
     GtkWidget * volume_scale;                  /* Scale for volume */
@@ -52,6 +52,7 @@ typedef struct {
     snd_mixer_selem_id_t * sid;                        /* The element ID */
     snd_mixer_elem_t * master_element;         /* The Master element */
     guint mixer_evt_idle;                      /* Timer to handle restarting poll */
+    guint restart_idle;
 
     /* unloading and error handling */
     GIOChannel **channels;                      /* Channels that we listen to */
@@ -64,27 +65,11 @@ typedef struct {
 
 } VolumeALSAPlugin;
 
-static gboolean asound_find_element(VolumeALSAPlugin * vol, const char * ename);
-static gboolean asound_reset_mixer_evt_idle(VolumeALSAPlugin * vol);
-static gboolean asound_mixer_event(GIOChannel * channel, GIOCondition cond, gpointer vol_gpointer);
 static gboolean asound_restart(gpointer vol_gpointer);
 static gboolean asound_initialize(VolumeALSAPlugin * vol);
 static void asound_deinitialize(VolumeALSAPlugin * vol);
-static gboolean asound_has_mute(VolumeALSAPlugin * vol);
-static gboolean asound_is_muted(VolumeALSAPlugin * vol);
-static int asound_get_volume(VolumeALSAPlugin * vol);
-static void asound_set_volume(VolumeALSAPlugin * vol, int volume);
 static void volumealsa_update_display(VolumeALSAPlugin * vol);
-static gboolean volumealsa_button_press_event(GtkWidget * widget, GdkEventButton * event, VolumeALSAPlugin * vol);
-static gboolean volumealsa_popup_focus_out(GtkWidget * widget, GdkEvent * event, VolumeALSAPlugin * vol);
-static void volumealsa_popup_map(GtkWidget * widget, VolumeALSAPlugin * vol);
-static void volumealsa_popup_scale_changed(GtkRange * range, VolumeALSAPlugin * vol);
-static void volumealsa_popup_scale_scrolled(GtkScale * scale, GdkEventScroll * evt, VolumeALSAPlugin * vol);
-static void volumealsa_popup_mute_toggled(GtkWidget * widget, VolumeALSAPlugin * vol);
-static void volumealsa_build_popup_window(Plugin * p);
-static int volumealsa_constructor(Plugin * p, char ** fp);
-static void volumealsa_destructor(Plugin * p);
-static void volumealsa_panel_configuration_changed(Plugin * p);
+static void volumealsa_destructor(gpointer user_data);
 
 /*** ALSA ***/
 
@@ -126,7 +111,8 @@ static gboolean asound_find_element(VolumeALSAPlugin * vol, const char * ename)
 
 static gboolean asound_reset_mixer_evt_idle(VolumeALSAPlugin * vol)
 {
-    vol->mixer_evt_idle = 0;
+    if (!g_source_is_destroyed(g_main_current_source()))
+        vol->mixer_evt_idle = 0;
     return FALSE;
 }
 
@@ -151,14 +137,15 @@ static gboolean asound_mixer_event(GIOChannel * channel, GIOCondition cond, gpoi
     if ((cond & G_IO_HUP) || (res < 0))
     {
         /* This means there're some problems with alsa. */
-       ERR("volumealsa: ALSA (or pulseaudio) had a problem: \n"
+        ERR("volumealsa: ALSA (or pulseaudio) had a problem: \n"
                 "volumealsa: snd_mixer_handle_events() = %d,"
                 " cond 0x%x (IN: 0x%x, HUP: 0x%x).\n", res, cond,
                 G_IO_IN, G_IO_HUP);
-        gtk_widget_set_tooltip_text(vol->plugin->pwid, "ALSA (or pulseaudio) had a problem."
+        gtk_widget_set_tooltip_text(vol->plugin, "ALSA (or pulseaudio) had a problem."
                 " Please check the lxpanel logs.");
 
-        g_timeout_add_seconds(1, asound_restart, vol);
+        if (vol->restart_idle == 0)
+            vol->restart_idle = g_timeout_add_seconds(1, asound_restart, vol);
 
         return FALSE;
     }
@@ -170,6 +157,9 @@ static gboolean asound_restart(gpointer vol_gpointer)
 {
     VolumeALSAPlugin * vol = vol_gpointer;
 
+    if (g_source_is_destroyed(g_main_current_source()))
+        return FALSE;
+
     asound_deinitialize(vol);
 
     if (!asound_initialize(vol)) {
@@ -179,6 +169,7 @@ static gboolean asound_restart(gpointer vol_gpointer)
 
     ERR("volumealsa: Restarted ALSA interface...\n");
 
+    vol->restart_idle = 0;
     return FALSE;
 }
 
@@ -197,7 +188,7 @@ static gboolean asound_initialize(VolumeALSAPlugin * vol)
     if ( ! asound_find_element(vol, "Master"))
         if ( ! asound_find_element(vol, "Front"))
             if ( ! asound_find_element(vol, "PCM"))
-               if ( ! asound_find_element(vol, "LineOut"))
+                if ( ! asound_find_element(vol, "LineOut"))
                     return FALSE;
 
     /* Set the playback volume range as we wish it. */
@@ -224,7 +215,7 @@ static gboolean asound_initialize(VolumeALSAPlugin * vol)
 
 static void asound_deinitialize(VolumeALSAPlugin * vol)
 {
-    int i;
+    guint i;
 
     if (vol->mixer_evt_idle != 0) {
         g_source_remove(vol->mixer_evt_idle);
@@ -241,6 +232,7 @@ static void asound_deinitialize(VolumeALSAPlugin * vol)
 
     snd_mixer_close(vol->mixer);
     vol->master_element = NULL;
+    /* FIXME: unalloc vol->sid */
 }
 
 /* Get the presence of the mute control from the sound system. */
@@ -292,7 +284,7 @@ static void volumealsa_update_current_icon(VolumeALSAPlugin * vol)
     /* Mute status. */
     gboolean mute = asound_is_muted(vol);
     int level = asound_get_volume(vol);
-    
+
     /* Change icon according to mute / volume */
     const char* icon="audio-volume-muted";
     const char* icon_panel="audio-volume-muted-panel";
@@ -337,11 +329,11 @@ static void volumealsa_update_display(VolumeALSAPlugin * vol)
     volumealsa_update_current_icon(vol);
 
     /* Change icon, fallback to default icon if theme doesn't exsit */
-    if ( ! panel_image_set_icon_theme(vol->plugin->panel, vol->tray_icon, vol->icon_panel))
+    if ( ! lxpanel_image_set_icon_theme(vol->panel, vol->tray_icon, vol->icon_panel))
     {
-        if ( ! panel_image_set_icon_theme(vol->plugin->panel, vol->tray_icon, vol->icon))
+        if ( ! lxpanel_image_set_icon_theme(vol->panel, vol->tray_icon, vol->icon))
         {
-            panel_image_set_from_file(vol->plugin->panel, vol->tray_icon, vol->icon_fallback);
+            lxpanel_image_set_from_file(vol->panel, vol->tray_icon, vol->icon_fallback);
         }
     }
 
@@ -360,17 +352,15 @@ static void volumealsa_update_display(VolumeALSAPlugin * vol)
 
     /* Display current level in tooltip. */
     char * tooltip = g_strdup_printf("%s %d", _("Volume control"), level);
-    gtk_widget_set_tooltip_text(vol->plugin->pwid, tooltip);
+    gtk_widget_set_tooltip_text(vol->plugin, tooltip);
     g_free(tooltip);
 }
 
 
 /* Handler for "button-press-event" signal on main widget. */
-static gboolean volumealsa_button_press_event(GtkWidget * widget, GdkEventButton * event, VolumeALSAPlugin * vol)
+static gboolean volumealsa_button_press_event(GtkWidget * widget, GdkEventButton * event, LXPanel * panel)
 {
-    /* Standard right-click handling. */
-    if (plugin_button_press_event(widget, event, vol->plugin))
-        return TRUE;
+    VolumeALSAPlugin * vol = lxpanel_plugin_get_data(widget);
 
     /* Left-click.  Show or hide the popup window. */
     if (event->button == 1)
@@ -407,16 +397,16 @@ static gboolean volumealsa_popup_focus_out(GtkWidget * widget, GdkEvent * event,
 /* Handler for "map" signal on popup window. */
 static void volumealsa_popup_map(GtkWidget * widget, VolumeALSAPlugin * vol)
 {
-    plugin_adjust_popup_position(widget, vol->plugin);
+    lxpanel_plugin_adjust_popup_position(widget, vol->plugin);
 }
 
 static void volumealsa_theme_change(GtkWidget * widget, VolumeALSAPlugin * vol)
 {
-    if ( ! panel_image_set_icon_theme(vol->plugin->panel, vol->tray_icon, vol->icon_panel))
+    if ( ! lxpanel_image_set_icon_theme(vol->panel, vol->tray_icon, vol->icon_panel))
     {
-        if ( ! panel_image_set_icon_theme(vol->plugin->panel, vol->tray_icon, vol->icon))
+        if ( ! lxpanel_image_set_icon_theme(vol->panel, vol->tray_icon, vol->icon))
         {
-            panel_image_set_from_file(vol->plugin->panel, vol->tray_icon, vol->icon_fallback);
+            lxpanel_image_set_from_file(vol->panel, vol->tray_icon, vol->icon_fallback);
         }
     }
 }
@@ -466,21 +456,21 @@ static void volumealsa_popup_mute_toggled(GtkWidget * widget, VolumeALSAPlugin *
 }
 
 /* Build the window that appears when the top level widget is clicked. */
-static void volumealsa_build_popup_window(Plugin * p)
+static void volumealsa_build_popup_window(GtkWidget *p)
 {
-    VolumeALSAPlugin * vol = p->priv;
+    VolumeALSAPlugin * vol = lxpanel_plugin_get_data(p);
 
     /* Create a new window. */
-    vol->popup_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    vol->popup_window = gtk_window_new(GTK_WINDOW_POPUP);
     gtk_window_set_decorated(GTK_WINDOW(vol->popup_window), FALSE);
     gtk_container_set_border_width(GTK_CONTAINER(vol->popup_window), 5);
     gtk_window_set_default_size(GTK_WINDOW(vol->popup_window), 80, 140);
     gtk_window_set_skip_taskbar_hint(GTK_WINDOW(vol->popup_window), TRUE);
     gtk_window_set_skip_pager_hint(GTK_WINDOW(vol->popup_window), TRUE);
-    gtk_window_set_type_hint(GTK_WINDOW(vol->popup_window), GDK_WINDOW_TYPE_HINT_DIALOG);
+    gtk_window_set_type_hint(GTK_WINDOW(vol->popup_window), GDK_WINDOW_TYPE_HINT_UTILITY);
 
     /* Connect signals. */
-    g_signal_connect(G_OBJECT(vol->popup_window), "focus_out_event", G_CALLBACK(volumealsa_popup_focus_out), vol);
+    g_signal_connect(G_OBJECT(vol->popup_window), "focus-out-event", G_CALLBACK(volumealsa_popup_focus_out), vol);
     g_signal_connect(G_OBJECT(vol->popup_window), "map", G_CALLBACK(volumealsa_popup_map), vol);
 
     /* Create a scrolled window as the child of the top level window. */
@@ -488,7 +478,7 @@ static void volumealsa_build_popup_window(Plugin * p)
     gtk_container_set_border_width (GTK_CONTAINER(scrolledwindow), 0);
     gtk_widget_show(scrolledwindow);
     gtk_container_add(GTK_CONTAINER(vol->popup_window), scrolledwindow);
-    GTK_WIDGET_UNSET_FLAGS(scrolledwindow, GTK_CAN_FOCUS);
+    gtk_widget_set_can_focus(scrolledwindow, FALSE);
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_SHADOW_NONE);
 
@@ -514,7 +504,7 @@ static void volumealsa_build_popup_window(Plugin * p)
     gtk_box_pack_start(GTK_BOX(box), vol->volume_scale, TRUE, TRUE, 0);
 
     /* Value-changed and scroll-event signals. */
-    vol->volume_scale_handler = g_signal_connect(vol->volume_scale, "value_changed", G_CALLBACK(volumealsa_popup_scale_changed), vol);
+    vol->volume_scale_handler = g_signal_connect(vol->volume_scale, "value-changed", G_CALLBACK(volumealsa_popup_scale_changed), vol);
     g_signal_connect(vol->volume_scale, "scroll-event", G_CALLBACK(volumealsa_popup_scale_scrolled), vol);
 
     /* Create a check button as the child of the vertical box. */
@@ -523,48 +513,51 @@ static void volumealsa_build_popup_window(Plugin * p)
     vol->mute_check_handler = g_signal_connect(vol->mute_check, "toggled", G_CALLBACK(volumealsa_popup_mute_toggled), vol);
 
     /* Set background to default. */
-    gtk_widget_set_style(viewport, p->panel->defstyle);
+    gtk_widget_set_style(viewport, panel_get_defstyle(vol->panel));
 }
 
 /* Plugin constructor. */
-static int volumealsa_constructor(Plugin * p, char ** fp)
+static GtkWidget *volumealsa_constructor(LXPanel *panel, config_setting_t *settings)
 {
     /* Allocate and initialize plugin context and set into Plugin private data pointer. */
     VolumeALSAPlugin * vol = g_new0(VolumeALSAPlugin, 1);
-    vol->plugin = p;
-    p->priv = vol;
+    GtkWidget *p;
 
     /* Initialize ALSA.  If that fails, present nothing. */
     if ( ! asound_initialize(vol))
-        return 1;
+    {
+        volumealsa_destructor(vol);
+        return NULL;
+    }
 
     /* Allocate top level widget and set into Plugin widget pointer. */
-    p->pwid = gtk_event_box_new();
-    gtk_widget_add_events(p->pwid, GDK_BUTTON_PRESS_MASK);
-    gtk_widget_set_tooltip_text(p->pwid, _("Volume control"));
+    vol->panel = panel;
+    vol->plugin = p = gtk_event_box_new();
+    lxpanel_plugin_set_data(p, vol, volumealsa_destructor);
+    gtk_widget_add_events(p, GDK_BUTTON_PRESS_MASK);
+    gtk_widget_set_tooltip_text(p, _("Volume control"));
 
     /* Allocate icon as a child of top level. */
     vol->tray_icon = gtk_image_new();
-    gtk_container_add(GTK_CONTAINER(p->pwid), vol->tray_icon);
+    gtk_container_add(GTK_CONTAINER(p), vol->tray_icon);
 
     /* Initialize window to appear when icon clicked. */
     volumealsa_build_popup_window(p);
 
     /* Connect signals. */
-    g_signal_connect(G_OBJECT(p->pwid), "button-press-event", G_CALLBACK(volumealsa_button_press_event), vol);
-    g_signal_connect(G_OBJECT(p->pwid), "scroll-event", G_CALLBACK(volumealsa_popup_scale_scrolled), vol );
-    g_signal_connect(p->panel->icon_theme, "changed", G_CALLBACK(volumealsa_theme_change), vol );
+    g_signal_connect(G_OBJECT(p), "scroll-event", G_CALLBACK(volumealsa_popup_scale_scrolled), vol );
+    g_signal_connect(panel_get_icon_theme(panel), "changed", G_CALLBACK(volumealsa_theme_change), vol );
 
     /* Update the display, show the widget, and return. */
     volumealsa_update_display(vol);
-    gtk_widget_show_all(p->pwid);
-    return 1;
+    gtk_widget_show_all(p);
+    return p;
 }
 
 /* Plugin destructor. */
-static void volumealsa_destructor(Plugin * p)
+static void volumealsa_destructor(gpointer user_data)
 {
-    VolumeALSAPlugin * vol = (VolumeALSAPlugin *) p->priv;
+    VolumeALSAPlugin * vol = (VolumeALSAPlugin *) user_data;
 
     asound_deinitialize(vol);
 
@@ -572,17 +565,17 @@ static void volumealsa_destructor(Plugin * p)
     if (vol->popup_window != NULL)
         gtk_widget_destroy(vol->popup_window);
 
+    if (vol->restart_idle)
+        g_source_remove(vol->restart_idle);
+
     /* Deallocate all memory. */
     g_free(vol);
 }
 
 /* Callback when the configuration dialog is to be shown. */
 
-static void volumealsa_configure(Plugin * p, GtkWindow * parent)
+static GtkWidget *volumealsa_configure(LXPanel *panel, GtkWidget *p)
 {
-
-    GdkScreen *screen = gdk_screen_get_default();
-    GError *error = NULL;
     const gchar *command_line = NULL;
 
     if (g_find_program_in_path("pulseaudio"))
@@ -622,56 +615,37 @@ static void volumealsa_configure(Plugin * p, GtkWindow * parent)
 
     if (command_line)
     {
-        gdk_spawn_command_line_on_screen(screen,
-                                         command_line,
-                                         &error);
+        fm_launch_command_simple(NULL, NULL, G_APP_INFO_CREATE_NONE,
+                                 command_line, NULL);
     }
     else
     {
-
-        GtkWidget* msg;
-
-        msg = gtk_message_dialog_new( NULL,
-                                      0,
-                                      GTK_MESSAGE_ERROR,
-                                      GTK_BUTTONS_OK,
-                                      (_("Error, you need to install an application to configure the sound (pavucontol, alsamixer ...)")) );
-        gtk_dialog_run( GTK_DIALOG(msg) );
-        gtk_widget_destroy( msg );
-
-    }
-
-    if (error)
-    {
-        g_print("%s\n", error->message);
-        g_free (error);
+        fm_show_error(NULL,
+                      _("Error, you need to install an application to configure the sound (pavucontrol, alsamixer ...)"),
+                      NULL);
     }
 
+    return NULL;
 }
 
 /* Callback when panel configuration changes. */
-static void volumealsa_panel_configuration_changed(Plugin * p)
+static void volumealsa_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
 {
     /* Do a full redraw. */
-    volumealsa_update_display((VolumeALSAPlugin *) p->priv);
+    volumealsa_update_display(lxpanel_plugin_get_data(p));
 }
 
-/* Plugin descriptor. */
-PluginClass volumealsa_plugin_class = {
-
-    PLUGINCLASS_VERSIONING,
+FM_DEFINE_MODULE(lxpanel_gtk, volumealsa)
 
-    .type = "volumealsa",
+/* Plugin descriptor. */
+LXPanelPluginInit fm_module_init_lxpanel_gtk = {
     .name = N_("Volume Control"),
-    .version = "1.0",
     .description = N_("Display and control volume for ALSA"),
 
-    .constructor = volumealsa_constructor,
-    .destructor  = volumealsa_destructor,
+    .new_instance = volumealsa_constructor,
     .config = volumealsa_configure,
-    .save = NULL,
-    .panel_configuration_changed = volumealsa_panel_configuration_changed
-
+    .reconfigure = volumealsa_panel_configuration_changed,
+    .button_press_event = volumealsa_button_press_event
 };
 
 /* vim: set sw=4 et sts=4 : */