e: add automatic desktop lock/unlock when a bluetooth device disapear.
authorCedric Bail <cedric.bail@free.fr>
Sun, 17 Mar 2013 15:07:34 +0000 (00:07 +0900)
committerCedric Bail <cedric.bail@free.fr>
Sun, 17 Mar 2013 15:07:34 +0000 (00:07 +0900)
This use a custom implementation of l2ping to ping known device and do
proper action when they go away. I don't recommend to use the auto unlonck,
but it is there if someone need it.

configure.ac
data/etc/sysactions.conf.in
src/bin/Makefile.am
src/bin/e_sys_l2ping.c [new file with mode: 0644]
src/bin/e_sys_main.c
src/modules/bluez4/e_mod_main.c
src/modules/bluez4/e_mod_main.h
src/modules/bluez4/ebluez4.c

index 82c0a5b..b3dbcec 100644 (file)
@@ -197,9 +197,13 @@ CPPFLAGS="${PCPPFLAGS}"
 AC_SUBST(cf_cflags)
 AC_SUBST(cf_libs)
 
-AC_CHECK_HEADERS([bluetooth/bluetooth.h],
-       [have_bluetooth_h="yes"],
-       [have_bluetooth_h="no"])
+PKG_CHECK_MODULES([BLUEZ], [bluez],
+       [have_bluetooth="yes"],
+       [have_bluetooth="no"])
+AM_CONDITIONAL([HAVE_BLUETOOTH], [test "x${have_bluetooth}"])
+if test "x${have_bluetooth}"; then
+   AC_DEFINE_UNQUOTED([HAVE_BLUETOOTH], [1], [Bluetooth is there])
+fi
 
 execinfo_libs=""
 AC_CHECK_HEADERS([execinfo.h], [have_execinfo="yes"], [have_execinfo="no"])
