6 * - check why return NULL from method call triggers cancel error
8 * - find out why alias == address in _bluez_request_pincode_cb
9 * - more complete agent support (handle requests from devices)
10 * - handle device-disappeared events
11 * - icon with device state (trusted, connected, paired)
15 * - configure (probably module) timeout to trigger automatic rescan.
16 * - gadgets to show different adapters (see mixer module configuration)
17 * - module to choose the default adapter (see mixer module configuration)
18 * - icon with device class
21 #include "e_mod_main.h"
23 static E_Module *bluez_mod = NULL;
24 static char tmpbuf[PATH_MAX]; /* general purpose buffer, just use immediately */
26 static const char _e_bluez_agent_path[] = "/org/enlightenment/bluez/Agent";
27 const char _e_bluez_name[] = "bluez";
28 const char _e_bluez_Name[] = "Bluetooth Manager";
29 int _e_bluez_log_dom = -1;
31 static void _bluez_gadget_update(E_Bluez_Instance *inst);
32 static void _bluez_tip_update(E_Bluez_Instance *inst);
33 static void _bluez_popup_update(E_Bluez_Instance *inst);
35 struct bluez_pincode_data
37 void (*cb)(struct bluez_pincode_data *d);
39 E_Bluez_Module_Context *ctxt;
48 e_bluez_theme_path(void)
50 #define TF "/e-module-bluez.edj"
53 dirlen = strlen(bluez_mod->dir);
54 if (dirlen >= sizeof(tmpbuf) - sizeof(TF))
57 memcpy(tmpbuf, bluez_mod->dir, dirlen);
58 memcpy(tmpbuf + dirlen, TF, sizeof(TF));
65 _bluez_devices_clear(E_Bluez_Instance *inst)
67 E_Bluez_Instance_Device *d;
68 EINA_LIST_FREE(inst->devices, d)
70 eina_stringshare_del(d->address);
71 eina_stringshare_del(d->alias);
79 _bluez_discovery_cb(void *data,
80 DBusMessage *msg __UNUSED__,
83 E_Bluez_Instance *inst = data;
86 if (error && dbus_error_is_set(error))
88 _bluez_dbus_error_show(_("Cannot change adapter's discovery."), error);
89 dbus_error_free(error);
93 inst->discovering = !inst->discovering;
95 label = !inst->discovering ? _("Start Scan") : _("Stop Scan");
96 e_widget_button_label_set(inst->ui.button, label);
100 _bluez_create_paired_device_cb(void *data,
101 DBusMessage *msg __UNUSED__,
104 const char *alias = data;
106 if (error && dbus_error_is_set(error))
108 if (strcmp(error->name, "org.bluez.Error.AlreadyExists") != 0)
109 _bluez_dbus_error_show(_("Cannot pair with device."), error);
110 dbus_error_free(error);
111 eina_stringshare_del(alias);
116 (_("Bluetooth Manager"), _("Device '%s' successfully paired."), alias);
117 eina_stringshare_del(alias);
121 _bluez_toggle_powered_cb(void *data,
122 DBusMessage *msg __UNUSED__,
125 E_Bluez_Instance *inst = data;
127 if ((!error) || (!dbus_error_is_set(error)))
129 inst->powered_pending = EINA_FALSE;
130 inst->powered = !inst->powered;
134 _bluez_devices_clear(inst);
137 _bluez_popup_update(inst);
140 _bluez_gadget_update(inst);
144 _bluez_dbus_error_show(_("Cannot toggle adapter's powered."), error);
145 dbus_error_free(error);
149 _bluez_toggle_powered(E_Bluez_Instance *inst)
153 if ((!inst) || (!inst->ctxt->has_manager))
155 _bluez_operation_error_show(_("BlueZ Daemon is not running."));
161 _bluez_operation_error_show(_("No bluetooth adapter."));
165 if (!e_bluez_adapter_powered_get(inst->adapter, &powered))
167 _bluez_operation_error_show(_("Query adapter's powered."));
173 if (!e_bluez_adapter_powered_set
174 (inst->adapter, powered, _bluez_toggle_powered_cb, inst))
176 _bluez_operation_error_show(_("Query adapter's powered."));
182 _bluez_cb_toggle_powered(E_Object *obj __UNUSED__,
183 const char *params __UNUSED__)
185 E_Bluez_Module_Context *ctxt;
187 E_Bluez_Instance *inst;
192 ctxt = bluez_mod->data;
193 EINA_LIST_FOREACH(ctxt->instances, l, inst)
194 if (inst->adapter) _bluez_toggle_powered(inst);
197 static void _bluez_popup_del(E_Bluez_Instance *inst);
200 _bluez_popup_input_window_mouse_up_cb(void *data,
204 Ecore_Event_Mouse_Button *ev = event;
205 E_Bluez_Instance *inst = data;
207 if (ev->window != inst->ui.input.win)
208 return ECORE_CALLBACK_PASS_ON;
210 _bluez_popup_del(inst);
212 return ECORE_CALLBACK_PASS_ON;
216 _bluez_popup_input_window_key_down_cb(void *data,
220 Ecore_Event_Key *ev = event;
221 E_Bluez_Instance *inst = data;
224 if (ev->window != inst->ui.input.win)
225 return ECORE_CALLBACK_PASS_ON;
228 if (strcmp(keysym, "Escape") == 0)
229 _bluez_popup_del(inst);
231 return ECORE_CALLBACK_PASS_ON;
235 _bluez_popup_input_window_destroy(E_Bluez_Instance *inst)
237 ecore_x_window_free(inst->ui.input.win);
238 inst->ui.input.win = 0;
240 ecore_event_handler_del(inst->ui.input.mouse_up);
241 inst->ui.input.mouse_up = NULL;
243 ecore_event_handler_del(inst->ui.input.key_down);
244 inst->ui.input.key_down = NULL;
248 _bluez_popup_input_window_create(E_Bluez_Instance *inst)
250 Ecore_X_Window_Configure_Mask mask;
251 Ecore_X_Window w, popup_w;
254 man = e_manager_current_get();
256 w = ecore_x_window_input_new(man->root, 0, 0, man->w, man->h);
257 mask = (ECORE_X_WINDOW_CONFIGURE_MASK_STACK_MODE |
258 ECORE_X_WINDOW_CONFIGURE_MASK_SIBLING);
259 popup_w = inst->popup->win->evas_win;
260 ecore_x_window_configure(w, mask, 0, 0, 0, 0, 0, popup_w,
261 ECORE_X_WINDOW_STACK_BELOW);
262 ecore_x_window_show(w);
264 inst->ui.input.mouse_up =
265 ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP,
266 _bluez_popup_input_window_mouse_up_cb, inst);
268 inst->ui.input.key_down =
269 ecore_event_handler_add(ECORE_EVENT_KEY_DOWN,
270 _bluez_popup_input_window_key_down_cb, inst);
272 inst->ui.input.win = w;
276 _bluez_popup_cb_powered_changed(void *data,
279 E_Bluez_Instance *inst = data;
280 E_Bluez_Module_Context *ctxt = inst->ctxt;
281 Eina_Bool powered = e_widget_check_checked_get(obj);
283 if ((!ctxt) || (!ctxt->has_manager))
285 _bluez_operation_error_show(_("BlueZ Daemon is not running."));
291 _bluez_operation_error_show(_("No bluetooth adapter."));
295 if (!e_bluez_adapter_powered_set
296 (inst->adapter, powered, _bluez_toggle_powered_cb, inst))
298 _bluez_operation_error_show
299 (_("Cannot toggle adapter's powered."));
303 inst->powered_pending = EINA_TRUE;
307 _bluez_pincode_ask_cb(struct bluez_pincode_data *d)
313 e_util_dialog_show(_("Bluetooth Manager"), _("Invalid Pin Code."));
317 reply = dbus_message_new_method_return(d->msg);
318 dbus_message_append_args
319 (reply, DBUS_TYPE_STRING, &d->pincode, DBUS_TYPE_INVALID);
321 dbus_message_set_no_reply(reply, EINA_TRUE);
322 e_dbus_message_send(d->ctxt->agent.conn, reply, NULL, -1, NULL);
326 bluez_pincode_ask_ok(void *data,
329 struct bluez_pincode_data *d = data;
330 d->canceled = EINA_FALSE;
331 e_object_del(E_OBJECT(dia));
335 bluez_pincode_ask_cancel(void *data,
338 struct bluez_pincode_data *d = data;
339 d->canceled = EINA_TRUE;
340 e_object_del(E_OBJECT(dia));
344 bluez_pincode_ask_del(void *data)
346 E_Dialog *dia = data;
347 struct bluez_pincode_data *d = e_object_data_get(E_OBJECT(dia));
352 d->ctxt->agent.pending = eina_list_remove(d->ctxt->agent.pending, dia);
355 dbus_message_unref(d->msg);
356 eina_stringshare_del(d->alias);
361 bluez_pincode_ask_key_down(void *data,
363 Evas_Object *o __UNUSED__,
366 Evas_Event_Key_Down *ev = event;
367 struct bluez_pincode_data *d = data;
369 if (strcmp(ev->keyname, "Return") == 0)
370 bluez_pincode_ask_ok(d, d->dia);
371 else if (strcmp(ev->keyname, "Escape") == 0)
372 bluez_pincode_ask_cancel(d, d->dia);
376 bluez_pincode_ask(void (*cb)(struct bluez_pincode_data *),
379 E_Bluez_Module_Context *ctxt)
381 struct bluez_pincode_data *d;
382 Evas_Object *list, *o;
390 d = E_NEW(struct bluez_pincode_data, 1);
396 d->alias = eina_stringshare_add(alias);
397 d->msg = dbus_message_ref(msg);
398 d->canceled = EINA_TRUE; /* closing the dialog defaults to cancel */
399 d->dia = e_dialog_new(NULL, "E", "bluez_ask_pincode");
401 snprintf(buf, sizeof(buf), _("Pairing with device '%s'"), alias);
402 e_dialog_title_set(d->dia, buf);
403 e_dialog_icon_set(d->dia, "dialog-ask", 32);
404 e_dialog_border_icon_set(d->dia, "dialog-ask");
406 evas = d->dia->win->evas;
408 list = e_widget_list_add(evas, 0, 0);
410 o = edje_object_add(evas);
411 e_theme_edje_object_set(o, "base/theme/dialog",
412 "e/widgets/dialog/text");
413 snprintf(buf, sizeof(buf),
414 _("Enter the PIN code: "));
415 edje_object_part_text_set(o, "e.textblock.message", buf);
416 edje_object_size_min_calc(o, &mw, &mh);
417 evas_object_size_hint_min_set(o, mw, mh);
418 evas_object_resize(o, mw, mh);
420 e_widget_list_object_append(list, o, 1, 1, 0.5);
422 d->entry = o = e_widget_entry_add(evas, &d->pincode, NULL, NULL, NULL);
423 e_widget_entry_password_set(o, 0);
425 e_widget_list_object_append(list, o, 1, 0, 0.0);
427 e_widget_size_min_get(list, &mw, &mh);
432 e_dialog_content_set(d->dia, list, mw, mh);
435 (d->dia, _("Ok"), NULL, bluez_pincode_ask_ok, d);
437 (d->dia, _("Cancel"), NULL, bluez_pincode_ask_cancel, d);
439 evas_object_event_callback_add
440 (d->dia->bg_object, EVAS_CALLBACK_KEY_DOWN,
441 bluez_pincode_ask_key_down, d);
443 e_object_del_attach_func_set
444 (E_OBJECT(d->dia), bluez_pincode_ask_del);
445 e_object_data_set(E_OBJECT(d->dia), d);
447 e_dialog_button_focus_num(d->dia, 0);
448 e_widget_focus_set(d->entry, 1);
450 e_win_centered_set(d->dia->win, 1);
451 e_dialog_show(d->dia);
453 ctxt->agent.pending = eina_list_append(ctxt->agent.pending, d->dia);
457 _bluez_request_pincode_cb(E_DBus_Object *obj,
460 E_Bluez_Module_Context *ctxt = e_dbus_object_data_get(obj);
461 E_Bluez_Element *element;
465 // TODO: seems that returning NULL is causing pin code rquest to be canceled!
467 if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
468 DBUS_TYPE_INVALID) == FALSE)
471 element = e_bluez_device_get(path);
476 if (!e_bluez_device_alias_get(element, &alias))
478 if (!e_bluez_device_name_get(element, &alias))
482 // TODO: find out why alias == address, then remove debug:
483 fprintf(stderr, ">>> request pin code of '%s' (%s)\n", alias, path);
484 bluez_pincode_ask(_bluez_pincode_ask_cb, msg, alias, ctxt);
489 _bluez_popup_cb_scan(void *data,
490 void *data2 __UNUSED__)
492 E_Bluez_Instance *inst = data;
497 else if (inst->discovering)
498 ret = e_bluez_adapter_stop_discovery
499 (inst->adapter, _bluez_discovery_cb, inst);
502 inst->last_scan = ecore_loop_time_get();
504 _bluez_devices_clear(inst);
506 ret = e_bluez_adapter_start_discovery
507 (inst->adapter, _bluez_discovery_cb, inst);
509 _bluez_popup_update(inst);
513 ERR("Failed on discovery procedure");
517 _bluez_popup_cb_controls(void *data,
518 void *data2 __UNUSED__)
520 E_Bluez_Instance *inst = data;
522 _bluez_popup_del(inst);
523 if (inst->conf_dialog)
527 inst->conf_dialog = e_bluez_config_dialog_new(NULL, inst);
531 _bluez_popup_device_selected(void *data)
533 E_Bluez_Instance *inst = data;
534 const char *address = inst->address;
536 const char *cap = "DisplayYesNo";
537 const E_Bluez_Instance_Device *d;
541 _bluez_popup_del(inst);
545 ERR("no device selected for pairing.");
549 inst->alias = address;
550 EINA_LIST_FOREACH(inst->devices, l, d)
552 if (address == d->alias)
554 inst->alias = d->alias;
561 ERR("device %s does not have an alias.", address);
565 alias = eina_stringshare_ref(inst->alias);
566 if (!e_bluez_adapter_create_paired_device
567 (inst->adapter, _e_bluez_agent_path, cap, address,
568 _bluez_create_paired_device_cb, alias))
570 eina_stringshare_del(alias);
576 _bluez_event_devicefound(void *data,
580 E_Bluez_Module_Context *ctxt = data;
581 E_Bluez_Device_Found *device = event;
582 E_Bluez_Instance *inst;
583 const Eina_List *l_inst;
586 // TODO: get properties such as paired, connected, trusted, class, icon...
587 // TODO: check if the adapter contains device->name and if so get path.
589 alias = e_bluez_devicefound_alias_get(device);
591 EINA_LIST_FOREACH(ctxt->instances, l_inst, inst)
593 const Eina_List *l_dev;
594 E_Bluez_Instance_Device *dev;
595 Eina_Bool found = EINA_FALSE;
597 if (inst->adapter != device->adapter) continue;
599 EINA_LIST_FOREACH(inst->devices, l_dev, dev)
601 if (dev->address == device->name)
610 dev = malloc(sizeof(E_Bluez_Instance_Device));
613 dev->address = eina_stringshare_ref(device->name);
614 dev->alias = eina_stringshare_ref(alias);
616 inst->devices = eina_list_append(inst->devices, dev);
620 e_widget_ilist_append
621 (inst->ui.list, NULL, dev->alias,
622 _bluez_popup_device_selected, inst, dev->address);
623 e_widget_ilist_go(inst->ui.list);
631 _bluez_popup_update(E_Bluez_Instance *inst)
633 Evas_Object *list = inst->ui.list;
636 E_Bluez_Instance_Device *d;
639 /* TODO: replace this with a scroller + list of edje
640 * objects that are more full of features
642 selected = e_widget_ilist_selected_get(list);
643 e_widget_ilist_freeze(list);
644 e_widget_ilist_clear(list);
646 EINA_LIST_FOREACH(inst->devices, l, d)
648 e_widget_ilist_append
649 (inst->ui.list, NULL, d->alias,
650 _bluez_popup_device_selected, inst, d->address);
655 inst->first_selection = EINA_TRUE;
656 e_widget_ilist_selected_set(list, selected);
659 inst->first_selection = EINA_FALSE;
661 e_widget_ilist_go(list);
663 e_widget_check_checked_set(inst->ui.powered, inst->powered);
664 label = inst->discovering ? _("Stop Scan") : _("Start Scan");
665 e_widget_button_label_set(inst->ui.button, label);
666 e_widget_disabled_set(inst->ui.button, !inst->powered);
670 _bluez_popup_del(E_Bluez_Instance *inst)
672 _bluez_popup_input_window_destroy(inst);
673 e_object_del(E_OBJECT(inst->popup));
678 _bluez_popup_new(E_Bluez_Instance *inst)
684 Eina_Bool b, needs_scan = EINA_FALSE;
688 e_gadcon_popup_show(inst->popup);
694 _bluez_operation_error_show(_("No bluetooth adapter."));
698 if (!e_bluez_adapter_discovering_get(inst->adapter, &b))
700 _bluez_operation_error_show(_("Can't get Discovering property"));
703 inst->discovering = b;
704 // maybe auto-scan if did not in the last 30 minutes?
705 // seems scan will hurt things like bluetooth audio playback, so don't do it
706 if ((!inst->discovering) && (inst->last_scan <= 0.0) && (inst->ui.powered))
708 label = _("Stop Scan");
709 needs_scan = EINA_TRUE;
712 label = inst->discovering ? _("Stop Scan") : _("Start Scan");
714 inst->popup = e_gadcon_popup_new(inst->gcc);
715 evas = inst->popup->win->evas;
717 ol = e_widget_list_add(evas, 0, 0);
719 // TODO: get this size from edj
720 inst->ui.list = e_widget_ilist_add(evas, 32, 32, &inst->address);
721 e_widget_size_min_set(inst->ui.list, 180, 100);
722 e_widget_list_object_append(ol, inst->ui.list, 1, 1, 0.5);
724 inst->powered = inst->powered;
725 inst->ui.powered = e_widget_check_add(evas, _("Powered"), &inst->powered);
726 e_widget_on_change_hook_set
727 (inst->ui.powered, _bluez_popup_cb_powered_changed, inst);
728 e_widget_list_object_append(ol, inst->ui.powered, 1, 0, 0.5);
730 inst->ui.button = e_widget_button_add
731 (evas, label, NULL, _bluez_popup_cb_scan, inst, NULL);
732 e_widget_list_object_append(ol, inst->ui.button, 1, 0, 0.5);
734 inst->ui.control = e_widget_button_add
735 (evas, _("Controls"), NULL, _bluez_popup_cb_controls, inst, NULL);
736 e_widget_list_object_append(ol, inst->ui.control, 1, 0, 0.5);
738 _bluez_popup_update(inst);
740 e_widget_size_min_get(ol, &mw, &mh);
741 if (mh < 200) mh = 200;
742 if (mw < 200) mw = 200;
743 e_widget_size_min_set(ol, mw, mh);
745 e_gadcon_popup_content_set(inst->popup, ol);
746 e_gadcon_popup_show(inst->popup);
747 _bluez_popup_input_window_create(inst);
749 if (needs_scan) _bluez_popup_cb_scan(inst, NULL);
753 _bluez_menu_cb_post(void *data,
754 E_Menu *menu __UNUSED__)
756 E_Bluez_Instance *inst = data;
757 if ((!inst) || (!inst->menu))
761 e_object_del(E_OBJECT(inst->menu));
767 _bluez_menu_cb_cfg(void *data,
768 E_Menu *menu __UNUSED__,
769 E_Menu_Item *mi __UNUSED__)
771 E_Bluez_Instance *inst = data;
773 _bluez_popup_del(inst);
774 if (inst->conf_dialog)
778 inst->conf_dialog = e_bluez_config_dialog_new(NULL, inst);
782 _bluez_menu_new(E_Bluez_Instance *inst,
783 Evas_Event_Mouse_Down *ev)
790 zone = e_util_zone_current_get(e_manager_current_get());
793 mi = e_menu_item_new(m);
794 e_menu_item_label_set(mi, _("Settings"));
795 e_util_menu_item_theme_icon_set(mi, "configure");
796 e_menu_item_callback_set(mi, _bluez_menu_cb_cfg, inst);
798 m = e_gadcon_client_util_menu_items_append(inst->gcc, m, 0);
799 e_menu_post_deactivate_callback_set(m, _bluez_menu_cb_post, inst);
802 e_gadcon_canvas_zone_geometry_get(inst->gcc->gadcon, &x, &y, NULL, NULL);
803 e_menu_activate_mouse(m, zone, x + ev->output.x, y + ev->output.y,
804 1, 1, E_MENU_POP_DIRECTION_AUTO, ev->timestamp);
805 evas_event_feed_mouse_up(inst->gcc->gadcon->evas, ev->button,
806 EVAS_BUTTON_NONE, ev->timestamp, NULL);
810 _bluez_tip_new(E_Bluez_Instance *inst)
814 inst->tip = e_gadcon_popup_new(inst->gcc);
815 if (!inst->tip) return;
817 e = inst->tip->win->evas;
819 inst->o_tip = edje_object_add(e);
820 e_theme_edje_object_set(inst->o_tip, "base/theme/modules/bluez/tip",
821 "e/modules/bluez/tip");
823 _bluez_tip_update(inst);
825 e_gadcon_popup_content_set(inst->tip, inst->o_tip);
826 e_gadcon_popup_show(inst->tip);
830 _bluez_tip_del(E_Bluez_Instance *inst)
832 evas_object_del(inst->o_tip);
833 e_object_del(E_OBJECT(inst->tip));
839 _bluez_cb_mouse_down(void *data,
840 Evas *evas __UNUSED__,
841 Evas_Object *obj __UNUSED__,
844 E_Bluez_Instance *inst;
845 Evas_Event_Mouse_Down *ev;
855 _bluez_popup_new(inst);
857 _bluez_popup_del(inst);
859 else if (ev->button == 2)
860 _bluez_toggle_powered(inst);
861 else if ((ev->button == 3) && (!inst->menu))
862 _bluez_menu_new(inst, ev);
866 _bluez_cb_mouse_in(void *data,
867 Evas *evas __UNUSED__,
868 Evas_Object *obj __UNUSED__,
869 void *event __UNUSED__)
871 E_Bluez_Instance *inst = data;
876 _bluez_tip_new(inst);
880 _bluez_cb_mouse_out(void *data,
881 Evas *evas __UNUSED__,
882 Evas_Object *obj __UNUSED__,
883 void *event __UNUSED__)
885 E_Bluez_Instance *inst = data;
890 _bluez_tip_del(inst);
894 _bluez_edje_view_update(E_Bluez_Instance *inst,
897 E_Bluez_Module_Context *ctxt = inst->ctxt;
900 if (!ctxt->has_manager)
902 edje_object_part_text_set(o, "e.text.powered", "");
903 edje_object_part_text_set(o, "e.text.status", "");
904 edje_object_signal_emit(o, "e,changed,service,none", "e");
905 edje_object_part_text_set(o, "e.text.name", _("No Bluetooth daemon"));
906 edje_object_signal_emit(o, "e,changed,name", "e");
912 edje_object_part_text_set(o, "e.text.powered", "");
913 edje_object_part_text_set(o, "e.text.status", "");
914 edje_object_signal_emit(o, "e,changed,off", "e");
915 edje_object_part_text_set(o, "e.text.name", _("No Bluetooth adapter"));
916 edje_object_signal_emit(o, "e,changed,name", "e");
920 if (!e_bluez_adapter_name_get(inst->adapter, &name))
922 edje_object_part_text_set(o, "e.text.name", name);
923 edje_object_signal_emit(o, "e,changed,name", "e");
927 if (inst->discoverable)
929 edje_object_signal_emit(o, "e,changed,powered", "e");
930 edje_object_part_text_set
932 _("Bluetooth is powered and discoverable."));
936 edje_object_signal_emit(o, "e,changed,hidden", "e");
937 edje_object_part_text_set
938 (o, "e.text.status", _("Bluetooth is powered and hidden."));
943 edje_object_signal_emit(o, "e,changed,off", "e");
944 edje_object_part_text_set(o, "e.text.status", _("Bluetooth is off."));
949 _bluez_tip_update(E_Bluez_Instance *inst)
951 _bluez_edje_view_update(inst, inst->o_tip);
955 _bluez_gadget_update(E_Bluez_Instance *inst)
957 E_Bluez_Module_Context *ctxt = inst->ctxt;
959 if (inst->popup && ((!ctxt->has_manager) || (!inst->adapter)))
960 _bluez_popup_del(inst);
963 _bluez_popup_update(inst);
965 _bluez_tip_update(inst);
967 _bluez_edje_view_update(inst, inst->ui.gadget);
970 /* Gadcon Api Functions */
972 static E_Gadcon_Client *
973 _gc_init(E_Gadcon *gc,
978 E_Bluez_Instance *inst;
979 E_Bluez_Module_Context *ctxt;
984 ctxt = bluez_mod->data;
986 inst = E_NEW(E_Bluez_Instance, 1);
988 inst->ui.gadget = edje_object_add(gc->evas);
989 e_theme_edje_object_set(inst->ui.gadget, "base/theme/modules/bluez",
990 "e/modules/bluez/main");
992 inst->gcc = e_gadcon_client_new(gc, name, id, style, inst->ui.gadget);
993 inst->gcc->data = inst;
995 evas_object_event_callback_add
996 (inst->ui.gadget, EVAS_CALLBACK_MOUSE_DOWN, _bluez_cb_mouse_down, inst);
997 evas_object_event_callback_add
998 (inst->ui.gadget, EVAS_CALLBACK_MOUSE_IN, _bluez_cb_mouse_in, inst);
999 evas_object_event_callback_add
1000 (inst->ui.gadget, EVAS_CALLBACK_MOUSE_OUT, _bluez_cb_mouse_out, inst);
1002 // TODO: instead of getting the default adapter, get the adapter for
1003 // each instance. See the mixer module.
1004 if (ctxt->default_adapter)
1005 inst->adapter = e_bluez_adapter_get(ctxt->default_adapter);
1007 inst->adapter = NULL;
1011 Eina_Bool powered, discoverable, discovering;
1013 if (e_bluez_adapter_powered_get(inst->adapter, &powered))
1014 inst->powered = powered;
1016 if (e_bluez_adapter_discoverable_get(inst->adapter, &discoverable))
1017 inst->discoverable = discoverable;
1019 if (e_bluez_adapter_discovering_get(inst->adapter, &discovering))
1020 inst->discovering = discovering;
1023 _bluez_gadget_update(inst);
1025 ctxt->instances = eina_list_append(ctxt->instances, inst);
1031 _gc_shutdown(E_Gadcon_Client *gcc)
1033 E_Bluez_Module_Context *ctxt;
1034 E_Bluez_Instance *inst;
1039 ctxt = bluez_mod->data;
1049 e_menu_post_deactivate_callback_set(inst->menu, NULL, NULL);
1050 e_object_del(E_OBJECT(inst->menu));
1052 evas_object_del(inst->ui.gadget);
1054 _bluez_devices_clear(inst);
1056 ctxt->instances = eina_list_remove(ctxt->instances, inst);
1062 _gc_orient(E_Gadcon_Client *gcc,
1063 E_Gadcon_Orient orient __UNUSED__)
1065 e_gadcon_client_aspect_set(gcc, 16, 16);
1066 e_gadcon_client_min_size_set(gcc, 16, 16);
1070 _gc_label(E_Gadcon_Client_Class *client_class __UNUSED__)
1072 return _(_e_bluez_Name);
1075 static Evas_Object *
1076 _gc_icon(E_Gadcon_Client_Class *client_class __UNUSED__,
1081 o = edje_object_add(evas);
1082 edje_object_file_set(o, e_bluez_theme_path(), "icon");
1087 _gc_id_new(E_Gadcon_Client_Class *client_class __UNUSED__)
1089 E_Bluez_Module_Context *ctxt;
1094 ctxt = bluez_mod->data;
1098 snprintf(tmpbuf, sizeof(tmpbuf), "bluez.%d",
1099 eina_list_count(ctxt->instances));
1103 static const E_Gadcon_Client_Class _gc_class =
1105 GADCON_CLIENT_CLASS_VERSION, _e_bluez_name,
1107 _gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon, _gc_id_new, NULL,
1108 e_gadcon_site_is_not_toolbar
1110 E_GADCON_CLIENT_STYLE_PLAIN
1113 EAPI E_Module_Api e_modapi = {E_MODULE_API_VERSION, _e_bluez_Name};
1115 static const char _act_toggle_powered[] = "toggle_powered";
1116 static const char _lbl_toggle_powered[] = "Toggle Powered";
1119 _bluez_actions_register(E_Bluez_Module_Context *ctxt)
1121 ctxt->actions.toggle_powered = e_action_add(_act_toggle_powered);
1122 if (ctxt->actions.toggle_powered)
1124 ctxt->actions.toggle_powered->func.go =
1125 _bluez_cb_toggle_powered;
1126 e_action_predef_name_set
1127 (_(_e_bluez_Name), _(_lbl_toggle_powered), _act_toggle_powered,
1133 _bluez_actions_unregister(E_Bluez_Module_Context *ctxt)
1135 if (ctxt->actions.toggle_powered)
1137 e_action_predef_name_del(_(_e_bluez_Name), _(_lbl_toggle_powered));
1138 e_action_del(_act_toggle_powered);
1143 _bluez_manager_changed_do(void *data)
1145 E_Bluez_Module_Context *ctxt = data;
1147 //FIXME: reload the default adapter maybe?
1149 ctxt->poller.manager_changed = NULL;
1150 return ECORE_CALLBACK_CANCEL;
1154 _bluez_manager_changed(void *data,
1155 const E_Bluez_Element *element __UNUSED__)
1157 E_Bluez_Module_Context *ctxt = data;
1158 if (ctxt->poller.manager_changed)
1159 ecore_poller_del(ctxt->poller.manager_changed);
1160 ctxt->poller.manager_changed = ecore_poller_add
1161 (ECORE_POLLER_CORE, 1, _bluez_manager_changed_do, ctxt);
1165 _properties_sync_callback(void *data,
1166 DBusMessage *msg __UNUSED__,
1169 E_Bluez_Instance *inst = data;
1171 Eina_Bool discoverable;
1173 if (err && dbus_error_is_set(err))
1175 dbus_error_free(err);
1179 if (!e_bluez_adapter_powered_get(inst->adapter, &powered))
1181 _bluez_operation_error_show(_("Query adapter's powered."));
1185 inst->powered = powered;
1187 if (!e_bluez_adapter_discoverable_get(inst->adapter, &discoverable))
1189 _bluez_operation_error_show(_("Query adapter's discoverable."));
1193 inst->discoverable = discoverable;
1197 _default_adapter_callback(void *data,
1199 DBusError *err __UNUSED__)
1201 E_Bluez_Module_Context *ctxt = data;
1203 E_Bluez_Instance *inst;
1206 if (err && dbus_error_is_set(err))
1208 dbus_error_free(err);
1212 if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
1213 DBUS_TYPE_INVALID) == FALSE)
1216 eina_stringshare_replace(&ctxt->default_adapter, path);
1218 // TODO: instead of getting the default adapter, get the adapter for
1219 // each instance. See the mixer module.
1220 EINA_LIST_FOREACH(ctxt->instances, l, inst)
1222 inst->adapter = e_bluez_adapter_get(path);
1224 e_bluez_element_properties_sync_full
1225 (inst->adapter, _properties_sync_callback, inst);
1230 _bluez_event_manager_in(void *data,
1231 int type __UNUSED__,
1232 void *event __UNUSED__)
1234 E_Bluez_Module_Context *ctxt = data;
1235 E_Bluez_Element *element;
1237 E_Bluez_Instance *inst;
1239 ctxt->has_manager = EINA_TRUE;
1241 element = e_bluez_manager_get();
1242 if (!e_bluez_manager_default_adapter(_default_adapter_callback, ctxt))
1243 return ECORE_CALLBACK_DONE;
1245 e_bluez_element_listener_add(element, _bluez_manager_changed, ctxt, NULL);
1247 EINA_LIST_FOREACH(ctxt->instances, l, inst)
1248 _bluez_gadget_update(inst);
1250 return ECORE_CALLBACK_PASS_ON;
1254 _bluez_event_manager_out(void *data,
1255 int type __UNUSED__,
1256 void *event __UNUSED__)
1258 E_Bluez_Module_Context *ctxt = data;
1259 E_Bluez_Instance *inst;
1262 ctxt->has_manager = EINA_FALSE;
1263 eina_stringshare_replace(&ctxt->default_adapter, NULL);
1265 EINA_LIST_FOREACH(ctxt->instances, l, inst)
1266 _bluez_gadget_update(inst);
1268 return ECORE_CALLBACK_PASS_ON;
1272 _bluez_event_element_updated(void *data,
1273 int type __UNUSED__,
1274 void *event __UNUSED__)
1276 E_Bluez_Module_Context *ctxt = data;
1277 E_Bluez_Element *element = event;
1278 Eina_Bool powered, discoverable, discovering;
1279 E_Bluez_Instance *inst;
1282 if (!e_bluez_element_is_adapter(element)) return ECORE_CALLBACK_PASS_ON;
1284 if (!e_bluez_adapter_powered_get(element, &powered))
1285 powered = EINA_FALSE;
1287 if (!e_bluez_adapter_discoverable_get(element, &discoverable))
1288 discoverable = EINA_FALSE;
1290 if (!e_bluez_adapter_discovering_get(element, &discovering))
1291 discovering = EINA_FALSE;
1293 EINA_LIST_FOREACH(ctxt->instances, l, inst)
1295 if (inst->adapter != element) continue;
1297 inst->powered = powered;
1298 inst->discoverable = discoverable;
1299 inst->discovering = discovering;
1300 _bluez_gadget_update(inst);
1303 return ECORE_CALLBACK_PASS_ON;
1307 _bluez_events_register(E_Bluez_Module_Context *ctxt)
1309 ctxt->event.manager_in = ecore_event_handler_add
1310 (E_BLUEZ_EVENT_MANAGER_IN, _bluez_event_manager_in, ctxt);
1311 ctxt->event.manager_out = ecore_event_handler_add
1312 (E_BLUEZ_EVENT_MANAGER_OUT, _bluez_event_manager_out, ctxt);
1313 ctxt->event.element_updated = ecore_event_handler_add
1314 (E_BLUEZ_EVENT_ELEMENT_UPDATED, _bluez_event_element_updated, ctxt);
1315 ctxt->event.device_found = ecore_event_handler_add
1316 (E_BLUEZ_EVENT_DEVICE_FOUND, _bluez_event_devicefound, ctxt);
1318 // TODO: E_BLUEZ_EVENT_DEVICE_DISAPPEARED
1322 _bluez_events_unregister(E_Bluez_Module_Context *ctxt)
1324 if (ctxt->event.manager_in)
1325 ecore_event_handler_del(ctxt->event.manager_in);
1326 if (ctxt->event.manager_out)
1327 ecore_event_handler_del(ctxt->event.manager_out);
1328 if (ctxt->event.device_found)
1329 ecore_event_handler_del(ctxt->event.device_found);
1333 _bluez_agent_register(E_Bluez_Module_Context *ctxt)
1337 ctxt->agent.iface = e_dbus_interface_new("org.bluez.Agent");
1338 if (!ctxt->agent.iface)
1341 o = e_dbus_object_add(ctxt->agent.conn, _e_bluez_agent_path, ctxt);
1342 e_dbus_object_interface_attach(o, ctxt->agent.iface);
1343 e_dbus_interface_method_add
1344 (ctxt->agent.iface, "RequestPinCode", "o", "s", _bluez_request_pincode_cb);
1345 // TODO: RequestPasskey
1346 // TODO: RequestConfirmation
1348 // TODO: DisplayPasskey
1349 // TODO: ConfirmModeChange
1353 ctxt->agent.obj = o;
1357 _bluez_agent_unregister(E_Bluez_Module_Context *ctxt)
1361 EINA_LIST_FREE(ctxt->agent.pending, o)
1364 e_dbus_object_interface_detach(ctxt->agent.obj, ctxt->agent.iface);
1365 e_dbus_object_free(ctxt->agent.obj);
1366 e_dbus_interface_unref(ctxt->agent.iface);
1370 e_modapi_init(E_Module *m)
1372 E_Bluez_Module_Context *ctxt = E_NEW(E_Bluez_Module_Context, 1);
1376 ctxt->agent.conn = e_dbus_bus_get(DBUS_BUS_SYSTEM);
1377 if ((!ctxt->agent.conn) || (!e_bluez_system_init(ctxt->agent.conn)))
1378 goto error_bluez_system_init;
1382 if (_e_bluez_log_dom < 0)
1384 _e_bluez_log_dom = eina_log_domain_register("ebluez", EINA_COLOR_ORANGE);
1385 if (_e_bluez_log_dom < 0)
1387 //EINA_LOG_CRIT("could not register logging domain ebluez");
1388 goto error_log_domain;
1392 _bluez_agent_register(ctxt);
1393 _bluez_actions_register(ctxt);
1394 e_gadcon_provider_register(&_gc_class);
1396 _bluez_events_register(ctxt);
1401 _e_bluez_log_dom = -1;
1403 e_bluez_system_shutdown();
1404 error_bluez_system_init:
1410 _bluez_instances_free(E_Bluez_Module_Context *ctxt)
1412 E_Bluez_Instance *inst;
1413 EINA_LIST_FREE(ctxt->instances, inst)
1416 _bluez_popup_del(inst);
1418 _bluez_tip_del(inst);
1420 e_object_del(E_OBJECT(inst->gcc));
1425 e_modapi_shutdown(E_Module *m)
1427 E_Bluez_Module_Context *ctxt = m->data;
1428 E_Bluez_Element *element;
1433 element = e_bluez_manager_get();
1434 e_bluez_element_listener_del(element, _bluez_manager_changed, ctxt);
1436 _bluez_events_unregister(ctxt);
1437 _bluez_instances_free(ctxt);
1439 _bluez_actions_unregister(ctxt);
1440 _bluez_agent_unregister(ctxt);
1441 e_gadcon_provider_unregister(&_gc_class);
1443 if (ctxt->poller.manager_changed)
1444 ecore_poller_del(ctxt->poller.manager_changed);
1446 eina_stringshare_del(ctxt->default_adapter);
1451 e_bluez_system_shutdown();
1457 e_modapi_save(E_Module *m __UNUSED__)