Guitarix
avahi_discover.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
3  * Copyright (C) 2011 Pete Shorthose
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  * ---------------------------------------------------------------------------
19  */
20 
21 #include "avahi_discover.h"
22 
23 /****************************************************************
24  ** class AvahiBrowser
25  */
26 
28  : client(0),
29  browser(0),
30  resolver(0),
31  services(),
32  service_names(),
33  changed(),
34  cache_done(false),
35  found_name(),
36  found_host() {
37  client = ga_client_new(GaClientFlags(0));
38  GError *error = 0;
39  if (!ga_client_start(client, &error)) {
41  _("Avahi system error"),
42  Glib::ustring::compose(
43  _("%1\n\nuse command line arguments --rpchost and/or"
44  " --rpcport to connect directly"), error->message));
45  }
46  browser = ga_service_browser_new("_guitarix._tcp");
47  ga_service_browser_attach(browser, client, &error);
48  g_signal_connect(browser, "new-service", G_CALLBACK(new_service), this);
49  g_signal_connect(browser, "removed-service", G_CALLBACK(removed_service), this);
50  g_signal_connect(browser, "cache-exhausted", G_CALLBACK(cache_exhausted), this);
51 }
52 
54  if (resolver) {
55  g_object_unref(resolver);
56  }
57  if (browser) {
58  g_object_unref(browser);
59  }
60  if (client) {
61  g_object_unref(client);
62  }
63 }
64 
65 static inline std::ostream& operator<<(std::ostream& o, const AvahiBrowser::Entry& e) {
66  o << "<" << e.interface << ", " << e.protocol << ", " << e.name << ", " << e.type << ", " << e.domain << ", " << e.flags << ">";
67  return o;
68 }
69 
70 bool AvahiBrowser::get_address_port(Glib::ustring& address, int& port, Glib::ustring& name, Glib::ustring& host) {
71  AvahiAddress a = AvahiAddress();
72  uint16_t p = uint16_t();
73  if (resolver && ga_service_resolver_get_address(resolver, &a, &p)) {
74  char buf[AVAHI_ADDRESS_STR_MAX];
75  address = avahi_address_snprint(buf, AVAHI_ADDRESS_STR_MAX, &a);
76  port = p;
77  name = found_name;
78  host = found_host;
79  return true;
80  }
81  return false;
82 }
83 
84 void AvahiBrowser::get_service_names(std::vector<Glib::ustring>& r) {
85  for (std::map<Glib::ustring,int>::iterator i = service_names.begin(); i != service_names.end(); ++i) {
86  r.push_back(i->first);
87  }
88 }
89 
90 bool AvahiBrowser::invoke_resolver(const Glib::ustring& name) {
91  AvahiProtocol protocol = AVAHI_PROTO_UNSPEC;
92  AvahiIfIndex interface = AVAHI_IF_UNSPEC;
93  Entry *e = 0;
94  for (std::list<Entry>::iterator i = services.begin(); i != services.end(); ++i) {
95  if (i->name != name) {
96  continue;
97  }
98  if (i->protocol < protocol) {
99  continue;
100  }
101  if (i->interface < interface) {
102  continue;
103  }
104  e = &(*i);
105  }
106  if (!e) {
107  return false;
108  }
109  assert(!resolver);
110  resolver = ga_service_resolver_new(
111  e->interface, e->protocol, e->name.c_str(), e->type.c_str(), e->domain.c_str(), e->protocol, GA_LOOKUP_NO_FLAGS);
112  g_signal_connect(resolver, "found", G_CALLBACK(on_found), this);
113  g_signal_connect(resolver, "failure", G_CALLBACK(on_failure), this);
114  GError *error = 0;
115  ga_service_resolver_attach(resolver, client, &error);
116  return true;
117 }
118 
119 //static
120 void AvahiBrowser::new_service(
121  GaServiceBrowser *browser, AvahiIfIndex interface, AvahiProtocol protocol,
122  const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void *data) {
123  AvahiBrowser& self = *static_cast<AvahiBrowser*>(data);
124  self.services.push_back(Entry(interface,protocol,name,type,domain,flags));
125  if (self.service_names.find(name) == self.service_names.end()) {
126  self.service_names[name] = 1;
127  if (self.cache_done) {
128  self.changed();
129  }
130  } else {
131  self.service_names[name] += 1;
132  }
133 }
134 
135 //static
136 void AvahiBrowser::removed_service(
137  GaServiceBrowser *browser, AvahiIfIndex interface, AvahiProtocol protocol,
138  const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void *data) {
139  AvahiBrowser& self = *static_cast<AvahiBrowser*>(data);
140  Entry e(interface,protocol,name,type,domain,flags);
141  for (std::list<Entry>::iterator i = self.services.begin(); i != self.services.end(); ++i) {
142  if (*i == e) {
143  self.services.erase(i);
144  self.service_names[name] -= 1;
145  if (self.service_names[name] <= 0) {
146  self.service_names.erase(name);
147  if (self.cache_done) {
148  self.changed();
149  }
150  }
151  break;
152  }
153  }
154 }
155 
156 //static
157 void AvahiBrowser::cache_exhausted(GaServiceBrowser *browser, void *data) {
158  AvahiBrowser& self = *static_cast<AvahiBrowser*>(data);
159  self.cache_done = true;
160  self.changed();
161 }
162 
163 //static
164 void AvahiBrowser::on_found(
165  GaServiceResolver *resolver, AvahiIfIndex interface, AvahiProtocol protocol,
166  const char *name, const char *type, const char *domain, const char *host_name,
167  const AvahiAddress * a, uint16_t port, AvahiStringList * txt,
168  AvahiLookupResultFlags flags, void *data) {
169  AvahiBrowser& self = *static_cast<AvahiBrowser*>(data);
170  self.found_name = name;
171  self.found_host = host_name;
172  Gtk::Main::quit();
173 }
174 
175 //static
176 void AvahiBrowser::on_failure(GaServiceResolver *resolver, GError *error, void *data) {
177  Gtk::Main::quit();
178 }
179 
180 /****************************************************************
181  ** class SelectInstance
182  */
183 
185  : splash(splash_),
186  win(0),
187  view(0),
188  av(),
189  cols() {
190  Glib::RefPtr<gx_gui::GxBuilder> bld = gx_gui::GxBuilder::create_from_file(
191  options.get_builder_filepath("selectinstance.glade"));
192  bld->get_toplevel("SelectInstance", win);
193  win->signal_response().connect(sigc::mem_fun(this, &SelectInstance::on_response));
194  bld->get_widget("treeview", view);
195  view->signal_row_activated().connect(
196  sigc::mem_fun(this,&SelectInstance::on_row));
197  //view->signal_row_activated().connect(
198  //sigc::group(sigc::mem_fun(win, &Gtk::Dialog::response), 1));
199  view->set_model(Gtk::ListStore::create(cols));
200  view->get_selection()->set_mode(Gtk::SELECTION_BROWSE);
201  view->get_selection()->signal_changed().connect(
202  sigc::mem_fun(this, &SelectInstance::on_selection_changed));
203  av.signal_changed().connect(sigc::mem_fun(this, &SelectInstance::on_avahi_changed));
204  Gtk::Main::run();
205 };
206 
208  delete win;
209 }
210 
211 void SelectInstance::on_selection_changed() {
212  win->get_widget_for_response(1)->set_sensitive(view->get_selection()->get_selected());
213 }
214 
215 void SelectInstance::on_avahi_changed() {
216  std::vector<Glib::ustring> r;
217  av.get_service_names(r);
218  if (!win->is_visible()) {
219  if (r.size() == 1) {
220  av.invoke_resolver(*r.begin());
221  } else {
222  if (splash) {
223  splash->hide();
224  }
225  win->show();
226  }
227  }
228  Glib::RefPtr<Gtk::ListStore> ls = Glib::RefPtr<Gtk::ListStore>::cast_dynamic(view->get_model());
229  Gtk::TreeIter s = view->get_selection()->get_selected();
230  Glib::ustring old;
231  if (s) {
232  old = s->get_value(cols.name);
233  }
234  ls->clear();
235  for (std::vector<Glib::ustring>::iterator i = r.begin(); i != r.end(); ++i) {
236  Gtk::TreeIter j = ls->append();
237  j->set_value(cols.name, *i);
238  if (old.empty()) {
239  old = *i;
240  }
241  if (old == *i) {
242  view->get_selection()->select(j);
243  }
244  }
245 }
246 
247 void SelectInstance::on_row(const Gtk::TreePath& path, Gtk::TreeViewColumn* column) {
248  on_response(1);
249 }
250 
251 void SelectInstance::on_response(int response_id) {
252  if (response_id == 1) {
253  Gtk::TreeIter i = view->get_selection()->get_selected();
254  if (!i) {
255  return;
256  }
257  av.invoke_resolver(i->get_value(cols.name));
258  return;
259  }
260  Gtk::Main::quit();
261 }
AvahiLookupResultFlags flags
std::string get_builder_filepath(const std::string &basename) const
Definition: gx_system.h:373
Glib::ustring name
void gx_print_fatal(const char *, const std::string &)
Definition: gx_logging.cpp:177
AvahiIfIndex interface
std::string type
SelectInstance(gx_system::CmdlineOptions &options, Gtk::Window *splash)
bool invoke_resolver(const Glib::ustring &name)
bool get_address_port(Glib::ustring &address, int &port, Glib::ustring &name, Glib::ustring &host)
sigc::signal< void > & signal_changed()
AvahiProtocol protocol
std::string domain
void get_service_names(std::vector< Glib::ustring > &r)