#include #include #include #include #include #include #include #include /* FIXME: find more portable one */ #include #include #ifdef HAVE_AVAHI # include # include # include # include # include # include #endif #include "list.h" #include "apps.h" #include "mdns.h" #ifdef HAVE_AVAHI static int debug = 0; /* ---------------------------------------------------------------------- */ /* build URLs */ struct protocols { char *type; char *proto; int defport; }; static const struct protocols protocols[] = { { "_ftp._tcp", "ftp", 21 }, { "_ssh._tcp", "ssh", 22 }, { "_http._tcp", "http", 80 }, { "_rfb._tcp", "vnc", -1 }, }; /* ---------------------------------------------------------------------- */ /* start apps */ struct actions { enum desktop_type desktop; char *type; char *tryapp; char *cmdline; int needurl:1; }; static const struct actions default_actions[] = { { .desktop = DESKTOP_ANY, .needurl = 1, .tryapp = "xdg-open", .cmdline = "xdg-open %u", },{ .desktop = DESKTOP_KDE, .needurl = 1, .tryapp = "kfmclient", .cmdline = "kfmclient exec %u", },{ .desktop = DESKTOP_GNOME, .needurl = 1, .tryapp = "gnome-open", .cmdline = "gnome-open %u", },{ .desktop = DESKTOP_ANY, .type = "_ssh._tcp", .cmdline = "xterm -title \"%n\" -e ssh -p %p \"%h\"", },{ .desktop = DESKTOP_ANY, .type = "_rfb._tcp", .tryapp = "vncviewer", .cmdline = "vncviewer %h::%p", } }; /* ---------------------------------------------------------------------- */ enum { /* browse */ ST_COL_NAME = 0, ST_COL_TYPE, ST_COL_DOMAIN, ST_COL_INTERFACE, ST_COL_PROTOCOL, /* resolve */ ST_COL_HOSTNAME, ST_COL_ADDR, ST_COL_PORT, ST_COL_PATH, /* other */ ST_COL_URL, ST_COL_XEN_DOM_ID, ST_COL_XEN_VM_UUID, ST_NUM_COLS }; static int str_sort[] = { ST_COL_NAME, ST_COL_TYPE, ST_COL_DOMAIN, ST_COL_INTERFACE, ST_COL_PROTOCOL, ST_COL_HOSTNAME, }; struct mdns_browser { char *service; char *domain; AvahiServiceBrowser *sb; struct list_head next; }; struct mdns_window { GtkListStore *store; GtkWidget *toplevel, *view, *status; GtkActionGroup *ag; int standalone; mdns_callback callback; const AvahiPoll *poll_api; AvahiGLibPoll *glib_poll; AvahiClient *client; struct list_head browser; }; /* ---------------------------------------------------------------------- */ static const char *revents[] = { [ AVAHI_RESOLVER_FOUND ] = "FOUND", [ AVAHI_RESOLVER_FAILURE ] = "FAILURE", }; static const char *bevents[] = { [ AVAHI_BROWSER_NEW ] = "NEW", [ AVAHI_BROWSER_REMOVE ] = "REMOVE", [ AVAHI_BROWSER_CACHE_EXHAUSTED ] = "CACHE_EXHAUSTED", [ AVAHI_BROWSER_ALL_FOR_NOW ] = "ALL_FOR_NOW", [ AVAHI_BROWSER_FAILURE ] = "FAILURE", }; /* ---------------------------------------------------------------------- */ static int find_entry(struct mdns_window *mdns, GtkTreeIter *iter, const char *sname, const char *stype, const char *sdomain, const char *sproto, const char *snif) { GtkTreeModel *model = GTK_TREE_MODEL(mdns->store); gboolean valid; char *name, *type, *domain, *proto, *nif; for (valid = gtk_tree_model_get_iter_first(model, iter); valid; valid = gtk_tree_model_iter_next(model, iter)) { gtk_tree_model_get(model, iter, ST_COL_NAME, &name, ST_COL_TYPE, &type, ST_COL_DOMAIN, &domain, ST_COL_PROTOCOL, &proto, ST_COL_INTERFACE, &nif, -1); if (0 == strcmp(name, sname) && 0 == strcmp(type, stype) && 0 == strcmp(domain, sdomain) && 0 == strcmp(proto, sproto) && 0 == strcmp(nif, snif)) return 0; } return -1; } static void get_entry(struct mdns_window *mdns, GtkTreeIter *iter, const char *sname, const char *stype, const char *sdomain, const char *sproto, const char *snif) { if (0 == find_entry(mdns, iter, sname, stype, sdomain, sproto, snif)) return; gtk_list_store_append(mdns->store, iter); gtk_list_store_set(mdns->store, iter, ST_COL_NAME, sname, ST_COL_TYPE, stype, ST_COL_DOMAIN, sdomain, ST_COL_PROTOCOL, sproto, ST_COL_INTERFACE, snif, -1); if (debug) fprintf(stderr, "add: %s: %s\n",stype, sname); } static void del_entry(struct mdns_window *mdns, const char *sname, const char *stype, const char *sdomain, const char *sproto, const char *snif) { GtkTreeIter iter; if (0 != find_entry(mdns, &iter, sname, stype, sdomain, sproto, snif)) return; gtk_list_store_remove(mdns->store, &iter); if (debug) fprintf(stderr, "del: %s: %s\n", stype, sname); } static void del_entries(struct mdns_window *mdns) { GtkTreeModel *model = GTK_TREE_MODEL(mdns->store); GtkTreeIter iter; while (gtk_tree_model_get_iter_first(model, &iter)) gtk_list_store_remove(mdns->store, &iter); } /* ---------------------------------------------------------------------- */ static void ifname(char *dst, int len, int nif) { #ifdef SIOCGIFNAME struct ifreq ifr; int fd; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) goto fallback; ifr.ifr_ifindex = nif; if (ioctl (fd, SIOCGIFNAME, &ifr) < 0) { close (fd); goto fallback; } snprintf(dst, len, "%s", ifr.ifr_name); close (fd); return; fallback: #endif if (-1 == nif) { snprintf(dst, len, "wide"); return; } snprintf(dst, len, "if-%d", nif); } static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, 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, void* userdata) { struct mdns_window *mdns = userdata; char a[AVAHI_ADDRESS_STR_MAX], p[32], url[256]; unsigned char *txtstr, *path = NULL; char *proto = NULL; AvahiStringList *txtlist; GtkTreeIter iter; char nif[32]; int defport = 0, i; switch (event) { case AVAHI_RESOLVER_FOUND: ifname(nif, sizeof(nif), interface); if (0 != find_entry(mdns, &iter, name, type, domain, avahi_proto_to_string(protocol), nif)) break; avahi_address_snprint(a, sizeof(a), address); snprintf(p, sizeof(p), "%d", port); gtk_list_store_set(mdns->store, &iter, ST_COL_HOSTNAME, host_name, ST_COL_ADDR, a, ST_COL_PORT, p, -1); /* xen stuff */ txtlist = avahi_string_list_find(txt, "dom-id"); if (txtlist) { txtstr = avahi_string_list_get_text(txtlist); if (txtstr) { gtk_list_store_set(mdns->store, &iter, ST_COL_XEN_DOM_ID, txtstr+7, -1); } } txtlist = avahi_string_list_find(txt, "vm-uuid"); if (txtlist) { txtstr = avahi_string_list_get_text(txtlist); if (txtstr) { gtk_list_store_set(mdns->store, &iter, ST_COL_XEN_VM_UUID, txtstr+8, -1); } } /* path */ txtlist = avahi_string_list_find(txt, "path"); if (txtlist) { txtstr = avahi_string_list_get_text(txtlist); if (txtstr) { path = txtstr+5; gtk_list_store_set(mdns->store, &iter, ST_COL_PATH, path, -1); } } /* url */ for (i = 0; i < array_size(protocols); i++) { if (0 != strcmp(protocols[i].type, type)) continue; proto = protocols[i].proto; defport = protocols[i].defport; break; } if (!proto) break; if (!path) path = (unsigned char*)""; if (defport != port) snprintf(url, sizeof(url), "%s://%s:%d%s", proto, a /* host_name */, port, path); else snprintf(url, sizeof(url), "%s://%s%s", proto, a /* host_name */, path); gtk_list_store_set(mdns->store, &iter, ST_COL_URL, url, -1); break; default: fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, revents[event], event); break; } 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, AvahiLookupResultFlags flags, void* userdata) { struct mdns_window *mdns = userdata; GtkTreeIter iter; char nif[32]; ifname(nif, sizeof(nif), interface); switch (event) { case AVAHI_BROWSER_NEW: get_entry(mdns, &iter, name, type, domain, avahi_proto_to_string(protocol), nif); avahi_service_resolver_new(mdns->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, mdns); break; case AVAHI_BROWSER_REMOVE: del_entry(mdns, name, type, domain, avahi_proto_to_string(protocol), nif); break; default: if (debug) fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, bevents[event], event); break; } } static void client_callback(AvahiClient *client, AvahiClientState state, void *userdata) { // struct mdns_window *mdns = userdata; switch (state) { case AVAHI_CLIENT_FAILURE: fprintf(stderr, "%s: error: connection lost\n", __FUNCTION__); break; default: if (debug) fprintf(stderr, "%s: state %d\n", __FUNCTION__, state); break; } } /* ---------------------------------------------------------------------- */ static void service_type_browser_callback(AvahiServiceTypeBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *type, const char *domain, AvahiLookupResultFlags flags, void *userdata) { char nif[32]; switch (event) { case AVAHI_BROWSER_NEW: ifname(nif, sizeof(nif), interface); fprintf(stderr, " service: %s, %s, %s: %s\n", nif, avahi_proto_to_string(protocol), domain, type); break; case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_FAILURE: avahi_service_type_browser_free(b); break; case AVAHI_BROWSER_CACHE_EXHAUSTED: /* don't log */ break; default: fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, bevents[event], event); break; } } static void domain_browser_callback(AvahiDomainBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *domain, AvahiLookupResultFlags flags, void *userdata) { struct mdns_window *mdns = userdata; char nif[32]; switch (event) { case AVAHI_BROWSER_NEW: ifname(nif, sizeof(nif), interface); fprintf(stderr, " domain : %s, %s, %s\n", nif, avahi_proto_to_string(protocol), domain); avahi_service_type_browser_new(mdns->client, interface, protocol, domain, 0, service_type_browser_callback, mdns); break; case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_FAILURE: avahi_domain_browser_free(b); break; case AVAHI_BROWSER_CACHE_EXHAUSTED: /* don't log */ break; default: fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, bevents[event], event); break; } } static void dump_stuff(struct mdns_window *mdns) { const char *domain; /* playground ... */ domain = avahi_client_get_domain_name(mdns->client); fprintf(stderr, "default domain is \"%s\"\n", domain); avahi_service_type_browser_new(mdns->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, domain, 0, service_type_browser_callback, mdns); avahi_domain_browser_new(mdns->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browser_callback, mdns); } /* ---------------------------------------------------------------------- */ static void mdns_fini(struct mdns_window *mdns) { if (mdns->client) { if (debug) fprintf(stderr, "%s\n", __FUNCTION__); avahi_client_free(mdns->client); mdns->client = NULL; } if (mdns->glib_poll) { avahi_glib_poll_free(mdns->glib_poll); mdns->glib_poll = NULL; } } static int mdns_init(struct mdns_window *mdns) { int error; if (mdns->client) return 0; if (debug) fprintf(stderr, "%s\n", __FUNCTION__); /* Create the GLIB Adaptor */ avahi_set_allocator(avahi_glib_allocator()); mdns->glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT); mdns->poll_api = avahi_glib_poll_get(mdns->glib_poll); mdns->client = avahi_client_new(mdns->poll_api, AVAHI_CLIENT_NO_FAIL, client_callback, mdns, &error); if (mdns->client == NULL) goto fail; INIT_LIST_HEAD(&mdns->browser); if (0) dump_stuff(mdns); return 0; fail: mdns_fini(mdns); return -1; } int mdns_browse(struct mdns_window *mdns, int replace, const char *service, const char *domain) { struct list_head *item, *tmp; struct mdns_browser *br; char label[256]; int pos; if (NULL == domain) domain = avahi_client_get_domain_name(mdns->client); if (replace) { /* zap old browsers and entries */ list_for_each_safe(item, tmp, &mdns->browser) { br = list_entry(item, struct mdns_browser, next); avahi_service_browser_free(br->sb); list_del(&br->next); free(br->domain); free(br->service); free(br); } del_entries(mdns); gtk_label_set_text(GTK_LABEL(mdns->status), "idle"); } /* allocate new one */ br = malloc(sizeof(*br)); memset(br,0,sizeof(*br)); br->sb = avahi_service_browser_new(mdns->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, service, domain, 0, browse_callback, mdns); if (NULL == br->sb) { fprintf(stderr, "%s: failed to create service browser: %s\n", __FUNCTION__, avahi_strerror(avahi_client_errno(mdns->client))); free(br); return -1; } br->service = strdup(service); br->domain = strdup(domain); list_add_tail(&br->next,&mdns->browser); /* update status line */ pos = 0; list_for_each_safe(item, tmp, &mdns->browser) { br = list_entry(item, struct mdns_browser, next); pos += snprintf(label+pos, sizeof(label)-pos, "%s%s", pos ? ", " : "", br->service); } gtk_label_set_text(GTK_LABEL(mdns->status), label); return 0; } /* ---------------------------------------------------------------------- */ static void menu_cb_view_default(GtkToggleAction *action, gpointer userdata) { struct mdns_window *mdns = userdata; mdns_view(mdns, MDNS_VIEW_DEFAULT); } static void menu_cb_view_url(GtkToggleAction *action, gpointer userdata) { struct mdns_window *mdns = userdata; mdns_view(mdns, MDNS_VIEW_URL); } static void menu_cb_view_xen(GtkToggleAction *action, gpointer userdata) { struct mdns_window *mdns = userdata; mdns_view(mdns, MDNS_VIEW_XEN); } static void menu_cb_view_col(GtkToggleAction *action, gpointer userdata, int id) { struct mdns_window *mdns = userdata; GtkTreeViewColumn *col; gboolean active; active = gtk_toggle_action_get_active(action); col = gtk_tree_view_get_column(GTK_TREE_VIEW(mdns->view), id); gtk_tree_view_column_set_visible(col, active); } static void menu_cb_view_col_type(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_TYPE); } static void menu_cb_view_col_domain(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_DOMAIN); } static void menu_cb_view_col_interface(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_INTERFACE); } static void menu_cb_view_col_protocol(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_PROTOCOL); } static void menu_cb_view_col_hostname(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_HOSTNAME); } static void menu_cb_view_col_addr(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_ADDR); } static void menu_cb_view_col_port(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_PORT); } static void menu_cb_view_col_path(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_PATH); } static void menu_cb_view_col_url(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_URL); } static void menu_cb_view_col_xen_dom_id(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_XEN_DOM_ID); } static void menu_cb_view_col_xen_vm_uuid(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_XEN_VM_UUID); } static void menu_cb_close(GtkAction *action, gpointer userdata) { struct mdns_window *mdns = userdata; gtk_widget_destroy(mdns->toplevel); } static void destroy(GtkWidget *widget, gpointer data) { struct mdns_window *mdns = data; mdns_fini(mdns); g_object_unref(mdns->store); if (mdns->standalone) gtk_main_quit(); free(mdns); } /* ---------------------------------------------------------------------- */ static int run_actions(const struct actions *actions, int nactions, char *name, char *type, char *host, char *port, char *url) { int i; for (i = 0; i < nactions; i++) { if (actions[i].desktop != DESKTOP_ANY && actions[i].desktop != desktop_type) continue; if (actions[i].type && 0 != strcmp(actions[i].type, type)) continue; if (actions[i].tryapp && !have_application(actions[i].tryapp)) continue; if (actions[i].needurl && NULL == url) continue; run_cmdline_replace(0, actions[i].cmdline, "%n", name, "%t", type, "%h", host, "%p", port, "%u", url, NULL); return 0; } return -1; } static void row_activate(GtkTreeView* treeview, GtkTreePath *path, GtkTreeViewColumn* col, gpointer data) { struct mdns_window *mdns = data; GtkTreeModel* model; GtkTreeIter iter; char *name, *type, *host, *port, *url; int rc; model = gtk_tree_view_get_model(treeview); if (!gtk_tree_model_get_iter(model, &iter, path)) return; gtk_tree_model_get(model, &iter, ST_COL_NAME, &name, ST_COL_TYPE, &type, ST_COL_HOSTNAME, &host, ST_COL_PORT, &port, ST_COL_URL, &url, -1); if (mdns->callback) { mdns->callback(mdns, name, type, host, atoi(port), url); } else { rc = run_actions(default_actions, array_size(default_actions), name, type, host, port, url); /* FIXME: error message if failed */ } } /* ---------------------------------------------------------------------- */ static const GtkActionEntry entries[] = { { .name = "FileMenu", .label = "_File", },{ .name = "ViewMenu", .label = "_View", },{ .name = "Close", .stock_id = GTK_STOCK_CLOSE, .label = "_Close", .accelerator = "Q", .callback = G_CALLBACK(menu_cb_close), },{ .name = "ViewDefault", .label = "_Default", .callback = G_CALLBACK(menu_cb_view_default), },{ .name = "ViewURL", .label = "_URL", .callback = G_CALLBACK(menu_cb_view_url), },{ .name = "ViewXen", .label = "_Xen", .callback = G_CALLBACK(menu_cb_view_xen), }, }; static const GtkToggleActionEntry tentries[] = { { .name = "ColType", .label = "Type", .callback = G_CALLBACK(menu_cb_view_col_type), .is_active = 1, },{ .name = "ColDomain", .label = "Domain", .callback = G_CALLBACK(menu_cb_view_col_domain), .is_active = 1, },{ .name = "ColInterface", .label = "Interface", .callback = G_CALLBACK(menu_cb_view_col_interface), .is_active = 1, },{ .name = "ColProtocol", .label = "Protocol", .callback = G_CALLBACK(menu_cb_view_col_protocol), .is_active = 1, },{ .name = "ColHostname", .label = "Hostname", .callback = G_CALLBACK(menu_cb_view_col_hostname), .is_active = 1, },{ .name = "ColAddress", .label = "Address", .callback = G_CALLBACK(menu_cb_view_col_addr), .is_active = 1, },{ .name = "ColPort", .label = "Port", .callback = G_CALLBACK(menu_cb_view_col_port), .is_active = 1, },{ .name = "ColPath", .label = "Path", .callback = G_CALLBACK(menu_cb_view_col_path), .is_active = 1, },{ .name = "ColURL", .label = "URL", .callback = G_CALLBACK(menu_cb_view_col_url), .is_active = 1, },{ .name = "ColXenDomID", .label = "Xen dom-id", .callback = G_CALLBACK(menu_cb_view_col_xen_dom_id), .is_active = 1, },{ .name = "ColXenVmUUID", .label = "Xen vm-uuid", .callback = G_CALLBACK(menu_cb_view_col_xen_vm_uuid), .is_active = 1, } }; static char ui_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " #ifdef WITH_TOOLBAR " " " " " " #endif ""; /* ------------------------------------------------------------------ */ static gint gtk_sort_iter_compare_str(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata) { gint sortcol = GPOINTER_TO_INT(userdata); char *aa,*bb; gtk_tree_model_get(model, a, sortcol, &aa, -1); gtk_tree_model_get(model, b, sortcol, &bb, -1); if (NULL == aa && NULL == bb) return 0; if (NULL == aa) return 1; if (NULL == bb) return -1; return strcmp(aa,bb); } static GtkWidget *mdns_create_view(struct mdns_window *mdns) { GtkCellRenderer *renderer; GtkTreeSortable *sortable; GtkWidget *view; GtkTreeViewColumn *col; int i; view = gtk_tree_view_new(); gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(mdns->store)); gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), GTK_SELECTION_SINGLE); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Name", renderer, "text", ST_COL_NAME, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Type", renderer, "text", ST_COL_TYPE, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Domain", renderer, "text", ST_COL_DOMAIN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "If", renderer, "text", ST_COL_INTERFACE, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Proto", renderer, "text", ST_COL_PROTOCOL, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Hostname", renderer, "text", ST_COL_HOSTNAME, NULL); renderer = gtk_cell_renderer_text_new(); g_object_set(renderer, "xalign", 1.0, NULL); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Address", renderer, "text", ST_COL_ADDR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Port", renderer, "text", ST_COL_PORT, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Path", renderer, "text", ST_COL_PATH, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "URL", renderer, "text", ST_COL_URL, NULL); renderer = gtk_cell_renderer_text_new(); g_object_set(renderer, "xalign", 1.0, NULL); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "ID", renderer, "text", ST_COL_XEN_DOM_ID, NULL); renderer = gtk_cell_renderer_text_new(); g_object_set(renderer, "font", "monospace", NULL); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "VM", renderer, "text", ST_COL_XEN_VM_UUID, NULL); /* fill remaining space */ renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "", renderer, NULL); /* sort bits */ sortable = GTK_TREE_SORTABLE(mdns->store); for (i = 0; i < sizeof(str_sort)/sizeof(str_sort[0]); i++) { gtk_tree_sortable_set_sort_func(sortable, str_sort[i], gtk_sort_iter_compare_str, GINT_TO_POINTER(str_sort[i]), NULL); col = gtk_tree_view_get_column(GTK_TREE_VIEW(view), str_sort[i]); gtk_tree_view_column_set_sort_column_id(col, str_sort[i]); } gtk_tree_sortable_set_sort_column_id(sortable, ST_COL_NAME, GTK_SORT_ASCENDING); return view; } static void set_default_visible_cols(struct mdns_window *mdns, enum mdns_view view) { GtkToggleAction *ta; ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColType")); gtk_toggle_action_set_active(ta, FALSE); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColDomain")); gtk_toggle_action_set_active(ta, FALSE); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColInterface")); gtk_toggle_action_set_active(ta, TRUE); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColProtocol")); gtk_toggle_action_set_active(ta, TRUE); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColHostname")); gtk_toggle_action_set_active(ta, TRUE); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColAddress")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_DEFAULT); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColPort")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_DEFAULT); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColPath")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_DEFAULT); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColURL")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_URL); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColXenDomID")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_XEN); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColXenVmUUID")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_XEN); } struct mdns_window *mdns_create_window(int standalone, enum mdns_view view, mdns_callback callback) { struct mdns_window *mdns; GtkWidget *vbox, *menubar, *toolbar, *scroll, *frame; GtkAccelGroup *accel; GtkUIManager *ui; GError *err; mdns = malloc(sizeof(*mdns)); memset(mdns,0,sizeof(*mdns)); if (-1 == mdns_init(mdns)) { free(mdns); return NULL; } mdns->standalone = standalone; mdns->callback = callback; mdns->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(mdns->toplevel), "mdns"); gtk_widget_set_size_request(GTK_WIDGET(mdns->toplevel), 480, 320); g_signal_connect(G_OBJECT(mdns->toplevel), "destroy", G_CALLBACK(destroy), mdns); /* menu + toolbar */ ui = gtk_ui_manager_new(); mdns->ag = gtk_action_group_new("MenuActions"); gtk_action_group_add_actions(mdns->ag, entries, G_N_ELEMENTS(entries), mdns); gtk_action_group_add_toggle_actions(mdns->ag, tentries, G_N_ELEMENTS(tentries), mdns); gtk_ui_manager_insert_action_group(ui, mdns->ag, 0); accel = gtk_ui_manager_get_accel_group(ui); gtk_window_add_accel_group(GTK_WINDOW(mdns->toplevel), accel); err = NULL; if (!gtk_ui_manager_add_ui_from_string(ui, ui_xml, -1, &err)) { g_message("building menus failed: %s", err->message); g_error_free(err); exit(1); } /* list */ mdns->store = gtk_list_store_new(ST_NUM_COLS, G_TYPE_STRING, // ST_COL_NAME G_TYPE_STRING, // ST_COL_TYPE G_TYPE_STRING, // ST_COL_DOMAIN G_TYPE_STRING, // ST_COL_INTERFACE G_TYPE_STRING, // ST_COL_PROTOCOL G_TYPE_STRING, // ST_COL_HOSTNAME G_TYPE_STRING, // ST_COL_ADDR G_TYPE_STRING, // ST_COL_PORT G_TYPE_STRING, // ST_COL_PATH G_TYPE_STRING, // ST_COL_URL G_TYPE_STRING, // ST_COL_XEN_DOM_ID G_TYPE_STRING, // ST_COL_XEN_VM_UUID NULL); mdns->view = mdns_create_view(mdns); g_signal_connect(mdns->view, "row-activated", G_CALLBACK(row_activate), mdns); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); /* other widgets */ mdns->status = gtk_label_new("status line"); gtk_misc_set_alignment(GTK_MISC(mdns->status), 0, 0.5); gtk_misc_set_padding(GTK_MISC(mdns->status), 3, 1); /* Make a vbox and put stuff in */ vbox = gtk_vbox_new(FALSE, 1); gtk_container_set_border_width(GTK_CONTAINER(vbox), 1); gtk_container_add(GTK_CONTAINER(mdns->toplevel), vbox); menubar = gtk_ui_manager_get_widget(ui, "/MainMenu"); gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); toolbar = gtk_ui_manager_get_widget(ui, "/ToolBar"); if (toolbar) gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(scroll), mdns->view); frame = gtk_frame_new(NULL); gtk_box_pack_end(GTK_BOX(vbox), frame, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), mdns->status); set_default_visible_cols(mdns, view); return mdns; } void mdns_show_window(struct mdns_window *mdns) { gtk_widget_show_all(mdns->toplevel); } void mdns_destroy_window(struct mdns_window *mdns) { gtk_widget_destroy(mdns->toplevel); } int mdns_view(struct mdns_window *mdns, enum mdns_view view) { set_default_visible_cols(mdns, view); return 0; } /* ---------------------------------------------------------------------- */ #else /* ! HAVE_AVAHI */ struct mdns_window *mdns_create_window(int standalone, enum mdns_view view, mdns_callback callback) { fprintf(stderr,"Compiled without mDNS support, sorry.\n"); return NULL; } void mdns_show_window(struct mdns_window *mdns) {} void mdns_destroy_window(struct mdns_window *mdns) {} int mdns_browse(struct mdns_window *mdns, int replace, const char *service, const char *domain) { return -1; } int mdns_view(struct mdns_window *mdns, enum mdns_view view) { return -1; } #endif