]> www.infradead.org Git - users/dwmw2/pidgin-headset.git/commitdiff
Initial import of media state tracking shell
authorDavid Woodhouse <dwmw2@infradead.org>
Wed, 21 Mar 2018 20:52:45 +0000 (20:52 +0000)
committerDavid Woodhouse <dwmw2@infradead.org>
Wed, 21 Mar 2018 21:16:31 +0000 (21:16 +0000)
Makefile.am [new file with mode: 0644]
acinclude.m4 [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
headset.c [new file with mode: 0644]
headset.h [new file with mode: 0644]
jabra.c [new file with mode: 0644]
po/LINGUAS [new file with mode: 0644]
po/Makefile.am [new file with mode: 0644]

diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..85e5eb4
--- /dev/null
@@ -0,0 +1,12 @@
+# @PIDGIN_PLUGIN_RULE@
+
+pidgin_plugin_LTLIBRARIES = headset.la
+
+headset_la_CFLAGS = $(PIDGIN_CFLAGS)
+
+headset_la_SOURCES = headset.c jabra.c headset.h
+
+headset_la_LDFLAGS = -module -avoid-version -no-undefined
+
+headset_la_LIBADD = $(PIDGIN_LIBS)
+
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644 (file)
index 0000000..1c74267
--- /dev/null
@@ -0,0 +1,61 @@
+dnl as-compiler-flag.m4 0.1.0
+
+dnl autostars m4 macro for detection of compiler flags
+
+dnl David Schleef <ds@schleef.org>
+
+dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $
+
+dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED])
+dnl Tries to compile with the given CFLAGS.
+dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags,
+dnl and ACTION-IF-NOT-ACCEPTED otherwise.
+
+AC_DEFUN([AS_COMPILER_FLAG],
+[
+  AC_MSG_CHECKING([to see if compiler understands $1])
+
+  save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS $1"
+
+  AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
+  CFLAGS="$save_CFLAGS"
+
+  if test "X$flag_ok" = Xyes ; then
+    m4_ifvaln([$2],[$2])
+    true
+  else
+    m4_ifvaln([$3],[$3])
+    true
+  fi
+  AC_MSG_RESULT([$flag_ok])
+])
+
+dnl AS_COMPILER_FLAGS(VAR, FLAGS)
+dnl Tries to compile with the given CFLAGS.
+
+AC_DEFUN([AS_COMPILER_FLAGS],
+[
+  list=$2
+  flags_supported=""
+  flags_unsupported=""
+  AC_MSG_CHECKING([for supported compiler flags])
+  for each in $list
+  do
+    save_CFLAGS="$CFLAGS"
+    CFLAGS="$CFLAGS $each"
+    AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
+    CFLAGS="$save_CFLAGS"
+
+    if test "X$flag_ok" = Xyes ; then
+      flags_supported="$flags_supported $each"
+    else
+      flags_unsupported="$flags_unsupported $each"
+    fi
+  done
+  AC_MSG_RESULT([$flags_supported])
+  if test "X$flags_unsupported" != X ; then
+    AC_MSG_WARN([unsupported compiler flags: $flags_unsupported])
+  fi
+  $1="$$1 $flags_supported"
+])
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..fba4adc
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+test -n "$srcdir" || srcdir=`dirname "$0"`
+test -n "$srcdir" || srcdir=.
+
+OLDDIR=`pwd`
+cd $srcdir
+
+AUTORECONF=`which autoreconf`
+if test -z $AUTORECONF; then
+    echo "*** No autoreconf found, please install it ***"
+    exit 1
+fi
+
+aclocal --install -I m4 || exit 1
+autoreconf --force --install --verbose || exit 1
+
+cd $OLDDIR
+test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..7716c59
--- /dev/null
@@ -0,0 +1,51 @@
+AC_INIT(pidgin-headset, 0.01)
+
+PKG_PROG_PKG_CONFIG
+AC_LANG_C
+AC_CANONICAL_HOST
+AC_DISABLE_STATIC
+AM_MAINTAINER_MODE([enable])
+AM_INIT_AUTOMAKE([foreign tar-ustar subdir-objects])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+LT_INIT
+
+AC_PREREQ([2.62], [], [AC_SUBST([localedir], ['$(datadir)/locale'])])
+
+AS_COMPILER_FLAGS(WFLAGS,
+        "-Wall
+         -Wextra
+         -Wno-missing-field-initializers
+         -Wno-sign-compare
+         -Wno-unused-parameter
+         -Werror=pointer-to-int-cast
+         -Wdeclaration-after-statement
+         -Werror-implicit-function-declaration
+         -Wformat-nonliteral
+         -Wformat-security
+         -Winit-self
+         -Wmissing-declarations
+         -Wmissing-include-dirs
+         -Wnested-externs
+         -Wpointer-arith
+         -Wwrite-strings
+        -Wno-declaration-after-statement")
+AC_SUBST(WFLAGS, [$WFLAGS])
+
+pidgin_plugindir=
+PKG_CHECK_MODULES(PIDGIN, [pidgin])
+pidgin_plugindir="$($PKG_CONFIG --variable=plugindir pidgin)"
+AC_SUBST(pidgin_plugindir, ${pidgin_plugindir})
+
+AC_SUBST([CONFIG_STATUS_DEPENDENCIES],
+       ['$(top_srcdir)/po/LINGUAS'])
+
+RAWLINGUAS=`sed -e "/^#/d" -e "s/#.*//" "${srcdir}/po/LINGUAS"`
+# Remove newlines
+LINGUAS=`echo $RAWLINGUAS`
+AC_SUBST(LINGUAS)
+
+AC_CONFIG_FILES([
+       Makefile
+       po/Makefile
+       ])
+AC_OUTPUT
diff --git a/headset.c b/headset.c
new file mode 100644 (file)
index 0000000..7ef8139
--- /dev/null
+++ b/headset.c
@@ -0,0 +1,262 @@
+/*
+ * Pidgin plugin for USB headset management
+ *
+ * Copyright © 2018 David Woodhouse.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ */
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+
+#define PURPLE_PLUGINS
+#include <debug.h>
+#include <version.h>
+#include <mediamanager.h>
+#include <request.h>
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "headset.h"
+
+/** Plugin id : type-author-name (to guarantee uniqueness) */
+#define SIMPLE_PLUGIN_ID "media-dwmw2-headset"
+static void
+headset_show_about_plugin(PurplePluginAction *action)
+{
+       purple_notify_formatted(action->context,
+                               NULL, _("Headset media plugin"), PACKAGE_STRING, _("Beep Beep"),
+                               NULL, NULL);
+}
+
+static GList*
+headset_plugin_actions(PurplePlugin *plugin,
+                        gpointer context)
+{
+       PurplePluginAction *act;
+       GList *acts = NULL;
+
+       act = purple_plugin_action_new(_("About headset plugin..."),
+                                      headset_show_about_plugin);
+       acts = g_list_append(acts, act);
+
+       return acts;
+}
+
+typedef gboolean (*media_cb)(PurpleMedia *media, const gchar *sess_id, gpointer user_data);
+static gboolean foreach_media(media_cb fn, gpointer user_data)
+{
+       PurpleMediaManager *mgr = purple_media_manager_get();
+       GList *medias = purple_media_manager_get_media(mgr);
+       gboolean ret = FALSE;
+
+       for (; medias; medias = medias->next) {
+               PurpleMedia *media = medias->data;
+
+               GList *sess_ids = purple_media_get_session_ids(media);
+               while (sess_ids) {
+                       const gchar *sess_id = sess_ids->data;
+                       sess_ids = sess_ids->next;
+                       if (purple_media_get_session_type(media, sess_id) == PURPLE_MEDIA_AUDIO) {
+                               ret = (*fn)(media, sess_id, user_data);
+                               if (ret)
+                                       return ret;
+                       }
+               }
+       }
+       return ret;
+}
+
+gboolean disconnect_stream(PurpleMedia *media, const gchar *sess_id, gpointer user_data)
+{
+       purple_debug(PURPLE_DEBUG_INFO, "headset", "disconnecting %s\n", sess_id);
+       purple_media_end(media, sess_id, NULL);
+       return FALSE;
+}
+
+void headset_connected(gpointer user_data, gboolean connected)
+{
+       if (!connected)
+               foreach_media(disconnect_stream, user_data);
+       /* Once we can handle ringing, we can do ACCEPT here too */
+}
+
+gboolean mute_stream(PurpleMedia *media, const gchar *sess_id, gpointer user_data)
+{
+       purple_debug(PURPLE_DEBUG_INFO, "headset", "muting %s\n", sess_id);
+       purple_media_stream_info(media, PURPLE_MEDIA_INFO_MUTE, sess_id, NULL, TRUE);
+       return FALSE;
+}
+
+gboolean unmute_stream(PurpleMedia *media, const gchar *sess_id, gpointer user_data)
+{
+       purple_debug(PURPLE_DEBUG_INFO, "headset", "unmuting %s\n", sess_id);
+       purple_media_stream_info(media, PURPLE_MEDIA_INFO_UNMUTE, sess_id, NULL, TRUE);
+       return FALSE;
+}
+
+void headset_muted(gpointer user_data, gboolean muted)
+{
+       if (muted)
+               foreach_media(mute_stream, user_data);
+       else
+               foreach_media(unmute_stream, user_data);
+}
+
+static void headset_media_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, gchar *id,
+                            const gchar *participant, gboolean local, gpointer user_data)
+{
+       if (id && purple_media_get_session_type(media, id) != PURPLE_MEDIA_AUDIO)
+               return;
+
+       gpointer klass = g_type_class_ref(purple_media_info_type_get_type());
+       GEnumValue *val = g_enum_get_value(klass, type);
+       purple_debug(PURPLE_DEBUG_INFO, "headset", "media stream-info: %s %s %s %d\n",
+                    val->value_name, id, participant, local);
+       g_type_class_unref(klass);
+
+       switch (type) {
+       case PURPLE_MEDIA_INFO_MUTE:
+               mute_headset(user_data, TRUE);
+               break;
+
+       case PURPLE_MEDIA_INFO_UNMUTE:
+               mute_headset(user_data, FALSE);
+               break;
+       }
+}
+
+static gboolean media_exists_fn(PurpleMedia *media, const gchar *sess_id, gpointer user_data)
+{
+       return TRUE;
+}
+
+
+static gboolean terminate_media(gpointer user_data)
+{
+       headset_connected(user_data, FALSE);
+       return FALSE;
+}
+
+static void headset_media_state_changed_cb(PurpleMedia *media, PurpleMediaState state, const gchar *id,
+                              const gchar *participant, gpointer user_data)
+{
+       if (id && purple_media_get_session_type(media, id) != PURPLE_MEDIA_AUDIO)
+               return;
+
+       gpointer klass = g_type_class_ref(purple_media_state_changed_get_type());
+       GEnumValue *val = g_enum_get_value(klass, state);
+       purple_debug(PURPLE_DEBUG_INFO, "headset", "media state-changed: %s %s %s\n",
+                    val->value_name, id, participant);
+       g_type_class_unref(klass);
+
+       switch (state) {
+       case PURPLE_MEDIA_STATE_NEW:
+               connect_headset(user_data, TRUE);
+               break;
+
+       case PURPLE_MEDIA_STATE_END:
+               if (!foreach_media(media_exists_fn, NULL))
+                       connect_headset(user_data, FALSE);
+               break;
+       }
+}
+
+
+static gboolean
+headset_init_media_cb(PurpleMediaManager *mgr, PurpleMedia *media, PurpleAccount *account,
+                     const gchar *remote_user, gpointer user_data)
+{
+       purple_debug(PURPLE_DEBUG_INFO, "headset", "media created for %s\n", remote_user);
+       g_signal_connect(media, "state-changed", G_CALLBACK(headset_media_state_changed_cb), user_data);
+       g_signal_connect(media, "stream-info", G_CALLBACK(headset_media_stream_info_cb), user_data);
+
+       return TRUE;
+}
+
+static gboolean
+headset_plugin_load(PurplePlugin *plugin)
+{
+       PurpleMediaManager *mgr = purple_media_manager_get();
+       g_signal_connect(mgr, "init-media", G_CALLBACK(headset_init_media_cb), NULL);
+
+       purple_debug(PURPLE_DEBUG_INFO, "headset", "headset plugin loaded.\n");
+       return TRUE;
+}
+
+static gboolean
+headset_plugin_unload(PurplePlugin *plugin)
+{
+       PurpleMediaManager *mgr = purple_media_manager_get();
+
+       g_signal_handlers_disconnect_matched(mgr, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK(headset_init_media_cb), NULL);
+
+       GList *medias = purple_media_manager_get_media(mgr);
+       gboolean ret = FALSE;
+
+       for (; medias; medias = medias->next) {
+               PurpleMedia *media = medias->data;
+
+               g_signal_handlers_disconnect_matched(media, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK(headset_media_state_changed_cb), NULL);
+               g_signal_handlers_disconnect_matched(media, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK(headset_media_stream_info_cb), NULL);
+
+       }
+       return ret;
+
+       purple_debug(PURPLE_DEBUG_INFO, "headset", "headset plugin unloaded.\n");
+       return TRUE;
+}
+
+static void
+headset_plugin_destroy(PurplePlugin *plugin)
+{
+}
+
+static PurplePluginInfo info =
+{
+       PURPLE_PLUGIN_MAGIC,
+       PURPLE_MAJOR_VERSION,                      /**<major version */
+       PURPLE_MINOR_VERSION,                      /**<minor version */
+       PURPLE_PLUGIN_STANDARD,                    /**< type */
+       NULL,                                      /**< ui_requirement */
+       0,                                         /**< flags */
+       NULL,                                      /**< dependencies */
+       PURPLE_PRIORITY_DEFAULT,                   /**< priority */
+       "headset",                                 /**< id */
+       "Headset",                                 /**< name */
+       PACKAGE_VERSION,                           /**< version */
+       "Headset control.",                        /**< summary */
+       "Controls headsets ",                      /**< description */
+       "David Woodhouse <dwmw2@infradead.org>",           /**< author */
+       "www.infradead.org",                       /**< homepage */
+       headset_plugin_load,                       /**< load */
+       headset_plugin_unload,             /**< unload */
+       headset_plugin_destroy,            /**< destroy */
+       NULL,                                      /**< ui_info */
+       NULL,                                      /**< extra_info */
+       NULL,                                      /**< prefs_info */
+       headset_plugin_actions,            /**< actions */
+
+       /* padding */
+       NULL,
+       NULL,
+       NULL,
+       NULL
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+}
+PURPLE_INIT_PLUGIN(headset_plugin, init_plugin, info)
diff --git a/headset.h b/headset.h
new file mode 100644 (file)
index 0000000..8e98030
--- /dev/null
+++ b/headset.h
@@ -0,0 +1,30 @@
+/*
+ * Pidgin plugin for USB headset management
+ *
+ * Copyright © 2018 David Woodhouse.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ */
+
+#ifndef __HEADSET_H__
+#define __HEADSET_H__
+
+/* headset.c */
+void headset_muted(gpointer user_data, gboolean mute);
+void headset_connected(gpointer user_data, gboolean connected);
+
+
+/* jabra.c */
+void mute_headset(gpointer user_data, gboolean muted);
+void connect_headset(gpointer user_data, gboolean connected);
+
+#endif /* __HEADSET_H__ */
diff --git a/jabra.c b/jabra.c
new file mode 100644 (file)
index 0000000..c9d54ea
--- /dev/null
+++ b/jabra.c
@@ -0,0 +1,32 @@
+/*
+ * Pidgin plugin for USB headset management
+ *
+ * Copyright © 2018 David Woodhouse.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ */
+
+#define PURPLE_PLUGINS
+#include <debug.h>
+
+#include "headset.h"
+
+void mute_headset(gpointer user_data, gboolean muted)
+{
+       purple_debug(PURPLE_DEBUG_INFO, "headset", "mute %d\n", muted);
+}
+
+void connect_headset(gpointer user_data, gboolean muted)
+{
+       purple_debug(PURPLE_DEBUG_INFO, "headset", "connect %d\n", muted);
+}
+
diff --git a/po/LINGUAS b/po/LINGUAS
new file mode 100644 (file)
index 0000000..daa2955
--- /dev/null
@@ -0,0 +1 @@
+en_GB
diff --git a/po/Makefile.am b/po/Makefile.am
new file mode 100644 (file)
index 0000000..90b5b4a
--- /dev/null
@@ -0,0 +1,41 @@
+
+LINGUAS = @LINGUAS@
+MOFILES = $(LINGUAS:%=%.mo)
+POFILES = $(LINGUAS:%=%.po)
+
+noinst_DATA = $(MOFILES)
+
+SUFFIXES = .mo
+
+.po.mo:
+       rm -f  && $(MSGFMT) -o $@ $<
+
+clean-local:
+       rm -f $(MOFILES)
+
+install-data-hook: all
+       linguas="$(LINGUAS)"; \
+       for l in $$linguas; do \
+         dir="$(DESTDIR)$(localedir)/$$l/LC_MESSAGES"; \
+         $(mkdir_p) $$dir; \
+         echo Installing $$l.mo to $$dir/$(PACKAGE).mo ; \
+         $(INSTALL_DATA) $$l.mo $$dir/$(PACKAGE).mo; \
+       done
+
+uninstall-hook:
+       linguas="$(LINGUAS)"; \
+       for l in $$linguas; do \
+         file="$(DESTDIR)$(localedir)/$$l/LC_MESSAGES/$(PACKAGE).mo"; \
+         if [ -r "$$file" ]; then \
+           echo "Removing $$file"; rm -f "$$file"; \
+         fi ; \
+       done
+
+# $(PACKAGE).pot is built by a rule in the parent directory Makefile
+# This rule isn't needed but is here for convenience if manually invoked
+.PHONY: $(PACKAGE).pot
+$(PACKAGE).pot:
+       $(MAKE) -C .. po/$@
+
+EXTRA_DIST = $(POFILES) LINGUAS
+DISTCLEANFILES=$(PACKAGE).pot