#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include /* FIXME: These globals really need to get reduced before leaving the PROTO * area. */ static char *unique_name = NULL; static Eldbus_Connection *conn = NULL; static Elocation_Provider *address_provider = NULL; static Elocation_Provider *position_provider = NULL; static Eldbus_Object *obj_meta = NULL; static Eldbus_Proxy *manager_master = NULL; static Eldbus_Proxy *meta_geoclue = NULL; static Eldbus_Proxy *meta_address = NULL; static Eldbus_Proxy *meta_position = NULL; static Eldbus_Proxy *meta_masterclient = NULL; static Eldbus_Proxy *meta_velocity = NULL; static Eldbus_Proxy *meta_nmea = NULL; static Eldbus_Proxy *meta_satellite = NULL; static Eldbus_Proxy *geonames_geocode = NULL; static Eldbus_Proxy *geonames_rgeocode = NULL; static Eldbus_Proxy *master_poi = NULL; static Elocation_Address *address = NULL; static Elocation_Position *position = NULL; static Elocation_Address *addr_geocode = NULL; static Elocation_Position *pos_geocode = NULL; static Elocation_Velocity *velocity = NULL; static int status = -1; /* 0 is a valid status code */ static char nmea_sentence[256]; int _elocation_log_dom = -1; /* Elocation ecore event types we provide to the application. */ EAPI int ELOCATION_EVENT_STATUS = 0; EAPI int ELOCATION_EVENT_POSITION = 0; EAPI int ELOCATION_EVENT_ADDRESS = 0; EAPI int ELOCATION_EVENT_VELOCITY = 0; EAPI int ELOCATION_EVENT_GEOCODE = 0; EAPI int ELOCATION_EVENT_REVERSEGEOCODE = 0; EAPI int ELOCATION_EVENT_NMEA = 0; EAPI int ELOCATION_EVENT_SATELLITE = 0; EAPI int ELOCATION_EVENT_POI = 0; EAPI int ELOCATION_EVENT_META_READY = 0; /* Internal events */ int ELOCATION_EVENT_IN = 0; int ELOCATION_EVENT_OUT = 0; static void _dummy_free(void *user_data EINA_UNUSED, void *func_data EINA_UNUSED) { /* Don't free the event data after dispatching the event. We keep track of * it on our own */ } /* Generic provider message unmarshaller. Used from all different provider * calbacks that receive such a message */ static Eina_Bool unmarshall_provider(const Eldbus_Message *reply, Elocation_Provider *provider) { char *name = NULL, *desc = NULL, *service = NULL, *path = NULL; if (!eldbus_message_arguments_get(reply, "ssss", &name, &desc, &service, &path)) return EINA_FALSE; provider->name = strdup(name); provider->description = strdup(desc); provider->service = strdup(service); provider->path = strdup(path); return EINA_TRUE; } static void meta_address_provider_info_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; Elocation_Provider *addr_provider; addr_provider = data; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } if (!unmarshall_provider(reply, addr_provider)) { ERR("Error: Unable to unmarshall address provider"); return; } DBG("Meta address provider name: %s, %s, %s, %s", addr_provider->name, addr_provider->description, addr_provider->service, addr_provider->path); } static void meta_position_provider_info_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; Elocation_Provider *pos_provider; pos_provider = data; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } if (!unmarshall_provider(reply, pos_provider)) { ERR("Error: Unable to unmarshall position provider"); return; } DBG("Meta position provider name: %s, %s, %s, %s", pos_provider->name, pos_provider->description, pos_provider->service, pos_provider->path); } static void meta_address_provider_info_signal_cb(void *data, const Eldbus_Message *reply) { Elocation_Provider *addr_provider; addr_provider = data; if (!unmarshall_provider(reply, addr_provider)) { ERR("Error: Unable to unmarshall address provider"); return; } DBG("Meta address provider name changed: %s, %s, %s, %s", addr_provider->name, addr_provider->description, addr_provider->service, addr_provider->path); } static void meta_position_provider_info_signal_cb(void *data, const Eldbus_Message *reply) { Elocation_Provider *pos_provider; pos_provider = data; if (!unmarshall_provider(reply, pos_provider)) { ERR("Error: Unable to unmarshall position provider"); return; } DBG("Meta position provider name changed: %s, %s, %s, %s", pos_provider->name, pos_provider->description, pos_provider->service, pos_provider->path); } /* A address is quite flexible what kind of key value pairs it contains in the * dict. Similar to a reverse GeoCode message as both return an address object. */ static Eina_Bool unmarshall_address(const Eldbus_Message *reply, Elocation_Address *addr) { int32_t level, timestamp; Eldbus_Message_Iter *sub, *dict, *entry; double horizontal; double vertical; const char *key, *signature; char *value; signature = eldbus_message_signature_get(reply); if (!strcmp(signature, "ia{ss}(idd)")) { if (!eldbus_message_arguments_get(reply, "ia{ss}(idd)", ×tamp, &dict, &sub)) return EINA_FALSE; addr->timestamp = timestamp; } else if (!strcmp(signature, "a{ss}(idd)")) { if (!eldbus_message_arguments_get(reply, "a{ss}(idd)", &dict, &sub)) return EINA_FALSE; addr->timestamp = 0; } else return EINA_FALSE; /* Cleanup potential old entries before re-using */ addr->country = NULL; addr->countrycode = NULL; addr->locality = NULL; addr->postalcode = NULL; addr->region = NULL; addr->timezone = NULL; while (eldbus_message_iter_get_and_next(dict, 'e', &entry)) { if (!eldbus_message_iter_arguments_get(entry, "ss", &key, &value)) continue; if (!strcmp(key, "country")) { free(addr->country); addr->country = strdup(value); } else if (!strcmp(key, "countrycode")) { free(addr->countrycode); addr->countrycode = strdup(value); } else if (!strcmp(key, "locality")) { free(addr->locality); addr->locality = strdup(value); } else if (!strcmp(key, "postalcode")) { free(addr->postalcode); addr->postalcode = strdup(value); } else if (!strcmp(key, "region")) { free(addr->region); addr->region = strdup(value); } else if (!strcmp(key, "timezone")) { free(addr->timezone); addr->timezone = strdup(value); } } if (!eldbus_message_iter_arguments_get(sub, "idd", &level, &horizontal, &vertical)) return EINA_FALSE; addr->accur->level = level; addr->accur->horizontal = horizontal; addr->accur->vertical = vertical; return EINA_TRUE; } /* Receive and unmarshall a reverse GeoCode message. The dict can contain a * variable set of key value pairs so we need to handle this with care */ static void rgeocode_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } if (!unmarshall_address(reply, addr_geocode)) { ERR("Error: Unable to unmarshall address"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_REVERSEGEOCODE, addr_geocode, _dummy_free, NULL); } /* Point of Interest (POI) aka landmark message unmarshalling. Thsi interface is * not in standard GeoClue but currently a Tizen extension. */ static void poi_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { int32_t count, id, rank; double lat, lon, bound_left, bound_top, bound_right, bound_bottom; const char *name, *icon, *house, *road, *village, *suburb, *postcode; const char *city, *county, *country, *country_code; Eldbus_Message_Iter *array, *struct_landmark; const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } /* Yeah, its quite a horrible message. The POI interface could use a better design */ if (!eldbus_message_arguments_get(reply, "ia(iiddddddsssssssssss", &count ,&array)) return; /* TODO re-check that the parameter ordering is what we expect */ while (eldbus_message_iter_get_and_next(array, 'r', &struct_landmark)) { if (!eldbus_message_iter_arguments_get(struct_landmark, "iiddddddsssssssssss", &id, &rank, &lat, &lon, &bound_left, &bound_top, &bound_right, &bound_bottom, &name, &icon, &house, &road, &village, &suburb, &postcode, &city, &county, &country, &country_code)) return; DBG("Landmark received: %i, %i, %f, %f, %f, %f, %f, %f, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,", id, rank, lat, lon, bound_left, bound_top, bound_right, bound_bottom, name, icon, house, road, village, suburb, postcode, city, county, country, country_code); } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_POI, NULL, _dummy_free, NULL); } /* Unmarshall a GeoCode message */ static void geocode_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { GeocluePositionFields fields; int32_t level; double horizontal = 0.0; double vertical = 0.0; double latitude = 0.0; double longitude = 0.0; double altitude = 0.0; Eldbus_Message_Iter *sub; const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } if (!eldbus_message_arguments_get(reply, "iddd(idd)", &fields,&latitude, &longitude, &altitude, &sub)) return; /* GeoClue uses some flags to mark position fields as valid. We set invalid * fields to 0.0 */ if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE) pos_geocode->latitude = latitude; else pos_geocode->latitude = 0.0; if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) pos_geocode->longitude = longitude; else pos_geocode->longitude = 0.0; if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE) pos_geocode->altitude = altitude; else pos_geocode->altitude = 0.0; if (!eldbus_message_iter_arguments_get(sub, "idd", &level, &horizontal, &vertical)) return; pos_geocode->accur->level = level; pos_geocode->accur->horizontal = horizontal; pos_geocode->accur->vertical = vertical; /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_GEOCODE, pos_geocode, _dummy_free, NULL); } static void address_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } if (!unmarshall_address(reply, address)) { ERR("Error: Unable to unmarshall address"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_ADDRESS, address, _dummy_free, NULL); } static void address_signal_cb(void *data EINA_UNUSED, const Eldbus_Message *reply) { const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } if (!unmarshall_address(reply, address)) { ERR("Error: Unable to unmarshall address"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_ADDRESS, address, _dummy_free, NULL); } /* Unmarshall a velocity message. This is only available if we use a GPS * provider from GeoClue. None of the other providers offer this currently. */ static Eina_Bool unmarshall_velocity(const Eldbus_Message *reply) { GeoclueVelocityFields fields; int32_t timestamp = 0; double speed = 0.0; double direction = 0.0; double climb = 0.0; if (!eldbus_message_arguments_get(reply, "iiddd", &fields, ×tamp, &speed, &direction, &climb)) return EINA_FALSE; velocity->timestamp = timestamp; /* GeoClue uses some flags to mark velocity fields as valid. We set invalid * fields to 0.0 */ if (fields & GEOCLUE_VELOCITY_FIELDS_SPEED) velocity->speed = speed; else velocity->speed = 0.0; if (fields & GEOCLUE_VELOCITY_FIELDS_DIRECTION) velocity->direction = direction; else velocity->direction = 0.0; if (fields & GEOCLUE_VELOCITY_FIELDS_CLIMB) velocity->climb = climb; else velocity->climb = 0.0; return EINA_TRUE; } static void velocity_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { WARN("Warning: %s %s", err, errmsg); return; } if (!unmarshall_velocity(reply)) { ERR("Error: Unable to unmarshall velocity"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_VELOCITY, velocity, _dummy_free, NULL); } static void velocity_signal_cb(void *data EINA_UNUSED, const Eldbus_Message *reply) { if (!unmarshall_velocity(reply)) { ERR("Error: Unable to unmarshall velocity"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_VELOCITY, velocity, _dummy_free, NULL); } /* Unmarshall an raw NMEA message. It conatins a raw NMEA sentence which we can * pass on to applications that want to use their own NMEA parser. This is not * reommended. Better use the other interfaces to access the needed data. * * This is currently a Tizen only interface and not in GeoClue upstream. */ static Eina_Bool unmarshall_nmea(const Eldbus_Message *reply) { int32_t timestamp = 0; if (!eldbus_message_arguments_get(reply, "is", ×tamp, &nmea_sentence)) return EINA_FALSE; return EINA_TRUE; } static void nmea_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { WARN("Warning: %s %s", err, errmsg); return; } if (!unmarshall_nmea(reply)) { ERR("Error: Unable to unmarshall nmea"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_NMEA, nmea_sentence, _dummy_free, NULL); } static void nmea_signal_cb(void *data EINA_UNUSED, const Eldbus_Message *reply) { if (!unmarshall_nmea(reply)) { ERR("Error: Unable to unmarshall nmea"); return; } ecore_event_add(ELOCATION_EVENT_NMEA, nmea_sentence, _dummy_free, NULL); } /* Unmarshall a satellite information message. This offers GPS specific * information about the used satellites and its properties. It can be used for * applications that rely on GPS and want to show more information like a 3D fix * or used satellites. * * This is currently a Tizen only interface and not available in GeoClue upstream. */ static Eina_Bool unmarshall_satellite(const Eldbus_Message *reply) { int32_t timestamp = 0, satellite_used = 0, satellite_visible = 0; int32_t snr = 0, elevation = 0, azimuth = 0, prn = 0, used_prn = 0; Eldbus_Message_Iter *sub_prn, *sub_info, *struct_info; if (!eldbus_message_arguments_get(reply, "iiiaia(iiii)", ×tamp, &satellite_used, &satellite_visible, &sub_prn, &sub_info)) return EINA_FALSE; while (eldbus_message_iter_get_and_next(sub_prn, 'i', &used_prn)) { DBG("Satellite used PRN %i", used_prn); } /* TODO re-check that the parameter ordering is what we expect */ while (eldbus_message_iter_get_and_next(sub_info, 'r', &struct_info)) { if (!eldbus_message_iter_arguments_get(struct_info, "iiii", &prn, &elevation, &azimuth, &snr)) return EINA_FALSE; DBG("Satellite info %i, %i, %i, %i", prn, elevation, azimuth, snr); } return EINA_TRUE; } static void satellite_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { WARN("Warning: %s %s", err, errmsg); return; } if (!unmarshall_satellite(reply)) { ERR("Error: Unable to unmarshall satellite"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_SATELLITE, NULL, _dummy_free, NULL); } static void last_satellite_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { WARN("Warning: %s %s", err, errmsg); return; } if (!unmarshall_satellite(reply)) { ERR("Error: Unable to unmarshall last satellite"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_SATELLITE, NULL, _dummy_free, NULL); } static void satellite_signal_cb(void *data EINA_UNUSED, const Eldbus_Message *reply) { if (!unmarshall_satellite(reply)) { ERR("Error: Unable to unmarshall satellite"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_SATELLITE, NULL, _dummy_free, NULL); } /* Unmarshall position coordination message */ static Eina_Bool unmarshall_position(const Eldbus_Message *reply) { GeocluePositionFields fields; int32_t level, timestamp; double horizontal = 0.0; double vertical = 0.0; double latitude = 0.0; double longitude = 0.0; double altitude = 0.0; Eldbus_Message_Iter *sub; if (!eldbus_message_arguments_get(reply, "iiddd(idd)", &fields, ×tamp, &latitude, &longitude, &altitude, &sub)) return EINA_FALSE; if (!eldbus_message_iter_arguments_get(sub, "idd", &level, &horizontal, &vertical)) return EINA_FALSE; position->timestamp = timestamp; /* GeoClue uses some flags to mark position fields as valid. We set invalid * fields to 0.0 */ if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE) position->latitude = latitude; else position->latitude = 0.0; if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) position->longitude = longitude; else position->longitude = 0.0; if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE) position->altitude = altitude; else position->altitude = 0.0; position->accur->level = level; position->accur->horizontal = horizontal; position->accur->vertical = vertical; return EINA_TRUE; } static void position_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } if (!unmarshall_position(reply)) { ERR("Error: Unable to unmarshall position"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_POSITION, position, _dummy_free, NULL); } static void position_signal_cb(void *data EINA_UNUSED, const Eldbus_Message *reply) { if (!unmarshall_position(reply)) { ERR("Error: Unable to unmarshall position"); return; } /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_POSITION, position, _dummy_free, NULL); } static Eina_Bool geoclue_start(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event EINA_UNUSED) { DBG("GeoClue start event at %s", unique_name); return ECORE_CALLBACK_DONE; } static Eina_Bool geoclue_stop(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event EINA_UNUSED) { DBG("GeoClue stop event"); return ECORE_CALLBACK_DONE; } static void _reference_add_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } DBG("Reference added"); } static void _reference_del_cb(void *data EINA_UNUSED, const Eldbus_Message *reply EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) { /* Dummy callback. We are not waiting for any reply here on shutdown */ } static void status_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } if (!eldbus_message_arguments_get(reply,"i", &status)) { ERR("Error: Unable to unmarshall status"); return; } address_provider->status = position_provider->status = status; /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_STATUS, &status, _dummy_free, NULL); } static void status_signal_cb(void *data EINA_UNUSED, const Eldbus_Message *reply) { if (!eldbus_message_arguments_get(reply,"i", &status)) { ERR("Error: Unable to unmarshall status"); return; } address_provider->status = position_provider->status = status; /* Send out an event to all interested parties that we have an update */ ecore_event_add(ELOCATION_EVENT_STATUS, &status, _dummy_free, NULL); } static void _dummy_cb(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED, Eldbus_Pending *pending EINA_UNUSED) { } /* We got notified from GeoClue that the meta-provider we asked for is now * ready. That means we can finish up our initialization and set up all * callbacks and handling for the interfaces we use on the meta-provider. * * We also call the interfaces to get an initial set of data that we can provide * to eager aplications. */ static void create_cb(void *data EINA_UNUSED, const Eldbus_Message *reply, Eldbus_Pending *pending EINA_UNUSED) { const char *object_path; Eina_Bool updates; int accur_level, min_time, resources; const char *err, *errmsg; if (eldbus_message_error_get(reply, &err, &errmsg)) { ERR("Error: %s %s", err, errmsg); return; } if (!eldbus_message_arguments_get(reply, "o", &object_path)) return; DBG("Object path for client: %s", object_path); /* With the created object path we now have a meta provider we can operate on. * Geoclue handles the selection of the best provider internally for the meta * provider */ obj_meta = eldbus_object_get(conn, GEOCLUE_DBUS_NAME, object_path); if (!obj_meta) { ERR("Error: could not get object for client"); return; } meta_geoclue = eldbus_proxy_get(obj_meta, GEOCLUE_GEOCLUE_IFACE); if (!meta_geoclue) { ERR("Error: could not get proxy for geoclue"); return; } meta_address = eldbus_proxy_get(obj_meta, GEOCLUE_ADDRESS_IFACE); if (!meta_address) { ERR("Error: could not get proxy address"); return; } meta_position = eldbus_proxy_get(obj_meta, GEOCLUE_POSITION_IFACE); if (!meta_position) { ERR("Error: could not get proxy for position"); return; } meta_masterclient = eldbus_proxy_get(obj_meta, GEOCLUE_MASTERCLIENT_IFACE); if (!meta_masterclient) { ERR("Error: could not get proxy for master client"); return; } meta_velocity = eldbus_proxy_get(obj_meta, GEOCLUE_VELOCITY_IFACE); if (!meta_velocity) { ERR("Error: could not get proxy for velocity"); return; } meta_nmea = eldbus_proxy_get(obj_meta, GEOCLUE_NMEA_IFACE); if (!meta_nmea) { ERR("Error: could not get proxy for nmea"); return; } meta_satellite = eldbus_proxy_get(obj_meta, GEOCLUE_SATELLITE_IFACE); if (!meta_satellite) { ERR("Error: could not get proxy for satellite"); return; } /* Send Geoclue a set of requirements we have for the provider and start the address and position * meta provider afterwards. After this we should be ready for operation. */ updates = EINA_FALSE; /* Especially the web providers do not offer updates */ accur_level = ELOCATION_ACCURACY_LEVEL_COUNTRY; min_time = 0; /* Minimal times between updates (no implemented yet) */ resources = ELOCATION_RESOURCE_ALL; eldbus_proxy_signal_handler_add(meta_masterclient, "AddressProviderChanged", meta_address_provider_info_signal_cb, address_provider); eldbus_proxy_signal_handler_add(meta_masterclient, "PositionProviderChanged", meta_position_provider_info_signal_cb, position_provider); if (!eldbus_proxy_call(meta_masterclient, "SetRequirements", _dummy_cb, NULL, -1, "iibi", accur_level, min_time, updates, resources)) { ERR("Error: could not call SetRequirements"); return; } if (!eldbus_proxy_call(meta_masterclient, "AddressStart", _dummy_cb, NULL, -1, "")) { ERR("Error: could not call AddressStart"); return; } if (!eldbus_proxy_call(meta_masterclient, "PositionStart", _dummy_cb, NULL, -1, "")) { ERR("Error: could not call PositionStart"); return; } if (!eldbus_proxy_call(meta_geoclue, "AddReference", _reference_add_cb, NULL, -1, "")) { ERR("Error: could not call AddReference"); return; } if (!eldbus_proxy_call(meta_address, "GetAddress", address_cb, NULL, -1, "")) { ERR("Error: could not call GetAddress"); return; } if (!eldbus_proxy_call(meta_position, "GetPosition", position_cb, NULL, -1, "")) { ERR("Error: could not call GetPosition"); return; } if (!eldbus_proxy_call(meta_geoclue, "GetStatus", status_cb, NULL, -1, "")) { ERR("Error: could not call GetStatus"); return; } if (!eldbus_proxy_call(meta_velocity, "GetVelocity", velocity_cb, NULL, -1, "")) { ERR("Error: could not call GetVelocity"); return; } if (!eldbus_proxy_call(meta_nmea, "GetNmea", nmea_cb, NULL, -1, "")) { ERR("Error: could not call GetNmea"); return; } if (!eldbus_proxy_call(meta_satellite, "GetSatellite", satellite_cb, NULL, -1, "")) { ERR("Error: could not call GetSatellite"); return; } if (!eldbus_proxy_call(meta_satellite, "GetLastSatellite", last_satellite_cb, NULL, -1, "")) { ERR("Error: could not call GetLastSatellite"); return; } if (!eldbus_proxy_call(meta_masterclient, "GetAddressProvider", meta_address_provider_info_cb, address_provider, -1, "")) { ERR("Error: could not call GetAddressProvider"); return; } if (!eldbus_proxy_call(meta_masterclient, "GetPositionProvider", meta_position_provider_info_cb, position_provider, -1, "")) { ERR("Error: could not call GetPositionProvider"); return; } eldbus_proxy_signal_handler_add(meta_address, "AddressChanged", address_signal_cb, NULL); eldbus_proxy_signal_handler_add(meta_position, "PositionChanged", position_signal_cb, NULL); eldbus_proxy_signal_handler_add(meta_geoclue, "StatusChanged", status_signal_cb, NULL); eldbus_proxy_signal_handler_add(meta_velocity, "VelocityChanged", velocity_signal_cb, NULL); eldbus_proxy_signal_handler_add(meta_nmea, "NmeaChanged", nmea_signal_cb, NULL); eldbus_proxy_signal_handler_add(meta_satellite, "SatelliteChanged", satellite_signal_cb, NULL); ecore_event_add(ELOCATION_EVENT_META_READY, NULL, NULL, NULL); } static void _name_owner_changed(void *data EINA_UNUSED, const char *bus EINA_UNUSED, const char *old, const char *new) { if (old[0] == '\0' && new[0] != '\0') { ecore_event_add(ELOCATION_EVENT_IN, NULL, NULL, NULL); unique_name = strdup(new); } else if (old[0] != '\0' && new[0] == '\0') { if (strcmp(unique_name, old) != 0) WARN("%s was not the known name %s, ignored.", old, unique_name); else ecore_event_add(ELOCATION_EVENT_OUT, NULL, NULL, NULL); } else { DBG("unknown change from %s to %s", old, new); } } /* Public API function to request a landmarks position based on an address object */ EAPI Eina_Bool elocation_landmarks_get(Elocation_Position *position_shadow EINA_UNUSED, Elocation_Address *address_shadow EINA_UNUSED) { Eldbus_Message *msg; Eldbus_Message_Iter *iter; const char *keyword = NULL, *lang = NULL, *country_code = NULL; int limit = 0; double left= 0.0, top = 0.0, right = 0.0, bottom = 0.0; msg = eldbus_proxy_method_call_new(master_poi, "SearchByPosition"); iter = eldbus_message_iter_get(msg); eldbus_message_iter_basic_append(iter, 's', keyword); eldbus_message_iter_basic_append(iter, 's', lang); eldbus_message_iter_basic_append(iter, 's', country_code); eldbus_message_iter_basic_append(iter, 'i', limit); eldbus_message_iter_basic_append(iter, 'd', left); eldbus_message_iter_basic_append(iter, 'd', top); eldbus_message_iter_basic_append(iter, 'd', right); eldbus_message_iter_basic_append(iter, 'd', bottom); if (!eldbus_proxy_send(master_poi, msg, poi_cb, NULL, -1)) { ERR("Error: could not call SearchByPosition"); eldbus_message_unref(msg); return EINA_FALSE; } return EINA_TRUE; } /* Public API function to get an address from a position */ EAPI Eina_Bool elocation_position_to_address(Elocation_Position *position_shadow, Elocation_Address *address_shadow EINA_UNUSED) { Eldbus_Message *msg; Eldbus_Message_Iter *iter, *structure; msg = eldbus_proxy_method_call_new(geonames_rgeocode, "PositionToAddress"); iter = eldbus_message_iter_get(msg); eldbus_message_iter_basic_append(iter, 'd', position_shadow->latitude); eldbus_message_iter_basic_append(iter, 'd', position_shadow->longitude); structure = eldbus_message_iter_container_new(iter, 'r', NULL); eldbus_message_iter_basic_append(structure, 'i', position_shadow->accur->level); eldbus_message_iter_basic_append(structure, 'd', position_shadow->accur->horizontal); eldbus_message_iter_basic_append(structure, 'd', position_shadow->accur->vertical); eldbus_message_iter_container_close(iter, structure); if (!eldbus_proxy_send(geonames_rgeocode, msg, rgeocode_cb, NULL, -1)) { ERR("Error: could not call PositionToAddress"); eldbus_message_unref(msg); return EINA_FALSE; } return EINA_TRUE; } /* Public API function to get a position from and address */ EAPI Eina_Bool elocation_address_to_position(Elocation_Address *address_shadow, Elocation_Position *position_shadow EINA_UNUSED) { Eldbus_Message *msg; Eldbus_Message_Iter *iter, *array; /* In function macro to generate a key value pair structure for the dict */ #define ENTRY(key) { #key, address_shadow->key } struct keyval { const char *key; const char *val; } keyval[] = { ENTRY(country), ENTRY(countrycode), ENTRY(locality), ENTRY(postalcode), ENTRY(region), ENTRY(timezone), { NULL, NULL } }; #undef ENTRY struct keyval *k; msg = eldbus_proxy_method_call_new(geonames_geocode, "AddressToPosition"); iter = eldbus_message_iter_get(msg); array = eldbus_message_iter_container_new(iter, 'a', "{ss}"); for (k = keyval; k && k->key; k++) { Eldbus_Message_Iter *entry; if (!k->val) continue; entry = eldbus_message_iter_container_new(array, 'e', NULL); eldbus_message_iter_arguments_append(entry, "ss", k->key, k->val); eldbus_message_iter_container_close(array, entry); } eldbus_message_iter_container_close(iter, array); if (!eldbus_proxy_send(geonames_geocode, msg, geocode_cb, NULL, -1)) { ERR("Error: could not call AddressToPosition"); eldbus_message_unref(msg); return EINA_FALSE; } return EINA_TRUE; } /* Public API function to get the position from a freeform text input style * address */ EAPI Eina_Bool elocation_freeform_address_to_position(const char *freeform_address, Elocation_Position *position_shadow EINA_UNUSED) { if (!eldbus_proxy_call(geonames_geocode, "FreeformAddressToPosition", geocode_cb, NULL, -1, "s", freeform_address)) { ERR("Error: could not call FreeformAddressToPosition"); return EINA_FALSE; } return EINA_TRUE; } /* Public API function to request the current address */ EAPI Eina_Bool elocation_address_get(Elocation_Address *address_shadow) { if (!address) return EINA_FALSE; if (address == address_shadow) return EINA_TRUE; *address_shadow = *address; return EINA_TRUE; } /* Public API function to request the current position */ EAPI Eina_Bool elocation_position_get(Elocation_Position *position_shadow) { if (!position) return EINA_FALSE; if (position == position_shadow) return EINA_TRUE; *position_shadow = *position; return EINA_TRUE; } /* Public API function to request the status */ EAPI Eina_Bool elocation_status_get(int *status_shadow) { if (status < 0) return EINA_FALSE; if (&status == status_shadow) return EINA_TRUE; *status_shadow = status; return EINA_TRUE; } /* Public API function to create a new position object */ EAPI Elocation_Position * elocation_position_new(void) { /* Malloc the global struct we operate on here in this lib. This shadows the * updated data we are giving to the application */ position = calloc(1, sizeof(Elocation_Position)); if (!position) return NULL; position->accur = calloc(1, sizeof(Elocation_Accuracy)); if (!position->accur) return NULL; return position; } /* Public API function to create an new address object */ EAPI Elocation_Address * elocation_address_new(void) { /* Malloc the global struct we operate on here in this lib. This shadows the * updated data we are giving to the application */ address = calloc(1, sizeof(Elocation_Address)); if (!address) return NULL; address->accur = calloc(1, sizeof(Elocation_Accuracy)); if (!address->accur) return NULL; return address; } /* Public API function to free an position object */ EAPI void elocation_position_free(Elocation_Position *position_shadow) { if (position != position_shadow) { ERR("Corrupted position object"); return; } free(position->accur); free(position); } /* Public API function to free an address object */ EAPI void elocation_address_free(Elocation_Address *address_shadow) { if (address != address_shadow) { ERR("Corrupted address object"); return; } if (address) { free(address->country); free(address->countrycode); free(address->locality); free(address->postalcode); free(address->region); free(address->timezone); free(address->accur); free(address); } } /* Public API function to initialize the elocation library */ EAPI Eina_Bool elocation_init(void) { Eldbus_Object *obj_master = NULL; Eldbus_Object *obj_geonames = NULL; if (!eina_init()) return EINA_FALSE; if (!ecore_init()) return EINA_FALSE; if (!eldbus_init()) return EINA_FALSE; _elocation_log_dom = eina_log_domain_register("elocation", EINA_COLOR_BLUE); if (_elocation_log_dom < 0) { EINA_LOG_ERR("Could not register 'elocation' log domain."); } /* Create objects, one for each kind, we operate on internally */ address_provider = calloc(1, sizeof(Elocation_Provider)); position_provider = calloc(1, sizeof(Elocation_Provider)); addr_geocode = calloc(1, sizeof(Elocation_Address)); if (!addr_geocode) return EINA_FALSE; addr_geocode->accur = calloc(1, sizeof(Elocation_Accuracy)); if (!addr_geocode->accur) return EINA_FALSE; pos_geocode = calloc(1, sizeof(Elocation_Position)); if (!pos_geocode) return EINA_FALSE; pos_geocode->accur = calloc(1, sizeof(Elocation_Accuracy)); if (!pos_geocode->accur) return EINA_FALSE; conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION); if (!conn) { ERR("Error: could not connect to session bus."); return EXIT_FAILURE; } /* Create all ecore event types we send out to interested applications */ ELOCATION_EVENT_IN = ecore_event_type_new(); ELOCATION_EVENT_OUT = ecore_event_type_new(); ELOCATION_EVENT_STATUS = ecore_event_type_new(); ELOCATION_EVENT_POSITION = ecore_event_type_new(); ELOCATION_EVENT_ADDRESS = ecore_event_type_new(); ELOCATION_EVENT_VELOCITY = ecore_event_type_new(); ELOCATION_EVENT_GEOCODE = ecore_event_type_new(); ELOCATION_EVENT_REVERSEGEOCODE = ecore_event_type_new(); ELOCATION_EVENT_NMEA = ecore_event_type_new(); ELOCATION_EVENT_SATELLITE = ecore_event_type_new(); ELOCATION_EVENT_POI = ecore_event_type_new(); ELOCATION_EVENT_META_READY = ecore_event_type_new(); obj_master= eldbus_object_get(conn, GEOCLUE_DBUS_NAME, GEOCLUE_OBJECT_PATH); if (!obj_master) { ERR("Error: could not get object"); return EXIT_FAILURE; } manager_master = eldbus_proxy_get(obj_master, GEOCLUE_MASTER_IFACE); if (!manager_master) { ERR("Error: could not get proxy"); return EXIT_FAILURE; } /* Create a meta provider for all normal use cases. This will allow GeoClue * to decide which provider is the best for us internally. * Right now we don't have the functionality in place to specifically request * a provider but we maybe need this in the future. We will try without it * for now. */ if (!eldbus_proxy_call(manager_master, "Create", create_cb, NULL, -1, "")) { ERR("Error: could not call Create"); return EXIT_FAILURE; } master_poi = eldbus_proxy_get(obj_master, GEOCLUE_POI_IFACE); if (!master_poi) { ERR("Error: could not get proxy"); return EXIT_FAILURE; } /* Geocode and reverse geocode never show up as meta provider. Still we want * to be able to convert so we keep them around directly here. */ obj_geonames= eldbus_object_get(conn, GEONAMES_DBUS_NAME, GEONAMES_OBJECT_PATH); if (!obj_geonames) { ERR("Error: could not get object for geonames"); return EXIT_FAILURE; } geonames_geocode = eldbus_proxy_get(obj_geonames, GEOCLUE_GEOCODE_IFACE); if (!geonames_geocode) { ERR("Error: could not get proxy"); return EXIT_FAILURE; } geonames_rgeocode = eldbus_proxy_get(obj_geonames, GEOCLUE_REVERSEGEOCODE_IFACE); if (!geonames_rgeocode) { ERR("Error: could not get proxy"); return EXIT_FAILURE; } eldbus_name_owner_changed_callback_add(conn, GEOCLUE_DBUS_NAME, _name_owner_changed, NULL, EINA_TRUE); ecore_event_handler_add(ELOCATION_EVENT_IN, geoclue_start, NULL); ecore_event_handler_add(ELOCATION_EVENT_OUT, geoclue_stop, NULL); return EINA_TRUE; } /* Public API function to shutdown the elocation library form the application */ EAPI void elocation_shutdown(void) { /* Depending on if the create_cb was successfully received meta_geoclue is * setup or not. So we * need to check here if this is not the case */ if (meta_geoclue) { /* To allow geoclue freeing unused providers we free our reference on it here */ if (!eldbus_proxy_call(meta_geoclue, "RemoveReference", _reference_del_cb, NULL, -1, "")) { ERR("Error: could not call RemoveReference"); } } ecore_event_type_flush(ELOCATION_EVENT_IN, ELOCATION_EVENT_OUT, ELOCATION_EVENT_STATUS, ELOCATION_EVENT_POSITION, ELOCATION_EVENT_ADDRESS, ELOCATION_EVENT_VELOCITY, ELOCATION_EVENT_GEOCODE, ELOCATION_EVENT_REVERSEGEOCODE, ELOCATION_EVENT_NMEA, ELOCATION_EVENT_SATELLITE, ELOCATION_EVENT_POI, ELOCATION_EVENT_META_READY); /* Quite a bit of allocated string and generic memory cleanup. This should be *less when we went away from all this global var business. */ if (address_provider) { free(address_provider->name); free(address_provider->description); free(address_provider->service); free(address_provider->path); free(address_provider); } if (position_provider) { free(position_provider->name); free(position_provider->description); free(position_provider->service); free(position_provider->path); free(position_provider); } if (pos_geocode) { free(pos_geocode->accur); free(pos_geocode); } if (addr_geocode) { free(addr_geocode->country); free(addr_geocode->countrycode); free(addr_geocode->locality); free(addr_geocode->postalcode); free(addr_geocode->region); free(addr_geocode->timezone); free(addr_geocode->accur); free(addr_geocode); } /* Unreference some eldbus strcutures we now longer use. To allow eldbus to * free them internally. */ if (manager_master) eldbus_proxy_unref(manager_master); eldbus_name_owner_changed_callback_del(conn, GEOCLUE_DBUS_NAME, _name_owner_changed, NULL); eldbus_connection_unref(conn); eldbus_shutdown(); ecore_shutdown(); eina_log_domain_unregister(_elocation_log_dom); eina_shutdown(); }