I have a headset (microsoft lifechat lx-3000) with an "answer" button on the cord, which I wanted to have work with linphone.

Because of reasons like:

* answer button press doesn't generate an actual X keyboard event (but is visible through /dev/hidrawX) * linphone is running on local desktop, but I'm working on a fullscreen remote desktop and all keyboard events get sent there

I decided to use a simple Unix signal IPC to hook into the linphone gtk main loop, as described in:

http://askra.de/software/gtk-signals/a2955.html

The implementation is basically:

* create a "self-pipe" during initialization
* create GIOChannel on "read" end of pipe, and add a watch to main loop
* unix signal handler writes "message" into pipe
* GIOChannel event reads "message" from pipe and calls linphone_gtk_answer_clicked(NULL), which already takes care of what to do if no call is currently ringing.

Last part is a perl script which reads bytes from /dev/hidrawX and looks for a specific byte-sequence and sends the "kill" to the pid of the running linphone. Perl script is currently hard-coded to hell, but I'll attach anyway for "documentation purposes". Note: requires "read" access to be granted on /dev/hidrawX device to current user before running. Note2: script also pauses playing rhythmbox on answer press.

--
Thanks,
David Mansfield
Cobite, INC.

--- linphone-3.5.2.orig/gtk/main.c      2012-12-26 10:29:12.521835222 -0500
+++ linphone-3.5.2/gtk/main.c   2012-12-26 11:38:40.804443105 -0500
@@ -1632,6 +1632,138 @@
 }
 #endif
 
+// unix signal handler
+
+static int signal_pipe[2];
+
+static void pipe_signals(int signal)
+{
+  if(write(signal_pipe[1], &signal, sizeof(int)) != sizeof(int))
+    {
+      fprintf(stderr, "unix signal %d lost\n", signal);
+    }
+}
+
+static gboolean deliver_signal(GIOChannel *source, GIOCondition cond, gpointer 
d)
+{
+  GError *error = NULL;                /* for error handling */
+
+  /* 
+   * There is no g_io_channel_read or g_io_channel_read_int, so we read
+   * char's and use a union to recover the unix signal number.
+   */
+  union {
+    gchar chars[sizeof(int)];
+    int signal;
+  } buf;
+  GIOStatus status;            /* save the reading status */
+  gsize bytes_read;            /* save the number of chars read */
+
+  /* 
+   * Read from the pipe as long as data is available. The reading end is 
+   * also in non-blocking mode, so if we have consumed all unix signals, 
+   * the read returns G_IO_STATUS_AGAIN. 
+   */
+  while((status = g_io_channel_read_chars(source, buf.chars, 
+                    sizeof(int), &bytes_read, &error)) == G_IO_STATUS_NORMAL)
+    {
+      g_assert(error == NULL); /* no error if reading returns normal */
+
+      /* 
+       * There might be some problem resulting in too few char's read.
+       * Check it.
+       */
+      if(bytes_read != sizeof(int)){
+       fprintf(stderr, "lost data in signal pipe (expected %d, received %d)\n",
+               (int)sizeof(int), (int)bytes_read);
+       continue;             /* discard the garbage and keep fingers crossed */
+      }
+
+      /* Ok, we read a unix signal number, answer the call (if any) */
+      fprintf(stderr, "===== received signal %d\n", buf.signal);
+      linphone_gtk_answer_clicked(NULL);
+    }
+  
+  /* 
+   * Reading from the pipe has not returned with normal status. Check for 
+   * potential errors and return from the callback.
+   */
+  if(error != NULL){
+    fprintf(stderr, "reading signal pipe failed: %s\n", error->message);
+    exit(1);
+  }
+  if(status == G_IO_STATUS_EOF){
+    fprintf(stderr, "signal pipe has been closed\n");
+    exit(1);
+  }
+
+  g_assert(status == G_IO_STATUS_AGAIN);
+
+  return (TRUE);               /* keep the event source */
+}
+
+static void setup_answer_handler() {
+  int fd_flags;
+  GIOChannel *g_signal_in; 
+  GError *error = NULL;        /* handle errors */
+
+  /* 
+   * Set the unix signal handling up.
+   * First create a pipe.
+   */
+  if(pipe(signal_pipe)) {
+    perror("pipe");
+    exit(1);
+  }
+
+  /* 
+   * put the write end of the pipe into nonblocking mode,
+   * need to read the flags first, otherwise we would clear other flags too.
+   */
+  fd_flags = fcntl(signal_pipe[1], F_GETFL);
+  if(fd_flags == -1)
+    {
+      perror("read descriptor flags");
+      exit(1);
+    }
+  if(fcntl(signal_pipe[1], F_SETFL, fd_flags | O_NONBLOCK) == -1)
+    {
+      perror("write descriptor flags");
+      exit(1);
+    }
+
+  /* Install the unix signal handler pipe_signals for the signals of interest 
*/
+  signal(SIGUSR1, pipe_signals);
+  
+  /* convert the reading end of the pipe into a GIOChannel */
+  g_signal_in = g_io_channel_unix_new(signal_pipe[0]);
+  /* 
+   * we only read raw binary data from the pipe, 
+   * therefore clear any encoding on the channel
+   */
+  g_io_channel_set_encoding(g_signal_in, NULL, &error);
+  if(error != NULL){           /* handle potential errors */
+    fprintf(stderr, "g_io_channel_set_encoding failed %s\n",
+           error->message);
+    exit(1);
+  }
+
+  /* put the reading end also into non-blocking mode */
+  g_io_channel_set_flags(g_signal_in,     
+                        g_io_channel_get_flags(g_signal_in) | 
G_IO_FLAG_NONBLOCK, &error);
+
+  if(error != NULL){           /* tread errors */
+    fprintf(stderr, "g_io_set_flags failed %s\n",
+           error->message);
+    exit(1);
+  }
+  
+  /* register the reading end with the event loop */
+  g_io_add_watch(g_signal_in, G_IO_IN | G_IO_PRI, deliver_signal, NULL);
+
+}
+
+
 int main(int argc, char *argv[]){
 #ifdef ENABLE_NLS
        void *p;
@@ -1764,6 +1896,8 @@
        if (linphone_gtk_get_ui_config_int("update_check_menu",0)==0)
                linphone_gtk_check_for_new_version();
 
+       setup_answer_handler();
+
        gtk_main();
        linphone_gtk_quit();
 #ifndef HAVE_GTK_OSX

Attachment: answer_linphone.pl
Description: Perl program

_______________________________________________
Linphone-users mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/linphone-users

Reply via email to