@@ -487,6 +491,7 @@ PKG_CHECK_MODULES(E_FM_OPEN, [
 
 PKG_CHECK_MODULES(E_SYS, [
   eina >= ${efl_version}
+  ecore >= ${efl_version}
 ])
 
 PKG_CHECK_MODULES(E_INIT, [
index 6054a04..b0dfab5 100644 (file)
@@ -51,6 +51,7 @@ action:   /bin/mount /bin/mount
 action:   /bin/umount /bin/umount
 action:   /usr/bin/eject /usr/bin/eject
 action:   gdb       gdb
+action:   l2ping    l2ping
 
 # on FreeBSD use this instead of the above.
 #action suspend  /usr/sbin/zzz 
index fd930ef..dfdb96b 100644 (file)
@@ -422,10 +422,10 @@ e_fm_op.c
 enlightenment_fm_op_LDADD = @E_FM_OP_LIBS@ -lm
 
 enlightenment_sys_SOURCES = \
-e_sys_main.c
+e_sys_main.c e_sys_l2ping.c
 
-enlightenment_sys_LDADD = @SUID_LDFLAGS@ @E_SYS_LIBS@
-enlightenment_sys_CFLAGS = @SUID_CFLAGS@ @E_SYS_CFLAGS@
+enlightenment_sys_LDADD = @SUID_LDFLAGS@ @E_SYS_LIBS@ @BLUEZ_LIBS@
+enlightenment_sys_CFLAGS = @SUID_CFLAGS@ @E_SYS_CFLAGS@ @BLUEZ_CFLAGS@
 
 if HAVE_EEZE
 enlightenment_backlight_SOURCES = \
diff --git a/src/bin/e_sys_l2ping.c b/src/bin/e_sys_l2ping.c
new file mode 100644 (file)
index 0000000..ac826ea
--- /dev/null
@@ -0,0 +1,101 @@
+#include "config.h"
+
+#include <Ecore.h>
+
+#ifdef HAVE_BLUETOOTH
+#include <unistd.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#endif
+
+double
+e_sys_l2ping(const char *bluetooth_mac)
+{
+#ifdef HAVE_BLUETOOTH
+   char send_buf[L2CAP_CMD_HDR_SIZE + 1];
+   char recv_buf[L2CAP_CMD_HDR_SIZE + 1];
+   char tmp[18];
+   l2cap_cmd_hdr *send_cmd;
+   l2cap_cmd_hdr *recv_cmd;
+   struct sockaddr_l2 addr;
+   struct timeval tv;
+   socklen_t optlen;
+   fd_set rfds;
+   double start;
+   int fd;
+
+   /* Create socket */
+   fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+   if (fd < 0) {
+      perror("Can't create socket");
+      return -1;
+   }
+
+   /* Bind to local address */
+   memset(&addr, 0, sizeof(addr));
+   addr.l2_family = AF_BLUETOOTH;
+   bacpy(&addr.l2_bdaddr, BDADDR_ANY);
+
+   if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+     {
+        perror("Can't bind socket");
+       return -1;
+     }
+
+   /* Connect to remote device */
+   memset(&addr, 0, sizeof(addr));
+   addr.l2_family = AF_BLUETOOTH;
+   str2ba(bluetooth_mac, &addr.l2_bdaddr);
+
+   if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+     {
+        perror("Can't connect");
+       return -1;
+     }
+
+   start = ecore_time_get();
+
+   /* Get local address */
+   memset(&addr, 0, sizeof(addr));
+   optlen = sizeof(addr);
+
+   if (getsockname(fd, (struct sockaddr *) &addr, &optlen) < 0)
+     {
+        perror("Can't get local address");
+       return -1;
+     }
+
+   ba2str(&addr.l2_bdaddr, tmp);
+
+   send_cmd = (l2cap_cmd_hdr *) send_buf;
+   send_cmd->ident = 200;
+   send_cmd->len = htobs(1);
+   send_cmd->code = L2CAP_ECHO_REQ;
+   send_buf[L2CAP_CMD_HDR_SIZE] = 'A';
+
+   if (send(fd, send_buf, L2CAP_CMD_HDR_SIZE + 1, 0) <= 0)
+     {
+        perror("Send failed");
+       return -1;
+     }
+
+   if (recv(fd, recv_buf, L2CAP_CMD_HDR_SIZE + 1, 0) < 0)
+     {
+        perror("Recv failed");
+       return -1;
+     }
+
+   recv_cmd = (l2cap_cmd_hdr *) recv_buf;
+   recv_cmd->len = btohs(recv_cmd->len);
+   if (recv_cmd->ident != 200)
+     return -1; /* Wrong packet */
+
+   close(fd);
+
+   return ecore_time_get() - start;
+#else
+   fprintf(stderr, "e_sys_l2ping nop\n");
+   return -1;
+#endif
+}
index 747836d..1805706 100644 (file)
@@ -16,6 +16,8 @@
 #endif
 #include <Eina.h>
 
+double e_sys_l2ping(const char *bluetooth_mac);
+
 /* local subsystem functions */
 #ifdef HAVE_EEZE_MOUNT
 static Eina_Bool mountopts_check(const char *opts);
@@ -88,6 +90,11 @@ main(int argc,
 
              output = argv[3];
           }
+       else if (!strcmp(argv[1], "l2ping"))
+         {
+            action = argv[1];
+            output = argv[2];
+         }
 #ifdef HAVE_EEZE_MOUNT
         else
           {
@@ -173,6 +180,18 @@ main(int argc,
 
         exit(WEXITSTATUS(r));
      }
+   else if (!test && !strcmp(action, "l2ping"))
+     {
+        char tmp[128];
+       double latency;
+
+       latency = e_sys_l2ping(output);
+
+       eina_convert_dtoa(latency, tmp);
+       fprintf(stdout, tmp);
+
+       return (latency < 0) ? 1 : 0;
+     }
    if ((!test)
 #ifdef HAVE_EEZE_MOUNT
        && (!mnt)
index f294ae9..594a3a0 100644 (file)
@@ -4,6 +4,14 @@
 #include "ebluez4.h"
 
 /* Local Variables */
+static Ecore_Exe *autolock_exe = NULL;
+static Ecore_Poller *autolock_poller = NULL;
+static Ecore_Event_Handler *autolock_die = NULL;
+static Ecore_Event_Handler *autolock_out = NULL;
+static Ecore_Event_Handler *autolock_desklock = NULL;
+static Eina_Bool autolock_initted = EINA_FALSE;
+static Eina_Bool autolock_waiting = EINA_TRUE;
+
 static Eina_List *instances = NULL;
 static E_Module *mod = NULL;
 static char tmpbuf[1024];
@@ -15,6 +23,42 @@ Config *ebluez4_config = NULL;
 EAPI E_Module_Api e_modapi = {E_MODULE_API_VERSION, "Bluez4"};
 
 /* Local Functions */
+static Eina_Bool
+_ebluez_l2ping_poller(void *data EINA_UNUSED)
+{
+   Eina_Strbuf *buf;
+   const char *tmp = NULL;
+
+   autolock_poller = NULL;
+
+   buf = eina_strbuf_new();
+   if (e_desklock_state_get())
+     {
+       if (!autolock_waiting)
+         tmp = ebluez4_config->unlock_dev_addr;
+       else
+         tmp = ebluez4_config->lock_dev_addr;
+     }
+   else
+     {
+        if (!autolock_waiting)
+         tmp = ebluez4_config->lock_dev_addr;
+       else
+         tmp = ebluez4_config->unlock_dev_addr;
+     }
+
+   if (tmp)
+     {
+        eina_strbuf_append_printf(buf, "%s/enlightenment/utils/enlightenment_sys l2ping %s",
+                                 e_prefix_lib_get(), tmp);
+       autolock_exe = ecore_exe_run(eina_strbuf_string_get(buf), NULL);
+     }
+
+   eina_strbuf_free(buf);
+
+   return 0;
+}
+
 static void
 _ebluez4_search_dialog_del(Instance *inst)
 {
@@ -262,9 +306,15 @@ _ebluez4_cb_lock(void *data,
    int tog;
 
    tog = e_menu_item_toggle_get(mi);
-   eina_stringshare_replace(&ebluez4_config->lock_dev_name,
-                           tog ? dev->name : NULL);
+   eina_stringshare_replace(&ebluez4_config->lock_dev_addr,
+                           tog ? dev->addr : NULL);
    e_config_save_queue();
+
+   if (autolock_exe)
+     ecore_exe_kill(autolock_exe);
+   autolock_exe = NULL;
+   if (!autolock_poller && (ebluez4_config->lock_dev_addr || ebluez4_config->unlock_dev_addr))
+     autolock_poller = ecore_poller_add(ECORE_POLLER_CORE, 32, _ebluez_l2ping_poller, NULL);
 }
 
 static void
@@ -276,9 +326,15 @@ _ebluez4_cb_unlock(void *data,
    int tog;
 
    tog = e_menu_item_toggle_get(mi);
-   eina_stringshare_replace(&ebluez4_config->unlock_dev_name,
-                           tog ? dev->name : NULL);
+   eina_stringshare_replace(&ebluez4_config->unlock_dev_addr,
+                           tog ? dev->addr : NULL);
    e_config_save_queue();
+
+   if (autolock_exe)
+     ecore_exe_kill(autolock_exe);
+   autolock_exe = NULL;
+   if (!autolock_poller && (ebluez4_config->lock_dev_addr || ebluez4_config->unlock_dev_addr))
+     autolock_poller = ecore_poller_add(ECORE_POLLER_CORE, 32, _ebluez_l2ping_poller, NULL);
 }
 
 static void
@@ -348,22 +404,27 @@ _ebluez4_add_devices(Instance *inst)
           e_menu_item_label_set(submi, "Forget");
           e_menu_item_callback_set(submi, _ebluez4_cb_forget, dev);
 
-         /* Auto lock when away */
-         submi = e_menu_item_new(subm);
-         e_menu_item_check_set(submi, 1);
-         e_menu_item_label_set(submi, "Lock on disconnect");
-         e_menu_item_callback_set(submi, _ebluez4_cb_lock, dev);
-         chk = ebluez4_config->lock_dev_name && dev->name &&
-           !strcmp(dev->name, ebluez4_config->lock_dev_name);
-         e_menu_item_toggle_set(submi, !!chk);
-
-         submi = e_menu_item_new(subm);
-         e_menu_item_check_set(submi, 1);
-         e_menu_item_label_set(submi, "Unlock on disconnect");
-         e_menu_item_callback_set(submi, _ebluez4_cb_unlock, dev);
-         chk = ebluez4_config->unlock_dev_name && dev->name &&
-           !strcmp(dev->name, ebluez4_config->unlock_dev_name);
-         e_menu_item_toggle_set(submi, !!chk);
+#ifdef HAVE_BLUETOOTH
+         if (autolock_initted)
+           {
+             /* Auto lock when away */
+             submi = e_menu_item_new(subm);
+             e_menu_item_check_set(submi, 1);
+             e_menu_item_label_set(submi, "Lock on disconnect");
+             e_menu_item_callback_set(submi, _ebluez4_cb_lock, dev);
+             chk = ebluez4_config->lock_dev_addr && dev->addr &&
+               !strcmp(dev->addr, ebluez4_config->lock_dev_addr);
+             e_menu_item_toggle_set(submi, !!chk);
+
+             submi = e_menu_item_new(subm);
+             e_menu_item_check_set(submi, 1);
+             e_menu_item_label_set(submi, "Unlock on disconnect");
+             e_menu_item_callback_set(submi, _ebluez4_cb_unlock, dev);
+             chk = ebluez4_config->unlock_dev_addr && dev->addr &&
+               !strcmp(dev->addr, ebluez4_config->unlock_dev_addr);
+             e_menu_item_toggle_set(submi, !!chk);
+           }
+#endif
        }
 
    return ret;
@@ -520,10 +581,93 @@ static const E_Gadcon_Client_Class _gc_class =
    E_GADCON_CLIENT_STYLE_PLAIN
 };
 
+static Eina_Bool
+_ebluez_exe_die(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event_info)
+{
+   Ecore_Exe_Event_Del *ev = event_info;
+
+   if (ev->exe != autolock_exe)
+     return ECORE_CALLBACK_PASS_ON;
+
+   if (!autolock_initted)
+     {
+        if (ev->exit_code == 0)
+         {
+            autolock_initted = EINA_TRUE;
+         }
+     }
+   else
+     {
+        if (e_desklock_state_get()) // Locked state ?
+         {
+            if (!autolock_waiting)
+              {
+                 // Not waiting yet for the auto unlock device to appear before unlock
+                 if (ev->exit_code == 0 && ebluez4_config->unlock_dev_addr)
+                   {
+                      e_desklock_hide();
+                   }
+              }
+            else if (ev->exit_code == 1)
+              {
+                 // The device just disapeared, now we can wait for it to disapear
+                 autolock_waiting = EINA_FALSE;
+              }
+         }
+       else
+         {
+           if (!autolock_waiting)
+             {
+                // Not waiting yet for the auto lock device to disappear before locking
+                if (ev->exit_code == 1 && ebluez4_config->lock_dev_addr)
+                  {
+                     e_desklock_show(EINA_FALSE);
+                  }
+             }
+           else if (ev->exit_code == 0)
+             {
+                // The device just appeared, now we can wait for it to disapear
+                autolock_waiting = EINA_FALSE;
+             }
+         }
+     }
+
+   if (autolock_initted)
+     autolock_poller = ecore_poller_add(ECORE_POLLER_CORE, 32, _ebluez_l2ping_poller, NULL);
+
+   autolock_exe = NULL;
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_ebluez_exe_out(void *data, int ev_type, void *ev)
+{
+   /* FIXME: Need experiment, but we should be able to use latency to somehow estimate distance, right ? */
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_ebluez_desklock(void *data, int ev_type, void *ev)
+{
+   if (autolock_exe)
+     ecore_exe_kill(autolock_exe);
+   autolock_exe = NULL;
+
+   if (!autolock_poller && autolock_initted && (ebluez4_config->lock_dev_addr || ebluez4_config->unlock_dev_addr))
+     autolock_poller = ecore_poller_add(ECORE_POLLER_CORE, 32, _ebluez_l2ping_poller, NULL);
+
+   autolock_waiting = EINA_TRUE;
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
 /* Module Functions */
 EAPI void *
 e_modapi_init(E_Module *m)
 {
+   Eina_Strbuf *buf;
+
    mod = m;
 
    conf_edd = E_CONFIG_DD_NEW("Config", Config);
@@ -531,8 +675,8 @@ e_modapi_init(E_Module *m)
 #undef D           
 #define T Config   
 #define D conf_edd 
-   E_CONFIG_VAL(D, T, lock_dev_name, STR);
-   E_CONFIG_VAL(D, T, unlock_dev_name, STR);
+   E_CONFIG_VAL(D, T, lock_dev_addr, STR);
+   E_CONFIG_VAL(D, T, unlock_dev_addr, STR);
 
    ebluez4_config = e_config_domain_load("module.ebluez4", conf_edd);
    if (!ebluez4_config)
@@ -542,6 +686,16 @@ e_modapi_init(E_Module *m)
 
    e_gadcon_provider_register(&_gc_class);
 
+   autolock_die = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _ebluez_exe_die, NULL);
+   autolock_out = ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _ebluez_exe_out, NULL);
+   autolock_desklock = ecore_event_handler_add(E_EVENT_DESKLOCK, _ebluez_desklock, NULL);
+
+   buf = eina_strbuf_new();
+   eina_strbuf_append_printf(buf, "%s/enlightenment/utils/enlightenment_sys -t l2ping",
+                            e_prefix_lib_get());
+   autolock_exe = ecore_exe_run(eina_strbuf_string_get(buf), NULL);
+   eina_strbuf_free(buf);
+
    return m;
 }
 
@@ -550,8 +704,17 @@ e_modapi_shutdown(E_Module *m)
 {
    E_CONFIG_DD_FREE(conf_edd);
 
-   eina_stringshare_del(ebluez4_config->lock_dev_name);
-   eina_stringshare_del(ebluez4_config->unlock_dev_name);
+   if (autolock_exe) ecore_exe_kill(autolock_exe);
+   autolock_exe = NULL;
+   if (autolock_poller) ecore_timer_del(autolock_poller);
+   autolock_poller = NULL;
+
+   ecore_event_handler_del(autolock_die);
+   ecore_event_handler_del(autolock_out);
+   ecore_event_handler_del(autolock_desklock);
+
+   eina_stringshare_del(ebluez4_config->lock_dev_addr);
+   eina_stringshare_del(ebluez4_config->unlock_dev_addr);
    free(ebluez4_config);
    ebluez4_config = NULL;
 
index c8d713b..dc48b3d 100644 (file)
@@ -16,8 +16,8 @@ struct _Instance
 typedef struct _Config Config;
 struct _Config
 {
-   const char *lock_dev_name;
-   const char *unlock_dev_name;
+   const char *lock_dev_addr;
+   const char *unlock_dev_addr;
 };
 
 extern Config *ebluez4_config;
index 2347808..e10ccbe 100644 (file)
@@ -385,10 +385,6 @@ _on_removed(void *context, const EDBus_Message *msg)
    fdev = eina_list_search_unsorted(ctxt->found_devices, _dev_addr_cmp,
                                     dev->addr);
 
-   if (dev->name && ebluez4_config->lock_dev_name &&
-       !strcmp(dev->name, ebluez4_config->lock_dev_name))
-     e_desklock_show(EINA_FALSE);
-
    _unset_dev(dev, &ctxt->devices);
    _unset_dev(fdev, &ctxt->found_devices);
 }
@@ -427,10 +423,6 @@ _on_device_found(void *context, const EDBus_Message *msg)
    dev->paired = paired;
    ctxt->found_devices = eina_list_append(ctxt->found_devices, dev);
 
-   if (dev->name && ebluez4_config->unlock_dev_name &&
-       !strcmp(dev->name, ebluez4_config->unlock_dev_name))
-     e_desklock_hide();
-
    ebluez4_update_instances(ctxt->found_devices);
 }