From 054a92d0fa14ec4e3e29e531a9392db1f2279263 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Fri, 21 Apr 2017 12:57:48 +0200 Subject: [PATCH] [feature] new remote devices are displayed and removed from the window, avahi connection --- src/Makefile | 6 +- src/browser.c | 196 +++++++++++++++++++++++++++++++++++++++++++ src/browser.h | 8 ++ src/remote_list.ui | 67 +++++++++++++++ src/teleportapp.c | 32 ++++++- src/teleportapp.h | 2 + src/teleportappwin.c | 92 +++++++++++++++----- src/teleportappwin.h | 2 + 8 files changed, 378 insertions(+), 27 deletions(-) create mode 100644 src/browser.c create mode 100644 src/browser.h create mode 100644 src/remote_list.ui diff --git a/src/Makefile b/src/Makefile index f7402ff..cc51623 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,10 +1,10 @@ CC ?= gcc PKGCONFIG = $(shell which pkg-config) -CFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-3.0) -LIBS = $(shell $(PKGCONFIG) --libs gtk+-3.0) +CFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-3.0 avahi-client) +LIBS = $(shell $(PKGCONFIG) --libs gtk+-3.0 avahi-client) GLIB_COMPILE_RESOURCES = $(shell $(PKGCONFIG) --variable=glib_compile_resources gio-2.0) -SRC = teleportapp.c teleportappwin.c main.c +SRC = teleportapp.c teleportappwin.c main.c browser.c BUILT_SRC = resources.c OBJS = $(BUILT_SRC:.c=.o) $(SRC:.c=.o) diff --git a/src/browser.c b/src/browser.c new file mode 100644 index 0000000..585641d --- /dev/null +++ b/src/browser.c @@ -0,0 +1,196 @@ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "teleportapp.h" +#include "browser.h" +static AvahiSimplePoll *simple_poll = NULL; +static AvahiThreadedPoll *threaded_poll = NULL; +static AvahiClient *client = NULL; + +static void resolve_callback( + AvahiServiceResolver *r, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + assert(r); + /* Called whenever a service has been resolved successfully or timed out */ + switch (event) { + case AVAHI_RESOLVER_FAILURE: + fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); + break; + case AVAHI_RESOLVER_FOUND: { + char a[AVAHI_ADDRESS_STR_MAX], *t; + fprintf(stderr, "Service '%s' of type '%s' in domain '%s':\n", name, type, domain); + avahi_address_snprint(a, sizeof(a), address); + t = avahi_string_list_to_string(txt); + teleport_app_add_peer(name, port, a); + /*fprintf(stderr, + "\t%s:%u (%s)\n" + "\tTXT=%s\n" + "\tcookie is %u\n" + "\tis_local: %i\n" + "\tour_own: %i\n" + "\twide_area: %i\n" + "\tmulticast: %i\n" + "\tcached: %i\n", + host_name, port, a, + t, + avahi_string_list_get_service_cookie(txt), + !!(flags & AVAHI_LOOKUP_RESULT_LOCAL), + !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN), + !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA), + !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST), + !!(flags & AVAHI_LOOKUP_RESULT_CACHED)); + */ + avahi_free(t); + } + } + avahi_service_resolver_free(r); +} +static void browse_callback( + AvahiServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + void* userdata) { + AvahiClient *c = userdata; + assert(b); + /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ + switch (event) { + case AVAHI_BROWSER_FAILURE: + fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); + avahi_shutdown(); + //avahi_simple_poll_quit(simple_poll); + return; + case AVAHI_BROWSER_NEW: + fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); + /* We ignore the returned resolver object. In the callback + function we free it. If the server is terminated before + the callback function is called the server will free + the resolver for us. */ + if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c))) + fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); + break; + case AVAHI_BROWSER_REMOVE: + fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); + teleport_app_remove_peer(name); + break; + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); + break; + } +} +static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { + assert(c); + /* Called whenever the client or server state changes */ + if (state == AVAHI_CLIENT_FAILURE) { + fprintf(stderr, "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c))); + avahi_shutdown(); + //avahi_simple_poll_quit(simple_poll); + } +} +/*int run_avahi_service() { + AvahiClient *client = NULL; + AvahiServiceBrowser *sb = NULL; + int error; + int ret = 1; + */ + /* Allocate main loop object */ + /*if (!(simple_poll = avahi_simple_poll_new())) { + fprintf(stderr, "Failed to create simple poll object.\n"); + goto fail; + } + */ + /* Allocate a new client */ + //client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error); + /* Check wether creating the client object succeeded */ + /*if (!client) { + fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error)); + goto fail; + } + *//* Create the service browser */ + /*if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_http._tcp", NULL, 0, browse_callback, client))) { + fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); + goto fail; + } + */ + /* Run the main loop */ +/* + + avahi_simple_poll_loop(simple_poll); + ret = 0; +fail: +*/ + /* Cleanup things */ +/* + if (sb) + avahi_service_browser_free(sb); + if (client) + avahi_client_free(client); + if (simple_poll) + avahi_simple_poll_free(simple_poll); + return ret; +} +*/ + +int run_avahi_service() { + /* Call this when the application starts up. */ + int error; + + if (!(threaded_poll = avahi_threaded_poll_new())) { + /* do something bad */ + return 1; + } + + if (!(client = avahi_client_new(avahi_threaded_poll_get(threaded_poll), 0, client_callback, NULL, &error))) { + fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error)); + /* do something bad */ + return 1; + } + + /* create some browsers on the client object here, if you wish */ + if (!(avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_http._tcp", NULL, 0, browse_callback, client))) { + /* so something bad */ + return 1; + } + + /* Finally, start the event loop thread */ + if (avahi_threaded_poll_start(threaded_poll) < 0) { + /* do something bad */ + return 1; + } + return 0; +} + +void avahi_shutdown(void) { + /* Call this when the app shuts down */ + + avahi_threaded_poll_stop(threaded_poll); + avahi_client_free(client); + avahi_threaded_poll_free(threaded_poll); +} diff --git a/src/browser.h b/src/browser.h new file mode 100644 index 0000000..968e1d5 --- /dev/null +++ b/src/browser.h @@ -0,0 +1,8 @@ +#ifndef __BROWSER_H +#define __BROWSER_H + + +extern int run_avahi_service(); +void avahi_shutdown(); + +#endif /* __BROWSER_H */ diff --git a/src/remote_list.ui b/src/remote_list.ui new file mode 100644 index 0000000..ea9a7ef --- /dev/null +++ b/src/remote_list.ui @@ -0,0 +1,67 @@ + + + + + True + True + false + false + + + True + False + 0 + 32 + 12 + 6 + 6 + 6 + center + + + True + False + True + 0 + end + Jan's Librem + True + + + 0 + 0 + 1 + 1 + + + + + True + True + end + center + Send File + + + 1 + 0 + 1 + 2 + + + + + + + True + True + false + false + + + true + horizontal + + + + diff --git a/src/teleportapp.c b/src/teleportapp.c index 9683a63..24a12b8 100644 --- a/src/teleportapp.c +++ b/src/teleportapp.c @@ -2,12 +2,23 @@ #include "teleportapp.h" #include "teleportappwin.h" +#include "browser.h" + +static TeleportAppWindow *win; struct _TeleportApp { GtkApplication parent; }; +typedef struct Peers { + char *name; + char *ip; + int port; +} Peer; + +static Peer remote_peers[100]; + G_DEFINE_TYPE(TeleportApp, teleport_app, GTK_TYPE_APPLICATION); static void @@ -18,10 +29,11 @@ teleport_app_init (TeleportApp *app) static void teleport_app_activate (GApplication *app) { - TeleportAppWindow *win; + //TeleportAppWindow *win; win = teleport_app_window_new (TELEPORT_APP (app)); gtk_window_present (GTK_WINDOW (win)); + run_avahi_service(); } static void @@ -31,7 +43,6 @@ teleport_app_open (GApplication *app, const gchar *hint) { GList *windows; - TeleportAppWindow *win; int i; windows = gtk_application_get_windows (GTK_APPLICATION (app)); @@ -61,3 +72,20 @@ teleport_app_new (void) "flags", G_APPLICATION_HANDLES_OPEN, NULL); } + + +void teleport_app_add_peer (char *name, int port, char* addr) { + fprintf(stderr, "\t%s:%u (%s)\n", name, port, addr); + if (win == NULL) { + fprintf(stderr, "You should do stuff"); + } + else { + fprintf(stderr, "%s", name); + update_remote_device_list(win, name); + } +} + +void teleport_app_remove_peer (char *name) { + fprintf(stderr, "A peer got removed"); + update_remote_device_list_remove(win, name); +} diff --git a/src/teleportapp.h b/src/teleportapp.h index bcb860a..49077d5 100644 --- a/src/teleportapp.h +++ b/src/teleportapp.h @@ -9,6 +9,8 @@ G_DECLARE_FINAL_TYPE (TeleportApp, teleport_app, TELEPORT, APP, GtkApplication) TeleportApp *teleport_app_new (void); +extern void teleport_app_add_peer(); +extern void teleport_app_remove_peer(); #endif /* __TELEPORTAPP_H */ diff --git a/src/teleportappwin.c b/src/teleportappwin.c index db49d1f..ac647ff 100644 --- a/src/teleportappwin.c +++ b/src/teleportappwin.c @@ -3,6 +3,9 @@ #include "teleportapp.h" #include "teleportappwin.h" + +GtkWidget* find_child(GtkWidget* , const gchar* ); + struct _TeleportAppWindow { GtkApplicationWindow parent; @@ -24,13 +27,7 @@ teleport_app_window_init (TeleportAppWindow *win) { TeleportAppWindowPrivate *priv; GtkBuilder *builder; - GtkBuilder *builder_remote_list; GtkWidget *menu; - GtkWidget *remote_list_row; - GtkLabel *remote_name; - GtkWidget *line; - GtkListStore *store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); - //GAction *action; priv = teleport_app_window_get_instance_private (win); gtk_widget_init_template (GTK_WIDGET (win)); @@ -39,31 +36,82 @@ teleport_app_window_init (TeleportAppWindow *win) menu = GTK_WIDGET (gtk_builder_get_object (builder, "settings")); gtk_menu_button_set_popover(GTK_MENU_BUTTON (priv->gears), menu); + //update_remote_device_list(win, "Jan"); + + g_object_unref (menu); + g_object_unref (builder); +} + +void update_remote_device_list(TeleportAppWindow *win, char * name) { + TeleportAppWindowPrivate *priv; + GtkBuilder *builder_remote_list; + GtkWidget *remote_list_row; + GtkLabel *remote_name; + //GtkWidget *line; + + priv = teleport_app_window_get_instance_private (win); builder_remote_list = gtk_builder_new_from_resource ("/org/gtk/teleportapp/remote_list.ui"); remote_list_row = GTK_WIDGET (gtk_builder_get_object (builder_remote_list, "remote_device_row")); remote_name = GTK_LABEL (gtk_builder_get_object (builder_remote_list, "device_name")); - gtk_label_set_text(remote_name, "Tobias's Laptop"); + gtk_label_set_text(remote_name, name); gtk_list_box_insert(GTK_LIST_BOX(priv->remote_devices_list), remote_list_row, -1); - - line = GTK_WIDGET (gtk_builder_get_object (builder_remote_list, "remote_space_row")); - gtk_list_box_insert(GTK_LIST_BOX(priv->remote_devices_list), line, -1); - -builder_remote_list = gtk_builder_new_from_resource ("/org/gtk/teleportapp/remote_list.ui"); - - remote_list_row = GTK_WIDGET (gtk_builder_get_object (builder_remote_list, "remote_device_row")); - remote_name = GTK_LABEL (gtk_builder_get_object (builder_remote_list, "device_name")); - gtk_label_set_text(remote_name, "Tobias's Laptop"); - gtk_list_box_insert(GTK_LIST_BOX(priv->remote_devices_list), remote_list_row, -1); - - - g_object_unref (builder); - g_object_unref (menu); - g_object_unref (remote_list_row); + //line = GTK_WIDGET (gtk_builder_get_object (builder_remote_list, "remote_space_row")); + //gtk_list_box_insert(GTK_LIST_BOX(priv->remote_devices_list), line, -1); + g_object_unref (builder_remote_list); } +void update_remote_device_list_remove(TeleportAppWindow *win, char * name) { + TeleportAppWindowPrivate *priv; + GtkWidget *box; + GtkListBoxRow *remote_row; + GtkLabel *remote_name; + GtkWidget *line; + gint i = 0; + + priv = teleport_app_window_get_instance_private (win); + box = priv->remote_devices_list; + + remote_row = gtk_list_box_get_row_at_index (GTK_LIST_BOX(box), i); + + while(remote_row != NULL) { + remote_name = GTK_LABEL(find_child(GTK_WIDGET(remote_row), "GtkLabel")); + if (remote_name != NULL && g_strcmp0(name, gtk_label_get_text(remote_name)) == 0) { + gtk_container_remove (GTK_CONTAINER(box), GTK_WIDGET(remote_row)); + } + i++; + remote_row = gtk_list_box_get_row_at_index (GTK_LIST_BOX(box), i); + } +} + + GtkWidget* +find_child(GtkWidget* parent, const gchar* name) +{ + if (g_strcmp0(gtk_widget_get_name((GtkWidget*)parent), (gchar*)name) == 0) { + return parent; + } + + if (GTK_IS_BIN(parent)) { + GtkWidget *child = gtk_bin_get_child(GTK_BIN(parent)); + return find_child(child, name); + } + + if (GTK_IS_CONTAINER(parent)) { + GList *children = gtk_container_get_children(GTK_CONTAINER(parent)); + while ((children = g_list_next(children)) != NULL) { + GtkWidget* widget = find_child(children->data, name); + if (widget != NULL) { + return widget; + } + } + } + + return NULL; +} + + static void teleport_app_window_dispose (GObject *object) { diff --git a/src/teleportappwin.h b/src/teleportappwin.h index 658ad94..eeff103 100644 --- a/src/teleportappwin.h +++ b/src/teleportappwin.h @@ -11,6 +11,8 @@ G_DECLARE_FINAL_TYPE (TeleportAppWindow, teleport_app_window, TELEPORT, APP_WIND TeleportAppWindow *teleport_app_window_new (TeleportApp *app); void teleport_app_window_open (TeleportAppWindow *win, GFile *file); + extern void update_remote_device_list(TeleportAppWindow *, char *); + extern void update_remote_device_list_remove(TeleportAppWindow *, char *); #endif /* __TELEPORTAPPWIN_H */