diff --git a/meson_options.txt b/meson_options.txt index 234802766..c570b0522 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -111,6 +111,10 @@ option('bluez4', type: 'boolean', value: true, description: 'enable bluez4 module: (default=true)') +option('bluez5', + type: 'boolean', + value: true, + description: 'enable bluez5 module: (default=true)') option('clock', type: 'boolean', value: true, diff --git a/src/bin/e_config.c b/src/bin/e_config.c index 1feb31bbc..5abf91bae 100644 --- a/src/bin/e_config.c +++ b/src/bin/e_config.c @@ -1525,6 +1525,44 @@ e_config_load(void) CONFIG_VERSION_UPDATE_INFO(25); e_config_save_queue(); } + CONFIG_VERSION_CHECK(26) + { + // move from bluez4 to bluez5 automatically. you can manually + // move back if that works better for you... + Eina_List *l, *ll; + E_Config_Module *em; + E_Config_Gadcon *gc; + E_Config_Gadcon_Client *gcc; + + EINA_LIST_FOREACH(e_config->modules, l, em) + { + if (!em->enabled) continue; + if (!em->name) continue; + if (eina_streq(em->name, "bluez4")) + { + eina_stringshare_replace(&(em->name), "bluez5"); + } + } + EINA_LIST_FOREACH(e_config->gadcons, l, gc) + { + EINA_LIST_FOREACH(gc->clients, ll, gcc) + { + if (!gcc->name) continue; + if (!strncmp(gcc->name, "bluez4", 6)) + { + char *tmp = strdup(gcc->name); + if (tmp) + { + tmp[5] = '5'; + eina_stringshare_replace(&(gcc->name), tmp); + free(tmp); + } + } + } + } + CONFIG_VERSION_UPDATE_INFO(26); + e_config_save_queue(); + } } elm_config_profile_set(_e_config_profile); if (!e_config->remember_internal_fm_windows) diff --git a/src/bin/e_config.h b/src/bin/e_config.h index a4af30c5c..0ac67f144 100644 --- a/src/bin/e_config.h +++ b/src/bin/e_config.h @@ -46,7 +46,7 @@ typedef enum /* increment this whenever a new set of config values are added but the users * config doesn't need to be wiped - simply new values need to be put in */ -#define E_CONFIG_FILE_GENERATION 25 +#define E_CONFIG_FILE_GENERATION 26 #define E_CONFIG_FILE_VERSION ((E_CONFIG_FILE_EPOCH * 1000000) + E_CONFIG_FILE_GENERATION) #define E_CONFIG_BINDINGS_VERSION 0 // DO NOT INCREMENT UNLESS YOU WANT TO WIPE ALL BINDINGS!!!!! diff --git a/src/bin/e_module.c b/src/bin/e_module.c index 96ddcb67a..8c65f1185 100644 --- a/src/bin/e_module.c +++ b/src/bin/e_module.c @@ -997,6 +997,7 @@ _e_module_whitelist_check(void) "backlight", "battery", "bluez4", + "bluez5", "clock", "conf", "conf_applications", diff --git a/src/modules/bluez5/bz.c b/src/modules/bluez5/bz.c new file mode 100644 index 000000000..d0a7e083d --- /dev/null +++ b/src/modules/bluez5/bz.c @@ -0,0 +1,109 @@ +#include "e_mod_main.h" + +Eldbus_Connection *bz_conn = NULL; + +static Ecore_Timer *owner_gain_timer = NULL; + +///////////////////////////////////////////////////////////////////////////// + +static Eina_Bool +cb_name_owner_new(void *data EINA_UNUSED) +{ + owner_gain_timer = NULL; + bz_obj_init(); + bz_agent_init(); + return EINA_FALSE; +} + +static void +cb_name_owner_changed(void *data EINA_UNUSED, + const char *bus EINA_UNUSED, + const char *from EINA_UNUSED, + const char *to) +{ + static Eina_Bool first = EINA_TRUE; + if (to[0]) + { + if (owner_gain_timer) ecore_timer_del(owner_gain_timer); + // on first start try and re-init quickly because we get a name + // owner change even if all is good when we register to listen for it, + // so start fast + if (first) + owner_gain_timer = ecore_timer_add(0.1, cb_name_owner_new, NULL); + // but if we gegt a name owner change later it's probably because + // bluez was restarted or crashed. a new bz daemon will (or should) + // come up. so re-init more slowly here giving the daemon some time + // to come up before pestering it. + else + owner_gain_timer = ecore_timer_add(1.0, cb_name_owner_new, NULL); + first = EINA_FALSE; + } + else + { + if (owner_gain_timer) ecore_timer_del(owner_gain_timer); + owner_gain_timer = NULL; + ebluze5_popup_clear(); + bz_agent_shutdown(); + bz_obj_shutdown(); + } +} + +static void +cb_obj_add(Obj *o) +{ + if (o->type == BZ_OBJ_ADAPTER) + { + o->fn_change = ebluez5_popup_adapter_change; + o->fn_del = ebluez5_popup_adapter_del; + ebluez5_popup_adapter_add(o); + return; + } + if (o->type == BZ_OBJ_DEVICE) + { + o->fn_change = ebluez5_popup_device_change; + o->fn_del = ebluez5_popup_device_del; + ebluez5_popup_device_add(o); + return; + } +} + +void +bz_init(void) +{ + bz_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM); + bz_obj_add_func_set(cb_obj_add); + bz_agent_release_func_set(ebluez5_agent_agent_release); + bz_agent_cancel_func_set(ebluez5_agent_agent_cancel); + bz_agent_req_pin_func_set(ebluez5_agent_agent_req_pin); + bz_agent_disp_pin_func_set(ebluez5_agent_agent_disp_pin); + bz_agent_req_pass_func_set(ebluez5_agent_req_pass); + bz_agent_disp_pass_func_set(ebluez5_agent_disp_pass); + bz_agent_req_confirm_func_set(ebluez5_agent_req_confirm); + bz_agent_req_auth_func_set(ebluez5_agent_req_auth); + bz_agent_auth_service_func_set(ebluez5_agent_auth_service); + eldbus_name_owner_changed_callback_add(bz_conn, "org.bluez", + cb_name_owner_changed, + NULL, EINA_TRUE); +} + +void +bz_shutdown(void) +{ + eldbus_name_owner_changed_callback_del(bz_conn, "org.bluez", + cb_name_owner_changed, NULL); + bz_agent_release_func_set(NULL); + bz_agent_cancel_func_set(NULL); + bz_agent_req_pin_func_set(NULL); + bz_agent_disp_pin_func_set(NULL); + bz_agent_req_pass_func_set(NULL); + bz_agent_disp_pass_func_set(NULL); + bz_agent_req_confirm_func_set(NULL); + bz_agent_req_auth_func_set(NULL); + bz_agent_auth_service_func_set(NULL); + bz_obj_add_func_set(NULL); + ebluze5_popup_clear(); + bz_agent_shutdown(); + bz_obj_shutdown(); + eldbus_connection_unref(bz_conn); + bz_conn = NULL; +} diff --git a/src/modules/bluez5/bz.h b/src/modules/bluez5/bz.h new file mode 100644 index 000000000..252f20180 --- /dev/null +++ b/src/modules/bluez5/bz.h @@ -0,0 +1,234 @@ +#ifndef BZ_H +# define BZ_H +# include + +typedef enum { + BZ_OBJ_UNKNOWN, + BZ_OBJ_BLUEZ, + BZ_OBJ_ADAPTER, + BZ_OBJ_DEVICE +} Obj_Type; + +typedef struct _Obj Obj; + +struct _Obj { + //// internal object data + Eldbus_Proxy *proxy; + Eldbus_Proxy *prop_proxy; + Eldbus_Signal_Handler *prop_sig; + unsigned int ref; + Eina_Bool in_table : 1; + Eina_Bool add_called : 1; + //// public data to read + const char *path; + Obj_Type type; + //// data to be set by users of the obj + void *data; // custom data + void (*fn_change) (Obj *o); + void (*fn_del) (Obj *o); + //// obj properties + Eina_Array *uuids; + const char *address; + const char *address_type; + const char *name; + const char *icon; + const char *alias; + const char *adapter; + const char *modalias; + unsigned int klass; + unsigned short appearance; + unsigned short txpower; + short rssi; + Eina_Bool paired : 1; + Eina_Bool connected : 1; + Eina_Bool trusted : 1; + Eina_Bool blocked : 1; + Eina_Bool legacy_pairing : 1; + Eina_Bool services_resolved : 1; + //// adapter specific properties + unsigned int discoverable_timeout; + unsigned int pairable_timeout; + Eina_Bool discoverable : 1; + Eina_Bool discovering : 1; + Eina_Bool pairable : 1; + Eina_Bool powered : 1; + //// agent data for when devices ask to pair etc. + const char *agent_request; + Eldbus_Message *agent_msg_ok; + Eldbus_Message *agent_msg_err; + void (*agent_entry_fn) (Eldbus_Message *msg, const char *str); + Eina_Bool agent_alert : 1; +}; + +#define BZ_OBJ_CLASS_SERVICE_LIMITED_DISCOVERABLE (1 << 13) +#define BZ_OBJ_CLASS_SERVICE_POSITIONING_BIT (1 << 16) +#define BZ_OBJ_CLASS_SERVICE_NETWORKING_BIT (1 << 17) +#define BZ_OBJ_CLASS_SERVICE_RENDERING_BIT (1 << 18) +#define BZ_OBJ_CLASS_SERVICE_CAPTURING_BIT (1 << 19) +#define BZ_OBJ_CLASS_SERVICE_OBJECT_TRANSFER_BIT (1 << 20) +#define BZ_OBJ_CLASS_SERVICE_AUDIO_BIT (1 << 21) +#define BZ_OBJ_CLASS_SERVICE_TELEPHONY_BIT (1 << 22) +#define BZ_OBJ_CLASS_SERVICE_INFORMATION_BIT (1 << 23) + +#define BZ_OBJ_CLASS_MAJ_MASK (0x1f << 8) +#define BZ_OBJ_CLASS_MAJ_MISC ( 0 << 8) +#define BZ_OBJ_CLASS_MAJ_COMPUTER ( 1 << 8) +#define BZ_OBJ_CLASS_MAJ_PHONE ( 2 << 8) +#define BZ_OBJ_CLASS_MAJ_LAN ( 3 << 8) +#define BZ_OBJ_CLASS_MAJ_AV ( 4 << 8) +#define BZ_OBJ_CLASS_MAJ_PERIPHERAL ( 5 << 8) +#define BZ_OBJ_CLASS_MAJ_IMAGING ( 6 << 8) +#define BZ_OBJ_CLASS_MAJ_WEARABLE ( 7 << 8) +#define BZ_OBJ_CLASS_MAJ_TOY ( 8 << 8) +#define BZ_OBJ_CLASS_MAJ_HEALTH ( 9 << 8) + +#define BZ_OBJ_CLASS_MIN_COMPUTER_MASK (0x3f << 2) +#define BZ_OBJ_CLASS_MIN_COMPUTER_DESKTOP ( 1 << 2) +#define BZ_OBJ_CLASS_MIN_COMPUTER_SERVER ( 2 << 2) +#define BZ_OBJ_CLASS_MIN_COMPUTER_LAPTOP ( 3 << 2) +#define BZ_OBJ_CLASS_MIN_COMPUTER_CLAMSHELL ( 4 << 2) +#define BZ_OBJ_CLASS_MIN_COMPUTER_PDA ( 5 << 2) +#define BZ_OBJ_CLASS_MIN_COMPUTER_WEARABLE ( 6 << 2) +#define BZ_OBJ_CLASS_MIN_COMPUTER_TABLET ( 7 << 2) + +#define BZ_OBJ_CLASS_MIN_PHONE_MASK (0x3f << 2) +#define BZ_OBJ_CLASS_MIN_PHONE_CELL ( 1 << 2) +#define BZ_OBJ_CLASS_MIN_PHONE_CORDLESS ( 2 << 2) +#define BZ_OBJ_CLASS_MIN_PHONE_SMARTPHONE ( 3 << 2) +#define BZ_OBJ_CLASS_MIN_PHONE_WIRED ( 4 << 2) +#define BZ_OBJ_CLASS_MIN_PHONE_ISDN ( 5 << 2) + +#define BZ_OBJ_CLASS_MIN_LAN_MASK (0x07 << 5) +#define BZ_OBJ_CLASS_MIN_LAN_AVAIL_7 ( 0 << 5) +#define BZ_OBJ_CLASS_MIN_LAN_AVAIL_6 ( 1 << 5) +#define BZ_OBJ_CLASS_MIN_LAN_AVAIL_5 ( 2 << 5) +#define BZ_OBJ_CLASS_MIN_LAN_AVAIL_4 ( 3 << 5) +#define BZ_OBJ_CLASS_MIN_LAN_AVAIL_3 ( 4 << 5) +#define BZ_OBJ_CLASS_MIN_LAN_AVAIL_2 ( 5 << 5) +#define BZ_OBJ_CLASS_MIN_LAN_AVAIL_1 ( 6 << 5) +#define BZ_OBJ_CLASS_MIN_LAN_AVAIL_0 ( 7 << 5) + +#define BZ_OBJ_CLASS_MIN_AV_MASK (0x3f << 2) +#define BZ_OBJ_CLASS_MIN_AV_WEARABLE_HEADSET ( 1 << 2) +#define BZ_OBJ_CLASS_MIN_AV_HANDS_FREE ( 2 << 2) +#define BZ_OBJ_CLASS_MIN_AV_MIC ( 4 << 2) +#define BZ_OBJ_CLASS_MIN_AV_SPEAKER ( 5 << 2) +#define BZ_OBJ_CLASS_MIN_AV_HEADPHONES ( 6 << 2) +#define BZ_OBJ_CLASS_MIN_AV_PORTABLE_AUDIO ( 7 << 2) +#define BZ_OBJ_CLASS_MIN_AV_CAR_AUDIO ( 8 << 2) +#define BZ_OBJ_CLASS_MIN_AV_SET_TOP_BOX ( 9 << 2) +#define BZ_OBJ_CLASS_MIN_AV_HIFI_AUDIO (10 << 2) +#define BZ_OBJ_CLASS_MIN_AV_VCR (11 << 2) +#define BZ_OBJ_CLASS_MIN_AV_VIDEO_CAMERA (12 << 2) +#define BZ_OBJ_CLASS_MIN_AV_CAMCORDER (13 << 2) +#define BZ_OBJ_CLASS_MIN_AV_VIDEO_MONITOR (14 << 2) +#define BZ_OBJ_CLASS_MIN_AV_VIDEO_DISPLAY_SPEAKER (15 << 2) +#define BZ_OBJ_CLASS_MIN_AV_VIDEO_CONFERENCE (16 << 2) +#define BZ_OBJ_CLASS_MIN_AV_GAMING (18 << 2) + +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_KEYBOARD_BIT ( 1 << 6) +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_MOUSE_BIT ( 1 << 7) + +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_MASK2 (0x0f << 2) +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_JOYSTICK ( 1 << 2) +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_GAMEPAD ( 2 << 2) +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_REMOTE ( 3 << 2) +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_SENSING ( 4 << 2) +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_DIGITIZER_TAB ( 5 << 2) +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_CARD_READER ( 6 << 2) +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_PEN ( 7 << 2) +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_SCANNER ( 8 << 2) +#define BZ_OBJ_CLASS_MIN_PERIPHERAL_WAND ( 9 << 2) + +#define BZ_OBJ_CLASS_MIN_IMAGING_DISPLAY_BIT ( 1 << 4) +#define BZ_OBJ_CLASS_MIN_IMAGING_CAMERA_BIT ( 1 << 5) +#define BZ_OBJ_CLASS_MIN_IMAGING_SCANNER_BIT ( 1 << 6) +#define BZ_OBJ_CLASS_MIN_IMAGING_PRINTER_BIT ( 1 << 7) + +#define BZ_OBJ_CLASS_MIN_WEARABLE_MASK (0x3f << 2) +#define BZ_OBJ_CLASS_MIN_WEARABLE_WATCH ( 1 << 2) +#define BZ_OBJ_CLASS_MIN_WEARABLE_PAGER ( 2 << 2) +#define BZ_OBJ_CLASS_MIN_WEARABLE_JACKET ( 3 << 2) +#define BZ_OBJ_CLASS_MIN_WEARABLE_HELMET ( 4 << 2) +#define BZ_OBJ_CLASS_MIN_WEARABLE_GLASSES ( 5 << 2) + +#define BZ_OBJ_CLASS_MIN_TOY_MASK (0x3f << 2) +#define BZ_OBJ_CLASS_MIN_TOY_ROBOT ( 1 << 2) +#define BZ_OBJ_CLASS_MIN_TOY_VEHICLE ( 2 << 2) +#define BZ_OBJ_CLASS_MIN_TOY_DOLL ( 3 << 2) +#define BZ_OBJ_CLASS_MIN_TOY_CONTROLLER ( 4 << 2) +#define BZ_OBJ_CLASS_MIN_TOY_GAME ( 5 << 2) + +#define BZ_OBJ_CLASS_MIN_HEALTH_MASK (0x3f << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_BLOOD_PRESSURE ( 1 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_THERMOMETER ( 2 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_SCALES ( 3 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_GLUCOSE ( 4 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_PULSE_OXIMITER ( 5 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_HEART_RATE ( 6 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_HEALTH_DATA_DISP ( 7 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_STEP ( 8 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_BODY_COMPOSITION ( 9 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_PEAK_FLOW (10 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_MEDICATION (11 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_KNEE_PROSTHESIS (12 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_ANKLE_PROSTHESIS (13 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_GENERIC_HEALTH (14 << 2) +#define BZ_OBJ_CLASS_MIN_HEALTH_PRESONAL_MOBILITY (15 << 2) + + +extern Eldbus_Connection *bz_conn; + +void bz_init(void); +void bz_shutdown(void); + +Obj *bz_obj_add(const char *path); +Obj *bz_obj_find(const char *path); +void bz_obj_power_on(Obj *o); +void bz_obj_power_off(Obj *o); +void bz_obj_discoverable(Obj *o); +void bz_obj_undiscoverable(Obj *o); +void bz_obj_pairable(Obj *o); +void bz_obj_unpairable(Obj *o); +void bz_obj_trust(Obj *o); +void bz_obj_distrust(Obj *o); +void bz_obj_pair(Obj *o); +void bz_obj_pair_cancel(Obj *o); +void bz_obj_connect(Obj *o); +void bz_obj_disconnect(Obj *o); +void bz_obj_remove(Obj *o); +void bz_obj_profile_connect(Obj *o, const char *uuid); +void bz_obj_profile_disconnect(Obj *o, const char *uuid); +void bz_obj_ref(Obj *o); +void bz_obj_unref(Obj *o); +void bz_obj_discover_start(Obj *o); +void bz_obj_discover_stop(Obj *o); +void bz_obj_agent_request(Obj *o, const char *req, void (*fn) (Eldbus_Message *msg, const char *str), Eldbus_Message *ok_msg, Eldbus_Message *err_msg); + +void bz_obj_init(void); +void bz_obj_shutdown(void); +void bz_obj_add_func_set(void (*fn) (Obj *o)); + +void bz_agent_msg_reply(Eldbus_Message *msg); +void bz_agent_msg_drop(Eldbus_Message *msg); +Eldbus_Message *bz_agent_msg_err(Eldbus_Message *msg); +Eldbus_Message *bz_agent_msg_ok(Eldbus_Message *msg); +const char *bz_agent_msg_path(Eldbus_Message *msg); +const char *bz_agent_msg_path_str(Eldbus_Message *msg, const char **str); +const char *bz_agent_msg_path_u32(Eldbus_Message *msg, unsigned int *u32); +const char *bz_agent_msg_path_u32_u16(Eldbus_Message *msg, unsigned int *u32, unsigned short *u16); +void bz_agent_msg_str_add(Eldbus_Message *msg, const char *str); +void bz_agent_msg_u32_add(Eldbus_Message *msg, unsigned int u32); + +void bz_agent_init(void); +void bz_agent_shutdown(void); +void bz_agent_release_func_set(void (*fn) (void)); +void bz_agent_cancel_func_set(void (*fn) (void)); +void bz_agent_req_pin_func_set(void (*fn) (Eldbus_Message *msg)); +void bz_agent_disp_pin_func_set(void (*fn) (Eldbus_Message *msg)); +void bz_agent_req_pass_func_set(void (*fn) (Eldbus_Message *msg)); +void bz_agent_disp_pass_func_set(void (*fn) (Eldbus_Message *msg)); +void bz_agent_req_confirm_func_set(void (*fn) (Eldbus_Message *msg)); +void bz_agent_req_auth_func_set(void (*fn) (Eldbus_Message *msg)); +void bz_agent_auth_service_func_set(void (*fn) (Eldbus_Message *msg)); +#endif diff --git a/src/modules/bluez5/bz_agent.c b/src/modules/bluez5/bz_agent.c new file mode 100644 index 000000000..8eb0489cb --- /dev/null +++ b/src/modules/bluez5/bz_agent.c @@ -0,0 +1,346 @@ +#include "bz.h" +#include "e.h" + +static Eldbus_Service_Interface *agent_iface = NULL; +static Eldbus_Proxy *agent_proxy = NULL; +static void (*fn_release) (void) = NULL; +static void (*fn_cancel) (void) = NULL; +static void (*fn_req_pin) (Eldbus_Message *msg) = NULL; +static void (*fn_disp_pin) (Eldbus_Message *msg) = NULL; +static void (*fn_req_pass) (Eldbus_Message *msg) = NULL; +static void (*fn_disp_pass) (Eldbus_Message *msg) = NULL; +static void (*fn_req_confirm) (Eldbus_Message *msg) = NULL; +static void (*fn_req_auth) (Eldbus_Message *msg) = NULL; +static void (*fn_auth_service) (Eldbus_Message *msg) = NULL; + +static Eldbus_Message * +cb_agent_release(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + Eldbus_Message *reply = eldbus_message_method_return_new(msg); + + if (fn_release) fn_release(); + return reply; +} + +static Eldbus_Message * +cb_agent_cancel(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + Eldbus_Message *reply = eldbus_message_method_return_new(msg); + + if (fn_cancel) fn_cancel(); + return reply; +} + +static Eldbus_Message * +cb_agent_request_pin_code(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + if (fn_req_pin) fn_req_pin((Eldbus_Message *)msg); + return NULL; +} + +static Eldbus_Message * +cb_agent_display_pin_code(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + if (fn_disp_pin) fn_disp_pin((Eldbus_Message *)msg); + return NULL; +} + +static Eldbus_Message * +cb_agent_request_pass_key(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + if (fn_req_pass) fn_req_pass((Eldbus_Message *)msg); + return NULL; +} + +static Eldbus_Message * +cb_agent_display_pass_key(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + if (fn_disp_pass) fn_disp_pass((Eldbus_Message *)msg); + return NULL; +} + +static Eldbus_Message * +cb_agent_request_confirmation(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + if (fn_req_confirm) fn_req_confirm((Eldbus_Message *)msg); + return NULL; +} + +static Eldbus_Message * +cb_agent_request_authorization(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + if (fn_req_auth) fn_req_auth((Eldbus_Message *)msg); + return NULL; +} + +static Eldbus_Message * +cb_agent_authorize_service(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + if (fn_auth_service) fn_auth_service((Eldbus_Message *)msg); + return NULL; +/* Not done yet... don't even know what services are with bt... + Eldbus_Message *reply = eldbus_message_method_return_new(msg); + const char *path = NULL, *uuid = NULL; + + printf("Agent authorize service\n"); + if (!eldbus_message_arguments_get(msg, "os", &path, &uuid)) return reply; + printf(" %s %s\n", path, uuid); + // if ok return this reply, or make it an error... + // reply = eldbus_message_error_new(msg, "org.bluez.Error.Rejected", ""); + return reply; + // really return NULL and defer above reply with: + // eldbus_connection_send(bz_conn, reply, NULL, NULL, -1); + */ +} + +static void +cb_default(void *data EINA_UNUSED, const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + + if (eldbus_message_error_get(msg, &name, &text)) + { + // XXX: should have a visual e log... + e_util_dialog_show(_("Bluetooth"), + _("Could not register default agent:
%s %s"), + name, text); + return; + } +} + +static void +cb_register(void *data EINA_UNUSED, const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + + if (eldbus_message_error_get(msg, &name, &text)) + { + // XXX: should have a visual e log... + e_util_dialog_show(_("Bluetooth"), + _("Could not register agent:
%s %s\n"), + name, text); + return; + } + if (!agent_proxy) return; + eldbus_proxy_call(agent_proxy, "RequestDefaultAgent", + cb_default, NULL, -1, + "o", "/org/enlightenment/bluez5/agent"); +} + +static void +cb_unregister(void *data EINA_UNUSED, const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + + if (agent_proxy) + { + eldbus_proxy_unref(agent_proxy); + agent_proxy = NULL; + } + if (agent_iface) + { + eldbus_service_object_unregister(agent_iface); + agent_iface = NULL; + } + if (eldbus_message_error_get(msg, &name, &text)) + { + // just debug for developers + printf("Could not unregister agent.\n %s:\n %s\n", name, text); + return; + } +} + +static const Eldbus_Method agent_methods[] = { + { "Release", NULL, NULL, cb_agent_release, 0 }, + { "RequestPinCode", ELDBUS_ARGS({ "o", "device" }), ELDBUS_ARGS({ "s", "pincode" }), cb_agent_request_pin_code, 0 }, + { "DisplayPinCode", ELDBUS_ARGS({ "o", "device" }, { "s", "pincode" }), NULL, cb_agent_display_pin_code, 0 }, + { "RequestPasskey", ELDBUS_ARGS({ "o", "device" }), ELDBUS_ARGS({ "u", "passkey" }), cb_agent_request_pass_key, 0 }, + { "DisplayPasskey", ELDBUS_ARGS({ "o", "device" }, { "u", "passkey" }, { "q", "entered" }), NULL, cb_agent_display_pass_key, 0 }, + { "RequestConfirmation", ELDBUS_ARGS({ "o", "device" }, { "u", "passkey" }), NULL, cb_agent_request_confirmation, 0 }, + { "RequestAuthorization", ELDBUS_ARGS({ "o", "device" }), NULL, cb_agent_request_authorization, 0 }, + { "AuthorizeService", ELDBUS_ARGS({ "o", "device" }, { "s", "uuid" }), NULL, cb_agent_authorize_service, 0 }, + { "Cancel", NULL, NULL, cb_agent_cancel, 0 }, + + { NULL, NULL, NULL, NULL, 0 } +}; +static const Eldbus_Service_Interface_Desc agent_desc = { + "org.bluez.Agent1", agent_methods, NULL, NULL, NULL, NULL +}; + +void +bz_agent_msg_reply(Eldbus_Message *msg) +{ + if (!bz_conn) + { + eldbus_message_unref(msg); + return; + } + eldbus_connection_send(bz_conn, msg, NULL, NULL, -1); +} + +void +bz_agent_msg_drop(Eldbus_Message *msg) +{ + eldbus_message_unref(msg); +} + +Eldbus_Message * +bz_agent_msg_err(Eldbus_Message *msg) +{ + return eldbus_message_error_new(msg, "org.bluez.Error.Rejected", ""); +} + +Eldbus_Message * +bz_agent_msg_ok(Eldbus_Message *msg) +{ + return eldbus_message_method_return_new(msg); +} + +const char * +bz_agent_msg_path(Eldbus_Message *msg) +{ + const char *s = NULL; + + if (!eldbus_message_arguments_get(msg, "o", &s)) return NULL; + return s; +} + +const char * +bz_agent_msg_path_str(Eldbus_Message *msg, const char **str) +{ + const char *s = NULL, *s2 = NULL; + + if (!eldbus_message_arguments_get(msg, "os", &s, &s2)) return NULL; + if (str) *str = s2; + return s; +} + +const char * +bz_agent_msg_path_u32(Eldbus_Message *msg, unsigned int *u32) +{ + const char *s = NULL; + unsigned int uu32 = 0; + + if (!eldbus_message_arguments_get(msg, "ou", &s, &uu32)) return NULL; + if (u32) *u32 = uu32; + return s; +} + +const char * +bz_agent_msg_path_u32_u16(Eldbus_Message *msg, unsigned int *u32, unsigned short *u16) +{ + const char *s = NULL; + unsigned int uu32 = 0; + unsigned short uu16 = 0; + + if (!eldbus_message_arguments_get(msg, "ouq", &s, &uu32, &uu16)) return NULL; + if (u32) *u32 = uu32; + if (u16) *u16 = uu16; + return s; +} + +void +bz_agent_msg_str_add(Eldbus_Message *msg, const char *str) +{ + eldbus_message_arguments_append(msg, "s", str); +} + +void +bz_agent_msg_u32_add(Eldbus_Message *msg, unsigned int u32) +{ + eldbus_message_arguments_append(msg, "u", &u32); +} + +void +bz_agent_init(void) +{ + Eldbus_Object *obj; + + obj = eldbus_object_get(bz_conn, "org.bluez", "/org/bluez"); + agent_proxy = eldbus_proxy_get(obj, "org.bluez.AgentManager1"); + agent_iface = eldbus_service_interface_register + (bz_conn, "/org/enlightenment/bluez5/agent", &agent_desc); + if (agent_proxy) + eldbus_proxy_call(agent_proxy, "RegisterAgent", + cb_register, NULL, -1, + "os", "/org/enlightenment/bluez5/agent", + "KeyboardDisplay"); + else + e_util_dialog_show(_("Bluetooth"), + _("Could not call RegisterAgent\n")); +} + +void +bz_agent_shutdown(void) +{ + if (!agent_proxy) return; + eldbus_proxy_call(agent_proxy, "UnregisterAgent", + cb_unregister, NULL, -1, + "o", "/org/enlightenment/bluez5/agent"); +} + +void +bz_agent_release_func_set(void (*fn) (void)) +{ + fn_release = fn; +} + +void +bz_agent_cancel_func_set(void (*fn) (void)) +{ + fn_cancel = fn; +} + +void +bz_agent_req_pin_func_set(void (*fn) (Eldbus_Message *msg)) +{ + fn_req_pin = fn; +} + +void +bz_agent_disp_pin_func_set(void (*fn) (Eldbus_Message *msg)) +{ + fn_disp_pin = fn; +} + +void +bz_agent_req_pass_func_set(void (*fn) (Eldbus_Message *msg)) +{ + fn_req_pass = fn; +} + +void +bz_agent_disp_pass_func_set(void (*fn) (Eldbus_Message *msg)) +{ + fn_disp_pass = fn; +} + +void +bz_agent_req_confirm_func_set(void (*fn) (Eldbus_Message *msg)) +{ + fn_req_confirm = fn; +} + +void +bz_agent_req_auth_func_set(void (*fn) (Eldbus_Message *msg)) +{ + fn_req_auth = fn; +} + +void +bz_agent_auth_service_func_set(void (*fn) (Eldbus_Message *msg)) +{ + fn_auth_service = fn; +} diff --git a/src/modules/bluez5/bz_obj.c b/src/modules/bluez5/bz_obj.c new file mode 100644 index 000000000..24552bb2b --- /dev/null +++ b/src/modules/bluez5/bz_obj.c @@ -0,0 +1,760 @@ +#include "bz.h" + +static Eldbus_Proxy *objman_proxy = NULL; +static Eldbus_Signal_Handler *sig_ifadd = NULL; +static Eldbus_Signal_Handler *sig_ifdel = NULL; +static Eldbus_Pending *pend_getobj = NULL; +static Eina_Hash *obj_table = NULL; +static void (*fn_obj_add) (Obj *o) = NULL; + +/* +static void +cb_obj_prop_mandata(void *data, const void *key, Eldbus_Message_Iter *var) +{ + Obj *o = data; + unsigned short *skey = key; + + printf(" M KEY %x\n", (int)*skey); +} +*/ + +static void +cb_obj_prop_entry(void *data, const void *key, Eldbus_Message_Iter *var) +{ + Obj *o = data; + const char *skey = key; + + if (!strcmp(skey, "Paired")) + { + Eina_Bool val = EINA_FALSE; + if (eldbus_message_iter_arguments_get(var, "b", &val)) + o->paired = val; + } + if (!strcmp(skey, "Connected")) + { + Eina_Bool val = EINA_FALSE; + if (eldbus_message_iter_arguments_get(var, "b", &val)) + o->connected = val; + } + if (!strcmp(skey, "Trusted")) + { + Eina_Bool val = EINA_FALSE; + if (eldbus_message_iter_arguments_get(var, "b", &val)) + o->trusted = val; + } + if (!strcmp(skey, "Blocked")) + { + Eina_Bool val = EINA_FALSE; + if (eldbus_message_iter_arguments_get(var, "b", &val)) + o->blocked = val; + } + if (!strcmp(skey, "LegacyPairing")) + { + Eina_Bool val = EINA_FALSE; + if (eldbus_message_iter_arguments_get(var, "b", &val)) + o->legacy_pairing = val; + } + if (!strcmp(skey, "ServicesResolved")) + { + Eina_Bool val = EINA_FALSE; + if (eldbus_message_iter_arguments_get(var, "b", &val)) + o->services_resolved = val; + } + if (!strcmp(skey, "Address")) + { + const char *val = NULL; + if (eldbus_message_iter_arguments_get(var, "s", &val)) + o->address = eina_stringshare_add(val); + } + if (!strcmp(skey, "AddressType")) + { + const char *val = NULL; + if (eldbus_message_iter_arguments_get(var, "s", &val)) + o->address_type = eina_stringshare_add(val); + } + if (!strcmp(skey, "Name")) + { + const char *val = NULL; + if (eldbus_message_iter_arguments_get(var, "s", &val)) + o->name = eina_stringshare_add(val); + } + if (!strcmp(skey, "Icon")) + { + const char *val = NULL; + if (eldbus_message_iter_arguments_get(var, "s", &val)) + o->icon = eina_stringshare_add(val); + } + if (!strcmp(skey, "Alias")) + { + const char *val = NULL; + if (eldbus_message_iter_arguments_get(var, "s", &val)) + o->alias = eina_stringshare_add(val); + } + if (!strcmp(skey, "Modalias")) + { + const char *val = NULL; + if (eldbus_message_iter_arguments_get(var, "s", &val)) + o->modalias = eina_stringshare_add(val); + } + if (!strcmp(skey, "Adapter")) + { + const char *val = NULL; + if (eldbus_message_iter_arguments_get(var, "o", &val)) + o->adapter = eina_stringshare_add(val); + } + if (!strcmp(skey, "Class")) + { + unsigned int val = 0; + if (eldbus_message_iter_arguments_get(var, "u", &val)) + o->klass = val; + } + if (!strcmp(skey, "Appearance")) + { + unsigned short val = 0; + if (eldbus_message_iter_arguments_get(var, "q", &val)) + o->appearance = val; + } + if (!strcmp(skey, "RSSI")) + { + short val = 0; + if (eldbus_message_iter_arguments_get(var, "n", &val)) + o->rssi = val; + } + if (!strcmp(skey, "TxPower")) + { + unsigned short val = 0; + if (eldbus_message_iter_arguments_get(var, "n", &val)) + o->txpower = val; + } + if (!strcmp(skey, "UUIDs")) + { + Eldbus_Message_Iter *array = NULL; + + if (eldbus_message_iter_arguments_get(var, "as", &array)) + { + const char *val = NULL; + + while (eldbus_message_iter_get_and_next(array, 's', &val)) + { + if (!o->uuids) o->uuids = eina_array_new(1); + eina_array_push(o->uuids, eina_stringshare_add(val)); + } + } + } + if (!strcmp(skey, "Discoverable")) + { + Eina_Bool val = EINA_FALSE; + if (eldbus_message_iter_arguments_get(var, "b", &val)) + o->discoverable = val; + } + if (!strcmp(skey, "Discovering")) + { + Eina_Bool val = EINA_FALSE; + if (eldbus_message_iter_arguments_get(var, "b", &val)) + o->discovering = val; + } + if (!strcmp(skey, "Pairable")) + { + Eina_Bool val = EINA_FALSE; + if (eldbus_message_iter_arguments_get(var, "b", &val)) + o->pairable = val; + } + if (!strcmp(skey, "Powered")) + { + Eina_Bool val = EINA_FALSE; + if (eldbus_message_iter_arguments_get(var, "b", &val)) + o->powered = val; + } + if (!strcmp(skey, "DiscoverableTimeout")) + { + unsigned int val = 0; + if (eldbus_message_iter_arguments_get(var, "u", &val)) + o->discoverable_timeout = val; + } + if (!strcmp(skey, "PairableTimeout")) + { + unsigned int val = 0; + if (eldbus_message_iter_arguments_get(var, "u", &val)) + o->pairable_timeout = val; + } + // dict ManufacturerData [readonly, optional] + // Manufacturer specific advertisement data. Keys are + // 16 bits Manufacturer ID followed by its byte array + // value. +/* + if (!strcmp(skey, "ManufacturerData")) + { + Eldbus_Message_Iter *array = NULL; + + if (eldbus_message_iter_arguments_get(var, "a{qv}", &array)) + eldbus_message_iter_dict_iterate(array, "qv", cb_obj_prop_mandata, o); + } + */ + // dict ServiceData [readonly, optional] + // Service advertisement data. Keys are the UUIDs in + // string format followed by its byte array value. + // + // array{byte} AdvertisingFlags [readonly, experimental] + // The Advertising Data Flags of the remote device. + // + // dict AdvertisingData [readonly, experimental] + // The Advertising Data of the remote device. Keys are + // are 8 bits AD Type followed by data as byte array. +} + +static void +_obj_clear(Obj *o) +{ + o->paired = EINA_FALSE; + o->connected = EINA_FALSE; + o->trusted = EINA_FALSE; + o->blocked = EINA_FALSE; + o->legacy_pairing = EINA_FALSE; + o->services_resolved = EINA_FALSE; + eina_stringshare_del(o->address); + o->address = NULL; + eina_stringshare_del(o->address_type); + o->address_type = NULL; + eina_stringshare_del(o->name); + o->name = NULL; + eina_stringshare_del(o->icon); + o->icon = NULL; + eina_stringshare_del(o->alias); + o->alias = NULL; + eina_stringshare_del(o->adapter); + o->adapter = NULL; + eina_stringshare_del(o->modalias); + o->modalias = NULL; + eina_stringshare_del(o->modalias); + o->modalias = NULL; + o->klass = 0; + o->appearance = 0; + o->txpower = 0; + o->rssi = 0; + if (o->uuids) + { + const char *val; + + while ((val = eina_array_pop(o->uuids))) + eina_stringshare_del(val); + eina_array_free(o->uuids); + o->uuids = NULL; + } +} + +#define ERR_PRINT(str) \ + do { const char *name, *text; \ + if (eldbus_message_error_get(msg, &name, &text)) { \ + printf("Error: %s.\n %s:\n %s\n", str, name, text); \ + return; \ + } \ + } while(0) + + +static void +cb_obj_prop(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + Obj *o = data; + Eldbus_Message_Iter *array; + + if (eldbus_message_error_get(msg, NULL, NULL)) return; + _obj_clear(o); + if (eldbus_message_arguments_get(msg, "a{sv}", &array)) + eldbus_message_iter_dict_iterate(array, "sv", cb_obj_prop_entry, o); + bz_obj_ref(o); + if (!o->add_called) + { + o->add_called = EINA_TRUE; + if (fn_obj_add) fn_obj_add(o); + } + if (o->fn_change) o->fn_change(o); + bz_obj_unref(o); +} + +static void +cb_obj_prop_changed(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED) +{ + Obj *o = data; + if (!o->proxy) return; + eldbus_proxy_property_get_all(o->proxy, cb_obj_prop, o); +} + +Obj * +bz_obj_add(const char *path) +{ + Eldbus_Object *obj; + + Obj *o = calloc(1, sizeof(Obj)); + o->ref = 1; + o->path = eina_stringshare_add(path); + obj = eldbus_object_get(bz_conn, "org.bluez", o->path); + o->type = BZ_OBJ_UNKNOWN; + o->in_table = EINA_TRUE; + eina_hash_add(obj_table, o->path, o); + if (!strcmp(o->path, "/org/bluez")) + { + o->proxy = eldbus_proxy_get(obj, "org.bluez.AgentManager1"); + o->type = BZ_OBJ_BLUEZ; + o->add_called = EINA_TRUE; + bz_obj_ref(o); + if (fn_obj_add) fn_obj_add(o); + bz_obj_unref(o); + goto done; + } + // all devices are /org/bluez/XXX/dev_XXX so look for /dev_ + else if (strstr(o->path, "/dev_")) + { + o->proxy = eldbus_proxy_get(obj, "org.bluez.Device1"); + o->type = BZ_OBJ_DEVICE; + if (o->proxy) + { + eldbus_proxy_property_get_all(o->proxy, cb_obj_prop, o); + o->prop_proxy = eldbus_proxy_get(obj, + "org.freedesktop.DBus.Properties"); + if (o->prop_proxy) + o->prop_sig = eldbus_proxy_signal_handler_add(o->prop_proxy, + "PropertiesChanged", + cb_obj_prop_changed, o); + } + goto done; + } + // all dadapters begin with /org/bluez/ + else if (!strncmp(o->path, "/org/bluez/", 11)) + { + o->proxy = eldbus_proxy_get(obj, "org.bluez.Adapter1"); + o->type = BZ_OBJ_ADAPTER; + if (o->proxy) + { + eldbus_proxy_property_get_all(o->proxy, cb_obj_prop, o); + o->prop_proxy = eldbus_proxy_get(obj, + "org.freedesktop.DBus.Properties"); + if (o->prop_proxy) + o->prop_sig = eldbus_proxy_signal_handler_add(o->prop_proxy, + "PropertiesChanged", + cb_obj_prop_changed, o); + } + goto done; + } +done: + return o; +} + +Obj * +bz_obj_find(const char *path) +{ + return eina_hash_find(obj_table, path); +} + +static void +cb_power_on(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Power On"); +} + +void +bz_obj_power_on(Obj *o) +{ + Eina_Bool val = EINA_TRUE; + if (!o->proxy) return; + eldbus_proxy_property_set + (o->proxy, "Powered", "b", (void *)(uintptr_t)val, cb_power_on, o); +} + +static void +cb_power_off(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Power Off"); +} + +void +bz_obj_power_off(Obj *o) +{ + Eina_Bool val = EINA_FALSE; + if (!o->proxy) return; + eldbus_proxy_property_set + (o->proxy, "Powered", "b", (void *)(uintptr_t)val, cb_power_off, o); +} + +static void +cb_discoverable(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Discoverable"); +} + +void +bz_obj_discoverable(Obj *o) +{ + Eina_Bool val = EINA_TRUE; + if (!o->proxy) return; + eldbus_proxy_property_set + (o->proxy, "Discoverable", "b", (void *)(uintptr_t)val, cb_discoverable, o); +} + +static void +cb_undiscoverable(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Undiscoverable"); +} + +void +bz_obj_undiscoverable(Obj *o) +{ + Eina_Bool val = EINA_FALSE; + if (!o->proxy) return; + eldbus_proxy_property_set + (o->proxy, "Discoverable", "b", (void *)(uintptr_t)val, cb_undiscoverable, o); +} + +static void +cb_pairable(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Pairable"); +} + +void +bz_obj_pairable(Obj *o) +{ + Eina_Bool val = EINA_TRUE; + if (!o->proxy) return; + eldbus_proxy_property_set + (o->proxy, "Pairable", "b", (void *)(uintptr_t)val, cb_pairable, o); +} + +static void +cb_unpairable(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Unpairable"); +} + +void +bz_obj_unpairable(Obj *o) +{ + Eina_Bool val = EINA_FALSE; + if (!o->proxy) return; + eldbus_proxy_property_set + (o->proxy, "Pairable", "b", (void *)(uintptr_t)val, cb_unpairable, o); +} + +static void +cb_trust(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Trust"); +} + +void +bz_obj_trust(Obj *o) +{ + Eina_Bool val = EINA_TRUE; + if (!o->proxy) return; + eldbus_proxy_property_set + (o->proxy, "Trusted", "b", (void *)(uintptr_t)val, cb_trust, o); +} + +static void +cb_distrust(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Distrust"); +} + +void +bz_obj_distrust(Obj *o) +{ + Eina_Bool val = EINA_FALSE; + if (!o->proxy) return; + eldbus_proxy_property_set + (o->proxy, "Trusted", "b", (void *)(uintptr_t)val, cb_distrust, o); +} + +static void +cb_pair(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Pair"); +} + +void +bz_obj_pair(Obj *o) +{ + if (!o->proxy) return; + eldbus_proxy_call(o->proxy, "Pair", cb_pair, o, -1, ""); +} + +static void +cb_pair_cancel(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Pair Cancel"); +} + +void +bz_obj_pair_cancel(Obj *o) +{ + if (!o->proxy) return; + eldbus_proxy_call + (o->proxy, "CancelPairing", cb_pair_cancel, o, -1, ""); +} + +static void +cb_connect(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Connect"); +} + +void +bz_obj_connect(Obj *o) +{ + if (!o->proxy) return; + eldbus_proxy_call + (o->proxy, "Connect", cb_connect, o, -1, ""); +} + +static void +cb_disconnect(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Disconnect"); +} + +void +bz_obj_disconnect(Obj *o) +{ + if (!o->proxy) return; + eldbus_proxy_call + (o->proxy, "Disconnect", cb_disconnect, o, -1, ""); +} +/* +void +bz_obj_profile_connect(Obj *o, const char *uuid) +{ + if (!o->proxy) return; + eldbus_proxy_call(o->proxy, "ConnectProfile", NULL, NULL, -1, "s", uuid); +} + +void +bz_obj_profile_disconnect(Obj *o, const char *uuid) +{ + if (!o->proxy) return; + eldbus_proxy_call(o->proxy, "DisconnectProfile", NULL, NULL, -1, "s", uuid); +} +*/ +static void +cb_remove(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Remove"); +} + +void +bz_obj_remove(Obj *o) +{ + if (o->adapter) + { + Obj *adapter = bz_obj_find(o->adapter); + if (adapter) + { + if (!adapter->proxy) return; + eldbus_proxy_call(adapter->proxy, "RemoveDevice", + cb_remove, adapter, -1, + "o", o->path); + } + } +} + +void +bz_obj_ref(Obj *o) +{ + o->ref++; +} + +void +bz_obj_unref(Obj *o) +{ + o->ref--; + if (o->ref > 0) return; + if (o->in_table) + { + o->in_table = EINA_FALSE; + eina_hash_del(obj_table, o->path, o); + } + _obj_clear(o); + if (o->prop_sig) + { + eldbus_signal_handler_del(o->prop_sig); + o->prop_sig = NULL; + } + if (o->proxy) + { + eldbus_proxy_unref(o->proxy); + o->proxy = NULL; + } + if (o->prop_proxy) + { + eldbus_proxy_unref(o->prop_proxy); + o->prop_proxy = NULL; + } + if (o->path) + { + eina_stringshare_del(o->path); + o->path = NULL; + } + if (o->agent_request) + { + eina_stringshare_del(o->agent_request); + o->agent_request = NULL; + } + if (o->agent_msg_err) + { + bz_agent_msg_drop(o->agent_msg_err); + o->agent_msg_err = NULL; + } + if (o->agent_msg_ok) + { + bz_agent_msg_drop(o->agent_msg_ok); + o->agent_msg_ok = NULL; + } + free(o); +} + +static void +cb_discovery_start(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Discovery Start"); +} + +void +bz_obj_discover_start(Obj *o) +{ + if (!o->proxy) return; + eldbus_proxy_call + (o->proxy, "StartDiscovery", cb_discovery_start, o, -1, ""); +} + +static void +cb_discovery_stop(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED) +{ + ERR_PRINT("Discovery Stop"); +} + +void +bz_obj_discover_stop(Obj *o) +{ + if (!o->proxy) return; + eldbus_proxy_call + (o->proxy, "StopDiscovery", cb_discovery_stop, o, -1, ""); +} + +void +bz_obj_agent_request(Obj *o, const char *req, void (*fn) (Eldbus_Message *msg, const char *str), Eldbus_Message *msg_ok, Eldbus_Message *msg_err) +{ + if (o->agent_msg_ok) bz_agent_msg_drop(o->agent_msg_ok); + if (o->agent_msg_err) bz_agent_msg_reply(o->agent_msg_err); + o->agent_msg_ok = msg_ok; + o->agent_msg_err = msg_err; + o->agent_entry_fn = fn; + o->agent_alert = EINA_TRUE; + eina_stringshare_replace(&(o->agent_request), req); + bz_obj_ref(o); + if (o->fn_change) o->fn_change(o); + bz_obj_unref(o); +} + +static void +cb_obj_add(void *data EINA_UNUSED, const Eldbus_Message *msg) +{ + const char *path = NULL; + + if (!eldbus_message_arguments_get(msg, "o", &path)) return; + if (bz_obj_find(path)) return; + bz_obj_add(path); +} + +static void +cb_obj_del(void *data EINA_UNUSED, const Eldbus_Message *msg) +{ + Obj *o; + const char *path = NULL; + + if (!eldbus_message_arguments_get(msg, "o", &path)) return; + o = bz_obj_find(path); + if (o) + { + bz_obj_ref(o); + if (o->fn_del) o->fn_del(o); + bz_obj_unref(o); + bz_obj_unref(o); + } +} + +static void +cb_getobj(void *data EINA_UNUSED, const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + Eldbus_Message_Iter *prop, *dict; + + pend_getobj = NULL; + ERR_PRINT("Get Objects"); + if (eldbus_message_arguments_get(msg, "a{oa{sa{sv}}}", &prop)) + { + while (eldbus_message_iter_get_and_next(prop, 'e', &dict)) + { + const char *path; + + path = NULL; + if (!eldbus_message_iter_arguments_get(dict, "o", &path)) + { + return; + } + bz_obj_add(path); + } + } +} + +static void +_obj_hash_free(Obj *o) +{ + o->in_table = 0; + bz_obj_unref(o); +} + +void +bz_obj_init(void) +{ + Eldbus_Object *obj; + + obj_table = eina_hash_string_superfast_new((void *)_obj_hash_free); + + obj = eldbus_object_get(bz_conn, "org.bluez", "/"); + objman_proxy = eldbus_proxy_get(obj, "org.freedesktop.DBus.ObjectManager"); + sig_ifadd = eldbus_proxy_signal_handler_add(objman_proxy, "InterfacesAdded", + cb_obj_add, NULL); + sig_ifdel = eldbus_proxy_signal_handler_add(objman_proxy, "InterfacesRemoved", + cb_obj_del, NULL); + pend_getobj = eldbus_proxy_call(objman_proxy, "GetManagedObjects", + cb_getobj, NULL, -1, ""); +} + +void +bz_obj_shutdown(void) +{ + eina_hash_free(obj_table); + obj_table = NULL; + if (pend_getobj) + { + eldbus_pending_cancel(pend_getobj); + pend_getobj = NULL; + } + if (sig_ifadd) + { + eldbus_signal_handler_del(sig_ifadd); + sig_ifadd = NULL; + } + if (sig_ifdel) + { + eldbus_signal_handler_del(sig_ifdel); + sig_ifdel = NULL; + } + eldbus_proxy_unref(objman_proxy); + objman_proxy = NULL; +} + +void +bz_obj_add_func_set(void (*fn) (Obj *o)) +{ + fn_obj_add = fn; +} diff --git a/src/modules/bluez5/e-module-bluez5.edj b/src/modules/bluez5/e-module-bluez5.edj new file mode 100644 index 000000000..383b4460e Binary files /dev/null and b/src/modules/bluez5/e-module-bluez5.edj differ diff --git a/src/modules/bluez5/e_mod_agent.c b/src/modules/bluez5/e_mod_agent.c new file mode 100644 index 000000000..a5524ad11 --- /dev/null +++ b/src/modules/bluez5/e_mod_agent.c @@ -0,0 +1,143 @@ +#include "e_mod_main.h" + +///////////////////////////////////////////////////////////////////////////// + +static void +cb_get_pass(Eldbus_Message *msg, const char *str) +{ + if (!str) return; + bz_agent_msg_str_add(msg, str); +} + +static void +cb_get_pin(Eldbus_Message *msg, const char *str) +{ + unsigned int pin; + if (!str) return; + pin = atoi(str); + bz_agent_msg_u32_add(msg, pin); +} + +void +ebluez5_agent_agent_release(void) +{ + // just debugging + printf("BZ5 Agent Release\n"); +} + +void +ebluez5_agent_agent_cancel(void) +{ + // just debugging + printf("BZ5 Agent Cancel\n"); +} + +void +ebluez5_agent_agent_req_pin(Eldbus_Message *msg) +{ + const char *path; + Obj *o; + + if (!(path = bz_agent_msg_path(msg))) goto error; + if (!(o = bz_obj_find(path))) goto error; + bz_obj_agent_request(o, _("Supply PIN"), cb_get_pass, + bz_agent_msg_ok(msg), bz_agent_msg_err(msg)); + return; +error: + bz_agent_msg_reply(bz_agent_msg_err(msg)); +} + +void +ebluez5_agent_agent_disp_pin(Eldbus_Message *msg) +{ + const char *path, *s = ""; + Obj *o; + char buf[1024]; + + if (!(path = bz_agent_msg_path_str(msg, &s))) goto error; + if (!(o = bz_obj_find(path))) goto error; + snprintf(buf, sizeof(buf), _("Pair? PIN: %s"), s); + bz_obj_agent_request(o, buf, NULL, + bz_agent_msg_ok(msg), bz_agent_msg_err(msg)); + return; +error: + bz_agent_msg_reply(bz_agent_msg_err(msg)); +} + +void +ebluez5_agent_req_pass(Eldbus_Message *msg) +{ + const char *path; + Obj *o; + + if (!(path = bz_agent_msg_path(msg))) goto error; + if (!(o = bz_obj_find(path))) goto error; + bz_obj_agent_request(o, _("Enter PIN"), cb_get_pin, + bz_agent_msg_ok(msg), bz_agent_msg_err(msg)); + return; +error: + bz_agent_msg_reply(bz_agent_msg_err(msg)); +} + +void +ebluez5_agent_disp_pass(Eldbus_Message *msg) +{ + const char *path; + unsigned int pin = 0; + unsigned short entered = 0; + Obj *o; + char buf[1024]; + + if (!(path = bz_agent_msg_path_u32_u16(msg, &pin, &entered))) goto error; + if (!(o = bz_obj_find(path))) goto error; + snprintf(buf, sizeof(buf), _("Pair? PIN: %06u"), pin); + bz_obj_agent_request(o, buf, NULL, + bz_agent_msg_ok(msg), bz_agent_msg_err(msg)); + return; +error: + bz_agent_msg_reply(bz_agent_msg_err(msg)); +} + +void +ebluez5_agent_req_confirm(Eldbus_Message *msg) +{ + const char *path; + unsigned int pin = 0; + Obj *o; + char buf[1024]; + + if (!(path = bz_agent_msg_path_u32(msg, &pin))) goto error; + if (!(o = bz_obj_find(path))) goto error; + snprintf(buf, sizeof(buf), _("Pair? PIN: %06u"), pin); + bz_obj_agent_request(o, buf, NULL, + bz_agent_msg_ok(msg), bz_agent_msg_err(msg)); + return; +error: + bz_agent_msg_reply(bz_agent_msg_err(msg)); +} + +void +ebluez5_agent_req_auth(Eldbus_Message *msg) +{ + const char *path; + Obj *o; + + if (!(path = bz_agent_msg_path(msg))) goto error; + if (!(o = bz_obj_find(path))) goto error; + bz_obj_agent_request(o, _("Connect?"), NULL, + bz_agent_msg_ok(msg), bz_agent_msg_err(msg)); + return; +error: + bz_agent_msg_reply(bz_agent_msg_err(msg)); +} + +void +ebluez5_agent_auth_service(Eldbus_Message *msg) +{ + // if "ask for service" on then ask, otherwise always auth + // always auth: +// if (!(path = bz_agent_msg_path_str(msg, &s))) goto error; + Eldbus_Message *ok; + ok = bz_agent_msg_ok(msg); + bz_agent_msg_reply(ok); +} diff --git a/src/modules/bluez5/e_mod_main.c b/src/modules/bluez5/e_mod_main.c new file mode 100644 index 000000000..6d779e0ea --- /dev/null +++ b/src/modules/bluez5/e_mod_main.c @@ -0,0 +1,305 @@ +#include "e_mod_main.h" + +static Eina_List *instances = NULL; +static E_Module *mod = NULL; + +/* Local config */ +static E_Config_DD *conf_edd = NULL; +Config *ebluez5_config = NULL; + +E_API E_Module_Api e_modapi = {E_MODULE_API_VERSION, "Bluez5"}; + +static void +_mod_icon_set(Evas_Object *base, Eina_Bool gadget) +{ + char edj_path[4096], *group; + + // XXX: hack for now until we make the icon do things and have it + // in theme in efl + snprintf(edj_path, sizeof(edj_path), "%s/e-module-bluez5.edj", mod->dir); + if (1) group = "e/modules/bluez5/main"; + else group = "e/modules/bluez5/inactive"; + + if (!e_theme_edje_object_set(base, "base/theme/modules/bluez5", group)) + { + if (gadget) + elm_layout_file_set(base, edj_path, group); + else + edje_object_file_set(base, edj_path, group); + } +} + +///////////////////////////////////////////////////////////////////////////// + +static void +_gad_popup_dismiss(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + Instance *inst = data; + E_FREE_FUNC(obj, evas_object_del); + inst->pop = NULL; +} + +static void +_gad_popup_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Instance *inst = data; + inst->pop = NULL; +} + +static void +_gad_popup_do(Instance *inst) +{ + Evas_Object *o; + + if (inst->pop) return; + + inst->pop = o = elm_ctxpopup_add(e_comp->elm); + elm_object_style_set(o, "noblock"); + evas_object_smart_callback_add(o, "dismissed", _gad_popup_dismiss, inst); + evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _gad_popup_del, inst); + + inst->popcontent = o = ebluez5_popup_content_add(e_comp->elm, inst); + elm_object_content_set(inst->pop, o); + evas_object_show(o); + + e_gadget_util_ctxpopup_place(inst->o_bluez5, inst->pop, inst->o_bluez5); + evas_object_show(inst->pop); +} + +static void +_gad_mouse_down(void *data, Evas *evas EINA_UNUSED, + Evas_Object *obj EINA_UNUSED, void *event) +{ + Instance *inst = data; + Evas_Event_Mouse_Down *ev = event; + + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; + if (ev->button != 1) return; + if (!inst->pop) _gad_popup_do(inst); + else elm_ctxpopup_dismiss(inst->pop); +} + +static void +_gad_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Instance *inst = data; + + instances = eina_list_remove(instances, inst); + E_FREE(inst); +} + +/* XXX: fill in later when we have gotten this far +static Evas_Object * +_gad_config(Evas_Object *g EINA_UNUSED) +{ + if (e_configure_registry_exists("extensions/bluez5")) + e_configure_registry_call("extensions/bluez5", NULL, NULL); + return NULL; +} +*/ + +static Evas_Object * +_gad_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient) +{ + Evas_Object *o; + Instance *inst = E_NEW(Instance, 1); + + if (!inst) return NULL; + inst->id = *id; + inst->orient = orient; + inst->o_bluez5 = o = elm_layout_add(parent); + _mod_icon_set(o, EINA_TRUE); + evas_object_size_hint_aspect_set(o, EVAS_ASPECT_CONTROL_BOTH, 1, 1); +// XXX: fill in later when we have gotten this far +// e_gadget_configure_cb_set(o, _gad_config); + evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, _gad_mouse_down, inst); + if (*id != -1) + evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _gad_del, inst); + instances = eina_list_append(instances, inst); + return o; +} + +///////////////////////////////////////////////////////////////////////////// + +static void +_popup_del(Instance *inst) +{ + E_FREE_FUNC(inst->popup, e_object_del); +} + +static void +_popup_del_cb(void *obj) +{ + _popup_del(e_object_data_get(obj)); +} + +static void +_popup_comp_del_cb(void *data, Evas_Object *obj EINA_UNUSED) +{ + _popup_del(data); +} + +static void +_popup_new(Instance *inst) +{ + inst->popup = e_gadcon_popup_new(inst->gcc, 0); + + e_gadcon_popup_content_set(inst->popup, ebluez5_popup_content_add(e_comp->elm, inst)); + e_comp_object_util_autoclose(inst->popup->comp_object, _popup_comp_del_cb, NULL, inst); + e_gadcon_popup_show(inst->popup); + e_object_data_set(E_OBJECT(inst->popup), inst); + E_OBJECT_DEL_SET(inst->popup, _popup_del_cb); +} + +static void +_ebluez5_cb_mouse_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) +{ + Instance *inst = data; + Evas_Event_Mouse_Down *ev = event; + + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; + if (ev->button != 1) return; + if (!inst->popup) _popup_new(inst); + else _popup_del(inst); +} + +static E_Gadcon_Client * +_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style) +{ + Evas_Object *o; + Instance *inst = E_NEW(Instance, 1); + + if (!inst) return NULL; + inst->o_bluez5 = o = edje_object_add(gc->evas); + _mod_icon_set(o, EINA_FALSE); + inst->gcc = e_gadcon_client_new(gc, name, id, style, o); + inst->gcc->data = inst; + e_gadcon_client_util_menu_attach(inst->gcc); + evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, _ebluez5_cb_mouse_down, inst); + instances = eina_list_append(instances, inst); + return inst->gcc; +} + +static void +_gc_shutdown(E_Gadcon_Client *gcc) +{ + Instance *inst = gcc->data;; + + if (!inst) return; + instances = eina_list_remove(instances, inst); + _popup_del(inst); + E_FREE_FUNC(inst->o_bluez5, evas_object_del); + E_FREE(inst); +} + +static const char * +_gc_id_new(const E_Gadcon_Client_Class *client_class EINA_UNUSED) +{ + static char tmpbuf[128]; + snprintf(tmpbuf, sizeof(tmpbuf), "bluez5.%d", eina_list_count(instances)); + return tmpbuf; +} + +static void +_gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient EINA_UNUSED) +{ + e_gadcon_client_aspect_set(gcc, 16, 16); + e_gadcon_client_min_size_set(gcc, 16, 16); +} + +static const char * +_gc_label(const E_Gadcon_Client_Class *client_class EINA_UNUSED) +{ + return _("Bluez5"); +} + +static Evas_Object * +_gc_icon(const E_Gadcon_Client_Class *client_class EINA_UNUSED, Evas *evas) +{ + Evas_Object *o = NULL; + char buf[4096]; + + snprintf(buf, sizeof(buf), "%s/e-module-bluez5.edj", mod->dir); + o = edje_object_add(evas); + edje_object_file_set(o, buf, "icon"); + return o; +} + +static const E_Gadcon_Client_Class _gc_class = { + GADCON_CLIENT_CLASS_VERSION, "bluez5", + {_gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon, _gc_id_new, NULL, NULL}, + E_GADCON_CLIENT_STYLE_PLAIN +}; + +///////////////////////////////////////////////////////////////////////////// +void +ebluez5_popups_show(void) +{ + Eina_List *l; + Instance *inst; + + EINA_LIST_FOREACH(instances, l, inst) + { + if (inst->gcc) + { + if (!inst->popup) _popup_new(inst); + } + else + { + if (!inst->pop) _gad_popup_do(inst); + } + } +} + +///////////////////////////////////////////////////////////////////////////// + +/* Module Functions */ +E_API void * +e_modapi_init(E_Module *m) +{ + mod = m; + + conf_edd = E_CONFIG_DD_NEW("Config", Config); +#undef T +#undef D +#define T Config +#define D conf_edd + E_CONFIG_VAL(D, T, lock_dev_addr, STR); + E_CONFIG_VAL(D, T, unlock_dev_addr, STR); + + ebluez5_config = e_config_domain_load("module.ebluez5", conf_edd); + if (!ebluez5_config) ebluez5_config = E_NEW(Config, 1); + + ebluze5_popup_init(); + bz_init(); + + e_gadcon_provider_register(&_gc_class); + e_gadget_type_add("Bluetooth", _gad_create, NULL); + + return m; +} + +E_API int +e_modapi_shutdown(E_Module *m EINA_UNUSED) +{ + E_CONFIG_DD_FREE(conf_edd); + + eina_stringshare_del(ebluez5_config->lock_dev_addr); + eina_stringshare_del(ebluez5_config->unlock_dev_addr); + free(ebluez5_config); + ebluez5_config = NULL; + + bz_shutdown(); + ebluze5_popup_shutdown(); + + e_gadget_type_del("Bluetooth"); + e_gadcon_provider_unregister(&_gc_class); + return 1; +} + +E_API int +e_modapi_save(E_Module *m EINA_UNUSED) +{ + e_config_domain_save("module.ebluez5", conf_edd, ebluez5_config); + return 1; +} diff --git a/src/modules/bluez5/e_mod_main.h b/src/modules/bluez5/e_mod_main.h new file mode 100644 index 000000000..269726638 --- /dev/null +++ b/src/modules/bluez5/e_mod_main.h @@ -0,0 +1,68 @@ +#ifndef E_MOD_MAIN_H +#define E_MOD_MAIN_H + +#include +#include "bz.h" + +typedef struct _Instance Instance; +struct _Instance +{ + // common info + Evas_Object *o_bluez5; + // e_gadcon info + E_Gadcon_Client *gcc; + E_Gadcon_Popup *popup; + // e_gadget info + Evas_Object *pop; + Evas_Object *popcontent; + int id; + E_Gadget_Site_Orient orient; +}; + +typedef struct _Config Config; +struct _Config +{ + const char *lock_dev_addr; + const char *unlock_dev_addr; +}; + +extern Config *ebluez5_config; + +E_API extern E_Module_Api e_modapi; + +E_API void *e_modapi_init(E_Module *m); +E_API int e_modapi_shutdown(E_Module *m); +E_API int e_modapi_save(E_Module *m); + +void ebluez5_popups_show(void); + +void ebluez5_popup_hide(Instance *inst); + +Evas_Object *ebluez5_popup_content_add(Evas_Object *base, Instance *inst); +void ebluze5_popup_init(void); +void ebluze5_popup_shutdown(void); +void ebluze5_popup_clear(void); +void ebluez5_popup_adapter_add(Obj *o); +void ebluez5_popup_adapter_del(Obj *o); +void ebluez5_popup_adapter_change(Obj *o); +void ebluez5_popup_device_add(Obj *o); +void ebluez5_popup_device_del(Obj *o); +void ebluez5_popup_device_change(Obj *o); + +void ebluez5_agent_agent_release(void); +void ebluez5_agent_agent_cancel(void); +void ebluez5_agent_agent_req_pin(Eldbus_Message *msg); +void ebluez5_agent_agent_disp_pin(Eldbus_Message *msg); +void ebluez5_agent_req_pass(Eldbus_Message *msg); +void ebluez5_agent_disp_pass(Eldbus_Message *msg); +void ebluez5_agent_req_confirm(Eldbus_Message *msg); +void ebluez5_agent_req_auth(Eldbus_Message *msg); +void ebluez5_agent_auth_service(Eldbus_Message *msg); + +Evas_Object *util_obj_icon_add(Evas_Object *base, Obj *o, int size); +Evas_Object *util_obj_icon_rssi_add(Evas_Object *base, Obj *o, int size); +Evas_Object *util_check_add(Evas_Object *base, const char *text, const char *tip, Eina_Bool state); +Evas_Object *util_button_icon_add(Evas_Object *base, const char *icon, const char *tip); +const char *util_obj_name_get(Obj *o); + +#endif diff --git a/src/modules/bluez5/e_mod_popup.c b/src/modules/bluez5/e_mod_popup.c new file mode 100644 index 000000000..8152802f0 --- /dev/null +++ b/src/modules/bluez5/e_mod_popup.c @@ -0,0 +1,714 @@ +#include "e_mod_main.h" + +static Elm_Genlist_Item_Class *adapt_itc = NULL; +static Elm_Genlist_Item_Class *dev_itc = NULL; +static Elm_Genlist_Item_Class *group_itc = NULL; +static Eina_List *lists = NULL; +static Eina_List *adapters = NULL; +static Eina_List *devices = NULL; + +static void +_adapter_add(Evas_Object *gl, Obj *o) +{ + Elm_Object_Item *it = evas_object_data_get(gl, "adapters_item");; + elm_genlist_item_append(gl, adapt_itc, o, it, ELM_GENLIST_ITEM_NONE, + NULL, NULL); +} + +static int +_cb_insert_cmp(const void *ai, const void *bi) +{ + Obj *a = elm_object_item_data_get(ai); + Obj *b = elm_object_item_data_get(bi); + Eina_Bool apub = EINA_FALSE, bpub = EINA_FALSE; + + if ((!a) || (!a->address)) return -1; + if ((!b) || (!b->address)) return 1; + + // prefer paired at top + if ((a->paired) && (!b->paired)) return -1; + if ((!a->paired) && (b->paired)) return 1; + // prefer public addresses next after being paired + if ((a->address_type) && (!strcmp(a->address_type, "public"))) + apub = EINA_TRUE; + if ((b->address_type) && (!strcmp(b->address_type, "public"))) + bpub = EINA_TRUE; + if ((apub) && (!bpub)) return -1; + if ((!apub) && (bpub)) return 1; + // and sort by address + return strcmp(a->address, b->address); +} + +static void +_device_add(Evas_Object *gl, Obj *o) +{ + Elm_Object_Item *it = evas_object_data_get(gl, "devices_item");; + + elm_genlist_item_sorted_insert(gl, dev_itc, o, it, ELM_GENLIST_ITEM_NONE, + _cb_insert_cmp, NULL, NULL); +} + +static void +_cb_power(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + if (elm_check_state_get(obj)) bz_obj_power_on(o); + else bz_obj_power_off(o); +} + +static void +_cb_scan(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + if (elm_check_state_get(obj)) bz_obj_discover_start(o); + else bz_obj_discover_stop(o); +} + +static void +_cb_visible(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + if (elm_check_state_get(obj)) bz_obj_discoverable(o); + else bz_obj_undiscoverable(o); +} + +static void +_cb_pairable(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + if (elm_check_state_get(obj)) bz_obj_pairable(o); + else bz_obj_unpairable(o); +} + +static void +_cb_connect(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + bz_obj_connect(o); +} + +static void +_cb_disconnect(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + bz_obj_disconnect(o); +} + +static void +_cb_trust(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + bz_obj_trust(o); +} + +static void +_cb_distrust(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + bz_obj_distrust(o); +} + +static void +_cb_pair(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + bz_obj_pair(o); +} + +static void +_cb_unpair(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + bz_obj_remove(o); +} + +static void +_agent_done(Obj *o) +{ + Eina_List *l; + Evas_Object *gl; + Elm_Object_Item *it; + + if (o->agent_request) + { + eina_stringshare_del(o->agent_request); + o->agent_request = NULL; + } + EINA_LIST_FOREACH(lists, l, gl) + { + for (it = elm_genlist_first_item_get(gl); it; + it = elm_genlist_item_next_get(it)) + { + if (o == elm_object_item_data_get(it)) + { + elm_genlist_item_update(it); + break; + } + } + } +} + +static void +_cb_agent_ok(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + Obj *o = data; + + if ((o->agent_entry_fn) && (o->agent_msg_ok)) + { + Evas_Object *en = evas_object_data_get(obj, "entry"); + + if (en) + { + const char *s = elm_object_text_get(en); + + if (s) o->agent_entry_fn(o->agent_msg_ok, s); + } + } + if (o->agent_msg_err) + { + bz_agent_msg_drop(o->agent_msg_err); + o->agent_msg_err = NULL; + } + if (o->agent_msg_ok) + { + bz_agent_msg_reply(o->agent_msg_ok); + o->agent_msg_ok = NULL; + } + _agent_done(o); +} + +static void +_cb_agent_cancel(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Obj *o = data; + if (o->agent_msg_ok) + { + bz_agent_msg_drop(o->agent_msg_ok); + o->agent_msg_ok = NULL; + } + if (o->agent_msg_err) + { + bz_agent_msg_reply(o->agent_msg_err); + o->agent_msg_err = NULL; + } + _agent_done(o); +} + +static char * +_cb_group_text_get(void *data, Evas_Object *obj EINA_UNUSED, + const char *part EINA_UNUSED) +{ + if (!data) return strdup(_("Adapters")); + return strdup(_("Devices")); +} + +static Evas_Object * +_cb_group_content_get(void *data EINA_UNUSED, Evas_Object *obj, + const char *part EINA_UNUSED) +{ + if (!strcmp(part, "elm.swallow.icon")) + { + Evas_Object *ic = elm_icon_add(obj); + if (!data) + elm_icon_standard_set(ic, "computer"); + else + elm_icon_standard_set(ic, "system-run"); + evas_object_size_hint_min_set(ic, + ELM_SCALE_SIZE(16), + ELM_SCALE_SIZE(16)); + return ic; + } + return NULL; +} + +static char * +_cb_adapt_text_get(void *data, Evas_Object *obj EINA_UNUSED, + const char *part EINA_UNUSED) +{ + Obj *o = data; + + if (!strcmp(part, "elm.text")) + { + return strdup(util_obj_name_get(o)); + } + else if (!strcmp(part, "elm.text.sub")) + { + if (o->address) return strdup(o->address); + return strdup(_("Unknown Address")); + } + return NULL; +} + +static Evas_Object * +_cb_adapt_content_get(void *data EINA_UNUSED, Evas_Object *obj, + const char *part EINA_UNUSED) +{ + Obj *o = data; + + if (!strcmp(part, "elm.swallow.icon")) + { + return util_obj_icon_add(obj, o, 48); + } + else if (!strcmp(part, "elm.swallow.end")) + { + Evas_Object *tab, *ck; + + tab = elm_table_add(obj); + evas_object_size_hint_weight_set(tab, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(tab, EVAS_HINT_FILL, 0.0); + + ck = util_check_add(obj, _("Power"), _("Enable power for this adapter"), + o->powered); + evas_object_smart_callback_add(ck, "changed", _cb_power, o); + elm_table_pack(tab, ck, 0, 0, 1, 1); + evas_object_show(ck); + + ck = util_check_add(obj, _("Visible"), _("Make this adapter visible to other devices"), + o->discoverable); + evas_object_smart_callback_add(ck, "changed", _cb_visible, o); + elm_table_pack(tab, ck, 1, 0, 1, 1); + evas_object_show(ck); + + ck = util_check_add(obj, _("Scan"), _("Scan for other devices"), + o->discovering); + evas_object_smart_callback_add(ck, "changed", _cb_scan, o); + elm_table_pack(tab, ck, 0, 1, 1, 1); + evas_object_show(ck); + + ck = util_check_add(obj, _("Pairable"), _("Allow this adapter to have other devices request to pair with it"), + o->pairable); + evas_object_smart_callback_add(ck, "changed", _cb_pairable, o); + elm_table_pack(tab, ck, 1, 1, 1, 1); + evas_object_show(ck); + + return tab; + } + return NULL; +} + +static char * +_cb_dev_text_get(void *data, Evas_Object *obj EINA_UNUSED, + const char *part EINA_UNUSED) +{ + Obj *o = data; + return strdup(util_obj_name_get(o)); +} + +static Evas_Object * +_cb_dev_content_get(void *data EINA_UNUSED, Evas_Object *obj, + const char *part EINA_UNUSED) +{ + Obj *o = data; + char buf[512]; + + if (!strcmp(part, "elm.swallow.icon")) + { + Evas_Object *ic, *bx; + + bx = elm_box_add(obj); + ic = util_obj_icon_add(obj, o, 24); + snprintf(buf, sizeof(buf), + _("Address: %s (%s)
" + "Services: %s%s%s%s%s%s%s%s%s
" + "Trusted: %s
" + "Blocked: %s
" + ) + , + (o->address) ? o->address : _("Unknown"), + (o->address_type) ? o->address_type : _("Unknown"), + (o->klass & BZ_OBJ_CLASS_SERVICE_LIMITED_DISCOVERABLE) ? _("Limited-Discoverable ") : "", + (o->klass & BZ_OBJ_CLASS_SERVICE_POSITIONING_BIT) ? _("Positioning ") : "", + (o->klass & BZ_OBJ_CLASS_SERVICE_NETWORKING_BIT) ? _("Networking ") : "", + (o->klass & BZ_OBJ_CLASS_SERVICE_RENDERING_BIT) ? _("Rendering ") : "", + (o->klass & BZ_OBJ_CLASS_SERVICE_CAPTURING_BIT) ? _("Capture ") : "", + (o->klass & BZ_OBJ_CLASS_SERVICE_OBJECT_TRANSFER_BIT) ? _("OBEX ") : "", + (o->klass & BZ_OBJ_CLASS_SERVICE_AUDIO_BIT) ? _("Audio ") : "", + (o->klass & BZ_OBJ_CLASS_SERVICE_TELEPHONY_BIT) ? _("Telephony ") : "", + (o->klass & BZ_OBJ_CLASS_SERVICE_INFORMATION_BIT) ? _("Information ") : "", + (o->trusted) ? _("Yes") : _("No"), + (o->blocked) ? _("Yes") : _("No") + ); + elm_object_tooltip_text_set(ic, buf); + elm_box_pack_end(bx, ic); + evas_object_show(ic); + return bx; + } + else if (!strcmp(part, "elm.swallow.end")) + { + Evas_Object *bx, *ic, *bt, *lb, *tb, *en, *rec; + + bx = elm_box_add(obj); + elm_box_horizontal_set(bx, EINA_TRUE); + if (o->paired) + { + if (o->connected) + { + bt = util_button_icon_add(obj, "network-offline", + _("Disconnect this device")); + evas_object_smart_callback_add(bt, "clicked", _cb_disconnect, o); + } + else + { + bt = util_button_icon_add(obj, "network-transmit-receive", + _("Connect this device")); + evas_object_smart_callback_add(bt, "clicked", _cb_connect, o); + } + elm_box_pack_end(bx, bt); + evas_object_show(bt); + + if (o->trusted) + { + bt = util_button_icon_add(obj, "security-low", + _("Disrust this device")); + evas_object_smart_callback_add(bt, "clicked", _cb_distrust, o); + } + else + { + bt = util_button_icon_add(obj, "security-high", + _("Trust this device")); + evas_object_smart_callback_add(bt, "clicked", _cb_trust, o); + } + elm_box_pack_end(bx, bt); + evas_object_show(bt); + } + if (!o->paired) + { + if (o->agent_request) + { + if (o->agent_entry_fn) + { + tb = elm_table_add(obj); + + rec = evas_object_rectangle_add(evas_object_evas_get(obj)); + evas_object_size_hint_min_set(rec, ELM_SCALE_SIZE(80), ELM_SCALE_SIZE(1)); + elm_table_pack(tb, rec, 0, 0, 1, 1); + + en = elm_entry_add(obj); + elm_entry_single_line_set(en, EINA_TRUE); + elm_entry_scrollable_set(en, EINA_TRUE); + elm_scroller_policy_set(en, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF); + elm_object_part_text_set(en, "guide", o->agent_request); +// elm_entry_password_set(en, EINA_TRUE); + evas_object_smart_callback_add(en, "activated", _cb_agent_ok, o); + evas_object_smart_callback_add(en, "aborted", _cb_agent_cancel, o); + elm_table_pack(tb, en, 0, 0, 1, 1); + evas_object_show(en); + + elm_box_pack_end(bx, tb); + evas_object_show(tb); + + bt = util_button_icon_add(obj, "list-add", + _("Pair with this device")); + evas_object_data_set(bt, "entry", en); + evas_object_smart_callback_add(bt, "clicked", _cb_agent_ok, o); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + + bt = util_button_icon_add(obj, "list-remove", + _("Reject pairing")); + evas_object_smart_callback_add(bt, "clicked", _cb_agent_cancel, o); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + } + else + { + lb = elm_label_add(obj); + elm_layout_text_set(lb, NULL, o->agent_request); + elm_box_pack_end(bx, lb); + evas_object_show(lb); + + bt = util_button_icon_add(obj, "list-add", + _("Pair with this device")); + evas_object_smart_callback_add(bt, "clicked", _cb_agent_ok, o); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + + bt = util_button_icon_add(obj, "list-remove", + _("Reject pairing")); + evas_object_smart_callback_add(bt, "clicked", _cb_agent_cancel, o); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + } + } + else + { + bt = util_button_icon_add(obj, "list-add", + _("Pair with this device")); + evas_object_smart_callback_add(bt, "clicked", _cb_pair, o); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + } + } + else + { + bt = util_button_icon_add(obj, "list-remove", + _("Unpair with this device")); + evas_object_smart_callback_add(bt, "clicked", _cb_unpair, o); + elm_box_pack_end(bx, bt); + evas_object_show(bt); + } + + ic = util_obj_icon_rssi_add(obj, o, 24); + elm_box_pack_end(bx, ic); + evas_object_show(ic); + return bx; + } + return NULL; +} + +static void +_cb_list_del(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +{ + lists = eina_list_remove(lists, obj); +} + +/* +static void +_cb_settings(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Instance *inst = data; + ebluez5_popup_hide(inst); +} +*/ + +Evas_Object * +ebluez5_popup_content_add(Evas_Object *base, Instance *inst) +{ + Evas_Object *o, *box, *tab, *gl; + Eina_List *l; + Elm_Object_Item *it; + Obj *oo; + + o = box = elm_box_add(base); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL); + + tab = o = elm_table_add(base); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL); + + o = evas_object_rectangle_add(evas_object_evas_get(base)); + evas_object_size_hint_min_set(o, ELM_SCALE_SIZE(320), ELM_SCALE_SIZE(240)); + evas_object_size_hint_max_set(o, ELM_SCALE_SIZE(560), ELM_SCALE_SIZE(400)); + elm_table_pack(tab, o, 0, 0, 1, 1); + + o = gl = elm_genlist_add(base); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_genlist_mode_set(o, ELM_LIST_LIMIT); + elm_genlist_select_mode_set(o, ELM_OBJECT_SELECT_MODE_NONE); + + lists = eina_list_append(lists, gl); + evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _cb_list_del, inst); + + it = elm_genlist_item_append(gl, group_itc, NULL, NULL, + ELM_GENLIST_ITEM_GROUP, NULL, NULL); + evas_object_data_set(gl, "adapters_item", it); + it = elm_genlist_item_append(gl, group_itc, gl, NULL, + ELM_GENLIST_ITEM_GROUP, NULL, NULL); + evas_object_data_set(gl, "devices_item", it); + + EINA_LIST_FOREACH(adapters, l, oo) + { + _adapter_add(gl, oo); + } + EINA_LIST_FOREACH(devices, l, oo) + { + _device_add(gl, oo); + } + + elm_table_pack(tab, o, 0, 0, 1, 1); + evas_object_show(o); + + elm_box_pack_end(box, tab); + evas_object_show(tab); +/* + o = elm_separator_add(base); + elm_separator_horizontal_set(o, EINA_TRUE); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_box_pack_end(box, o); + evas_object_show(o); + + o = elm_button_add(base); + evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_layout_text_set(o, NULL, _("Settings")); + elm_object_tooltip_text_set(o, _("Bring up more detailed Bluetooth settings")); + evas_object_smart_callback_add(o, "clicked", _cb_settings, inst); + elm_box_pack_end(box, o); + evas_object_show(o); +*/ + return box; +} + +void +ebluze5_popup_init(void) +{ + adapt_itc = elm_genlist_item_class_new(); + adapt_itc->item_style = "double_label"; + adapt_itc->func.text_get = _cb_adapt_text_get; + adapt_itc->func.content_get = _cb_adapt_content_get; + adapt_itc->func.state_get = NULL; + adapt_itc->func.del = NULL; + + dev_itc = elm_genlist_item_class_new(); + dev_itc->item_style = "default"; + dev_itc->func.text_get = _cb_dev_text_get; + dev_itc->func.content_get = _cb_dev_content_get; + dev_itc->func.state_get = NULL; + dev_itc->func.del = NULL; + + group_itc = elm_genlist_item_class_new(); + group_itc->item_style = "group_index"; + group_itc->func.text_get = _cb_group_text_get; + group_itc->func.content_get = _cb_group_content_get; + group_itc->func.state_get = NULL; + group_itc->func.del = NULL; +} + +void +ebluze5_popup_shutdown(void) +{ + ebluze5_popup_clear(); + elm_genlist_item_class_free(group_itc); + elm_genlist_item_class_free(dev_itc); + elm_genlist_item_class_free(adapt_itc); + group_itc = NULL; + dev_itc = NULL; + adapt_itc = NULL; +} + +void +ebluze5_popup_clear(void) +{ + Eina_List *l; + Evas_Object *gl; + + adapters = eina_list_free(adapters); + devices = eina_list_free(devices); + EINA_LIST_FOREACH(lists, l, gl) + { + elm_genlist_clear(gl); + } +} + +void +ebluez5_popup_adapter_add(Obj *o) +{ + Eina_List *l; + Evas_Object *gl; + + adapters = eina_list_append(adapters, o); + EINA_LIST_FOREACH(lists, l, gl) + { + _adapter_add(gl, o); + } +} + +void +ebluez5_popup_adapter_del(Obj *o) +{ + Eina_List *l; + Evas_Object *gl; + Elm_Object_Item *it; + + EINA_LIST_FOREACH(lists, l, gl) + { + for (it = elm_genlist_first_item_get(gl); it; + it = elm_genlist_item_next_get(it)) + { + if (o == elm_object_item_data_get(it)) + { + elm_object_item_del(it); + break; + } + } + } + adapters = eina_list_remove(adapters, o); +} + +void +ebluez5_popup_adapter_change(Obj *o) +{ + Eina_List *l; + Evas_Object *gl; + Elm_Object_Item *it; + + EINA_LIST_FOREACH(lists, l, gl) + { + for (it = elm_genlist_first_item_get(gl); it; + it = elm_genlist_item_next_get(it)) + { + if (o == elm_object_item_data_get(it)) + { + elm_genlist_item_update(it); + break; + } + } + } +} + +void +ebluez5_popup_device_add(Obj *o) +{ + Eina_List *l; + Evas_Object *gl; + + devices = eina_list_append(devices, o); + EINA_LIST_FOREACH(lists, l, gl) + { + _device_add(gl, o); + } +} + +void +ebluez5_popup_device_del(Obj *o) +{ + Eina_List *l; + Evas_Object *gl; + Elm_Object_Item *it; + + EINA_LIST_FOREACH(lists, l, gl) + { + for (it = elm_genlist_first_item_get(gl); it; + it = elm_genlist_item_next_get(it)) + { + if (o == elm_object_item_data_get(it)) + { + elm_object_item_del(it); + break; + } + } + } + devices = eina_list_remove(devices, o); +} + +void +ebluez5_popup_device_change(Obj *o) +{ + Eina_List *l; + Evas_Object *gl; + Elm_Object_Item *it; + Eina_Bool alert = EINA_FALSE; + + if (o->agent_alert) + { + alert = EINA_TRUE; + o->agent_alert = EINA_FALSE; + if (!lists) ebluez5_popups_show(); + } + EINA_LIST_FOREACH(lists, l, gl) + { + for (it = elm_genlist_first_item_get(gl); it; + it = elm_genlist_item_next_get(it)) + { + if (o == elm_object_item_data_get(it)) + { + elm_genlist_item_update(it); + if (alert) + elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_MIDDLE); + break; + } + } + } +} diff --git a/src/modules/bluez5/e_mod_util.c b/src/modules/bluez5/e_mod_util.c new file mode 100644 index 000000000..aedd37d6a --- /dev/null +++ b/src/modules/bluez5/e_mod_util.c @@ -0,0 +1,262 @@ +#include "e_mod_main.h" + +Evas_Object * +util_obj_icon_add(Evas_Object *base, Obj *o, int size) +{ + Evas_Object *ic = elm_icon_add(base); + unsigned int maj, min; + const char *s ="bluetooth-active"; + + // XXX: replace this with a much better database... + maj = o->klass & BZ_OBJ_CLASS_MAJ_MASK; + if (maj == BZ_OBJ_CLASS_MAJ_MISC) + { + s ="bluetooth-active"; + } + else if (maj == BZ_OBJ_CLASS_MAJ_COMPUTER) + { + min = o->klass & BZ_OBJ_CLASS_MIN_COMPUTER_MASK; + if (min == BZ_OBJ_CLASS_MIN_COMPUTER_DESKTOP) + s = "computer"; + else if (min == BZ_OBJ_CLASS_MIN_COMPUTER_SERVER) + s = "computer"; + else if (min == BZ_OBJ_CLASS_MIN_COMPUTER_LAPTOP) + s = "computer-laptop"; + else if (min == BZ_OBJ_CLASS_MIN_COMPUTER_CLAMSHELL) + s = "computer-laptop"; + else if (min == BZ_OBJ_CLASS_MIN_COMPUTER_PDA) + s = "pda"; + else if (min == BZ_OBJ_CLASS_MIN_COMPUTER_WEARABLE) + s = "pda"; + else if (min == BZ_OBJ_CLASS_MIN_COMPUTER_TABLET) + s = "pda"; + } + else if (maj == BZ_OBJ_CLASS_MAJ_PHONE) + { + min = o->klass & BZ_OBJ_CLASS_MIN_PHONE_MASK; + if (min == BZ_OBJ_CLASS_MIN_PHONE_CELL) + s = "phone"; + else if (min == BZ_OBJ_CLASS_MIN_PHONE_CORDLESS) + s = "phone"; + else if (min == BZ_OBJ_CLASS_MIN_PHONE_SMARTPHONE) + s = "phone"; + else if (min == BZ_OBJ_CLASS_MIN_PHONE_WIRED) + s = "modem"; + else if (min == BZ_OBJ_CLASS_MIN_PHONE_ISDN) + s = "modem"; + } + else if (maj == BZ_OBJ_CLASS_MAJ_LAN) + { + s = "network-wired"; + // XXX: handle (top is max availability) + // BZ_OBJ_CLASS_MIN_LAN_AVAIL_7 + // BZ_OBJ_CLASS_MIN_LAN_AVAIL_6 + // BZ_OBJ_CLASS_MIN_LAN_AVAIL_5 + // BZ_OBJ_CLASS_MIN_LAN_AVAIL_4 + // BZ_OBJ_CLASS_MIN_LAN_AVAIL_3 + // BZ_OBJ_CLASS_MIN_LAN_AVAIL_2 + // BZ_OBJ_CLASS_MIN_LAN_AVAIL_1 + // BZ_OBJ_CLASS_MIN_LAN_AVAIL_0 + } + else if (maj == BZ_OBJ_CLASS_MAJ_AV) + { + min = o->klass & BZ_OBJ_CLASS_MIN_AV_MASK; + if (min == BZ_OBJ_CLASS_MIN_AV_WEARABLE_HEADSET) + s = "audio-input-microphone"; + else if (min == BZ_OBJ_CLASS_MIN_AV_HANDS_FREE) + s = "audio-input-microphone"; + else if (min == BZ_OBJ_CLASS_MIN_AV_MIC) + s = "audio-input-microphone"; + else if (min == BZ_OBJ_CLASS_MIN_AV_SPEAKER) + s = "audio-volume-high"; + else if (min == BZ_OBJ_CLASS_MIN_AV_HEADPHONES) + s = "audio-volume-high"; + else if (min == BZ_OBJ_CLASS_MIN_AV_PORTABLE_AUDIO) + s = "audio-volume-high"; + else if (min == BZ_OBJ_CLASS_MIN_AV_CAR_AUDIO) + s = "audio-volume-high"; + else if (min == BZ_OBJ_CLASS_MIN_AV_SET_TOP_BOX) + s = "modem"; + else if (min == BZ_OBJ_CLASS_MIN_AV_HIFI_AUDIO) + s = "audio-volume-high"; + else if (min == BZ_OBJ_CLASS_MIN_AV_VCR) + s = "media-tape"; + else if (min == BZ_OBJ_CLASS_MIN_AV_VIDEO_CAMERA) + s = "camera-photo"; + else if (min == BZ_OBJ_CLASS_MIN_AV_CAMCORDER) + s = "camera-photo"; + else if (min == BZ_OBJ_CLASS_MIN_AV_VIDEO_MONITOR) + s = "video-display"; + else if (min == BZ_OBJ_CLASS_MIN_AV_VIDEO_DISPLAY_SPEAKER) + s = "video-display"; + else if (min == BZ_OBJ_CLASS_MIN_AV_VIDEO_CONFERENCE) + s = "video-display"; + else if (min == BZ_OBJ_CLASS_MIN_AV_GAMING) + s = "input-gaming"; + } + else if (maj == BZ_OBJ_CLASS_MAJ_PERIPHERAL) + { + s = "input-keyboard"; + + // XXX: handle bits + ide below + if (o->klass & BZ_OBJ_CLASS_MIN_PERIPHERAL_KEYBOARD_BIT) + s = "input-keyboard"; + else if (o->klass & BZ_OBJ_CLASS_MIN_PERIPHERAL_MOUSE_BIT) + s = "input-mouse"; + + min = o->klass & BZ_OBJ_CLASS_MIN_PERIPHERAL_MASK2; + if (min == BZ_OBJ_CLASS_MIN_PERIPHERAL_JOYSTICK) + s = "input-gaming"; + else if (min == BZ_OBJ_CLASS_MIN_PERIPHERAL_GAMEPAD) + s = "input-gaming"; + else if (min == BZ_OBJ_CLASS_MIN_PERIPHERAL_REMOTE) + s = "input-gaming"; + else if (min == BZ_OBJ_CLASS_MIN_PERIPHERAL_SENSING) + s = "input-gaming"; + else if (min == BZ_OBJ_CLASS_MIN_PERIPHERAL_DIGITIZER_TAB) + s = "input-tablet"; + else if (min == BZ_OBJ_CLASS_MIN_PERIPHERAL_CARD_READER) + s = "media-flash"; + else if (min == BZ_OBJ_CLASS_MIN_PERIPHERAL_PEN) + s = "input-mouse"; + else if (min == BZ_OBJ_CLASS_MIN_PERIPHERAL_SCANNER) + s = "scanner"; + else if (min == BZ_OBJ_CLASS_MIN_PERIPHERAL_WAND) + s = "input-mouse"; + } + else if (maj == BZ_OBJ_CLASS_MAJ_IMAGING) + { + // XXX: handle permutations of bits + if (o->klass & BZ_OBJ_CLASS_MIN_IMAGING_CAMERA_BIT) + s = "camera-photo"; + else if (o->klass & BZ_OBJ_CLASS_MIN_IMAGING_SCANNER_BIT) + s = "scanner"; + else if (o->klass & BZ_OBJ_CLASS_MIN_IMAGING_PRINTER_BIT) + s = "printer"; + } + else if (maj == BZ_OBJ_CLASS_MAJ_WEARABLE) + { + min = o->klass & BZ_OBJ_CLASS_MIN_WEARABLE_MASK; + if (min == BZ_OBJ_CLASS_MIN_WEARABLE_WATCH) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_WEARABLE_PAGER) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_WEARABLE_JACKET) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_WEARABLE_HELMET) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_WEARABLE_GLASSES) + s = "cpu"; + } + else if (maj == BZ_OBJ_CLASS_MAJ_TOY) + { + min = o->klass & BZ_OBJ_CLASS_MIN_TOY_MASK; + if (min == BZ_OBJ_CLASS_MIN_TOY_ROBOT) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_TOY_VEHICLE) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_TOY_DOLL) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_TOY_CONTROLLER) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_TOY_GAME) + s = "cpu"; + } + else if (maj == BZ_OBJ_CLASS_MAJ_HEALTH) + { + min = o->klass & BZ_OBJ_CLASS_MIN_HEALTH_MASK; + if (min == BZ_OBJ_CLASS_MIN_HEALTH_BLOOD_PRESSURE) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_THERMOMETER) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_SCALES) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_GLUCOSE) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_PULSE_OXIMITER) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_HEART_RATE) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_HEALTH_DATA_DISP) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_STEP) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_BODY_COMPOSITION) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_PEAK_FLOW) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_MEDICATION) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_KNEE_PROSTHESIS) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_ANKLE_PROSTHESIS) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_GENERIC_HEALTH) + s = "cpu"; + else if (min == BZ_OBJ_CLASS_MIN_HEALTH_PRESONAL_MOBILITY) + s = "cpu"; + } + elm_icon_standard_set(ic, s); + evas_object_size_hint_min_set(ic, + ELM_SCALE_SIZE(size), + ELM_SCALE_SIZE(size)); + return ic; +} + +Evas_Object * +util_obj_icon_rssi_add(Evas_Object *base, Obj *o, int size) +{ + Evas_Object *ic = elm_icon_add(base); + char buf[64]; + if (o->rssi <= -80) + elm_icon_standard_set(ic, "network-cellular-signal-excellent"); + else if (o->rssi <= -72) + elm_icon_standard_set(ic, "network-cellular-signal-good"); + else if (o->rssi <= -64) + elm_icon_standard_set(ic, "network-cellular-signal-ok"); + else if (o->rssi <= -56) + elm_icon_standard_set(ic, "network-cellular-signal-weak"); + else if (o->rssi <= -48) + elm_icon_standard_set(ic, "network-cellular-signal-none"); + else + elm_icon_standard_set(ic, "network-cellular-signal-acquiring"); + snprintf(buf, sizeof(buf), "RSSI: %i", (int)o->rssi); + elm_object_tooltip_text_set(ic, buf); + evas_object_size_hint_min_set(ic, + ELM_SCALE_SIZE(size), + ELM_SCALE_SIZE(size)); + return ic; +} + +Evas_Object * +util_check_add(Evas_Object *base, const char *text, const char *tip, Eina_Bool state) +{ + Evas_Object *ck = elm_check_add(base); + evas_object_size_hint_align_set(ck, 0.0, EVAS_HINT_FILL); + elm_layout_text_set(ck, NULL, text); + elm_object_tooltip_text_set(ck, tip); + elm_check_state_set(ck, state); + return ck; +} + +Evas_Object * +util_button_icon_add(Evas_Object *base, const char *icon, const char *tip) +{ + Evas_Object *ic, *bt = elm_button_add(base); + ic = elm_icon_add(base); + evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_icon_standard_set(ic, icon); + elm_object_tooltip_text_set(bt, tip); + elm_object_part_content_set(bt, NULL, ic); + evas_object_show(ic); + return bt; +} + +const char * +util_obj_name_get(Obj *o) +{ + if (o->name) return o->name; + if (o->alias) return o->alias; + if (o->address) return o->address; + return _("Uknown"); +} diff --git a/src/modules/bluez5/meson.build b/src/modules/bluez5/meson.build new file mode 100644 index 000000000..f6cffcc37 --- /dev/null +++ b/src/modules/bluez5/meson.build @@ -0,0 +1,9 @@ +src = files( + 'e_mod_main.c', + 'e_mod_popup.c', + 'e_mod_agent.c', + 'e_mod_util.c', + 'bz.c', + 'bz_obj.c', + 'bz_agent.c' +) diff --git a/src/modules/bluez5/module.desktop b/src/modules/bluez5/module.desktop new file mode 100644 index 000000000..fd102a832 --- /dev/null +++ b/src/modules/bluez5/module.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Link +Name=Bluez5 +Name[ca]=Bluez5 +Name[de]=Bluez5 +Name[fi]=Bluez5 +Name[gl]=Bluez5 +Name[ja]=Bluez5 +Name[ms]=Bluez5 +Name[sr]=Блуез5 +Name[tr]=Bluetooth +Icon=e-module-bluez5 +X-Enlightenment-ModuleType=utils diff --git a/src/modules/meson.build b/src/modules/meson.build index 11b93df3f..4150367e7 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -34,6 +34,7 @@ mods = [ 'geolocation', 'connman', 'bluez4', + 'bluez5', 'syscon', 'systray', 'appmenu',