Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * fuse_image_list_widget.cpp - Fuse image list widget 00004 * 00005 * Created: Mon Mar 24 21:12:56 2008 00006 * Copyright 2008 Daniel Beck 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. 00014 * 00015 * This program is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU Library General Public License for more details. 00019 * 00020 * Read the full text in the LICENSE.GPL file in the doc directory. 00021 */ 00022 00023 #include "fuse_image_list_widget.h" 00024 00025 #include <fvutils/net/fuse_message.h> 00026 #include <fvutils/net/fuse_imagelist_content.h> 00027 00028 #include <netinet/in.h> 00029 #include <cstring> 00030 #include <sstream> 00031 00032 using namespace fawkes; 00033 00034 namespace firevision { 00035 #if 0 /* just to make Emacs auto-indent happy */ 00036 } 00037 #endif 00038 00039 /** @class FuseImageListWidget <fvwidgets/fuse_image_list_widget.h> 00040 * This widget displays all available Fuse images in a tree view. It also can check 00041 * the registered host for new images, regularly. 00042 * @author Daniel Beck 00043 */ 00044 00045 /** Constructor. */ 00046 FuseImageListWidget::FuseImageListWidget() 00047 { 00048 m_chk_compression = NULL; 00049 m_chk_auto_update = NULL; 00050 00051 m_cur_client.active = false; 00052 00053 m_new_clients.clear(); 00054 m_delete_clients.clear(); 00055 00056 m_image_list = Gtk::TreeStore::create(m_image_record); 00057 00058 m_signal_get_image_list.connect( sigc::mem_fun( *this, &FuseImageListWidget::get_image_list) ); 00059 m_signal_delete_clients.connect( sigc::mem_fun( *this, &FuseImageListWidget::delete_clients) ); 00060 m_signal_update_image_l.connect( sigc::mem_fun( *this, &FuseImageListWidget::update_image_list) ); 00061 00062 #if GTK_VERSION_LT(3,0) 00063 m_popup_menu = Gtk::manage( new Gtk::Menu() ); 00064 Gtk::Menu::MenuList& menulist = m_popup_menu->items(); 00065 menulist.push_back( Gtk::Menu_Helpers::MenuElem("Update now", sigc::mem_fun( *this, &FuseImageListWidget::update_image_list) ) ); 00066 menulist.push_back( Gtk::Menu_Helpers::SeparatorElem() ); 00067 menulist.push_back( Gtk::Menu_Helpers::MenuElem("Add host manually", sigc::mem_fun( *this, &FuseImageListWidget::on_add_host_manually) ) ); 00068 #endif 00069 00070 set_image_list_trv(this); 00071 } 00072 00073 /** Destructor. */ 00074 FuseImageListWidget::~FuseImageListWidget() 00075 { 00076 FuseClient* c; 00077 m_new_clients.lock(); 00078 while (m_new_clients.size() != 0) 00079 { 00080 c = m_new_clients.front().client; 00081 m_new_clients.pop_front(); 00082 c->disconnect(); 00083 c->cancel(); 00084 c->join(); 00085 delete c; 00086 } 00087 m_new_clients.unlock(); 00088 00089 if (m_cur_client.active) 00090 { 00091 m_cur_client.active = false; 00092 m_delete_clients.push_locked(m_cur_client.client); 00093 } 00094 delete_clients(); 00095 } 00096 00097 /** Call this method when new Fountain services are discovered. 00098 * @param name the name of the service 00099 * @param host_name the host the service is running on 00100 * @param port the port the service is running on 00101 */ 00102 void 00103 FuseImageListWidget::add_fountain_service( const char* name, 00104 const char* host_name, 00105 uint32_t port ) 00106 { 00107 // check whether it's already in the tree 00108 m_img_list_mutex.lock(); 00109 Gtk::TreeModel::Children children = m_image_list->children(); 00110 for ( Gtk::TreeModel::Children::iterator iter = children.begin(); 00111 iter != children.end(); ++iter ) 00112 { 00113 Gtk::TreeModel::Row row = *iter; 00114 if ( row[m_image_record.service_name] == Glib::ustring(name) ) 00115 { 00116 m_img_list_mutex.unlock(); 00117 return; 00118 } 00119 } 00120 m_img_list_mutex.unlock(); 00121 00122 // check if there is already a waiting request for this service 00123 m_new_clients.lock(); 00124 for ( LockList<ClientData>::iterator iter = m_new_clients.begin(); 00125 iter != m_new_clients.end(); ++iter ) 00126 { 00127 if (name == iter->service_name) 00128 { 00129 m_new_clients.unlock(); 00130 return; 00131 } 00132 } 00133 m_new_clients.unlock(); 00134 00135 ClientData data; 00136 data.client = 0; 00137 data.service_name = std::string(name); 00138 data.host_name = std::string(host_name); 00139 data.port = port; 00140 data.active = false; 00141 00142 m_new_clients.push_back_locked(data); 00143 m_signal_get_image_list(); 00144 } 00145 00146 /** Call this method when a Fountain service vanishes. 00147 * @param name the name of the service 00148 */ 00149 void 00150 FuseImageListWidget::remove_fountain_service(const char* name) 00151 { 00152 m_img_list_mutex.lock(); 00153 Gtk::TreeModel::Children children = m_image_list->children(); 00154 Gtk::TreeModel::Children::iterator iter = children.begin(); 00155 while ( iter != children.end() ) 00156 { 00157 Gtk::TreeModel::Row row = *iter; 00158 if ( row[m_image_record.service_name] == Glib::ustring(name) ) 00159 { 00160 iter = m_image_list->erase(iter); 00161 m_image_list->row_deleted( m_image_list->get_path(iter) ); 00162 } 00163 else 00164 { 00165 ++iter; 00166 } 00167 } 00168 m_img_list_mutex.unlock(); 00169 } 00170 00171 /** Assign the TreeView widget to hold the list of images. 00172 * @param trv a Gtk::TreeView 00173 */ 00174 void 00175 FuseImageListWidget::set_image_list_trv(Gtk::TreeView* trv) 00176 { 00177 m_img_list_mutex.lock(); 00178 m_trv_image_list = trv; 00179 m_trv_image_list->set_model(m_image_list); 00180 m_trv_image_list->append_column("asdf", m_image_record.display_text); 00181 m_trv_image_list->set_headers_visible(false); 00182 m_trv_image_list->signal_event().connect( sigc::mem_fun(*this, &FuseImageListWidget::on_image_event) ); 00183 m_trv_image_list->signal_cursor_changed().connect( sigc::mem_fun(*this, &FuseImageListWidget::on_image_selected) ); 00184 m_img_list_mutex.unlock(); 00185 } 00186 00187 /** Assign the CheckButton to toggle the compression. 00188 * @param chk a Gtk::CheckButton 00189 */ 00190 void 00191 FuseImageListWidget::set_toggle_compression_chk(Gtk::CheckButton* chk) 00192 { 00193 m_chk_compression = chk; 00194 m_chk_compression->signal_toggled().connect( sigc::mem_fun(*this, &FuseImageListWidget::on_compression_toggled) ); 00195 } 00196 00197 /** Assign the CheckButton that enables/disables the auto update function. 00198 * @param chk a Gtk::CheckButton 00199 */ 00200 void 00201 FuseImageListWidget::set_auto_update_chk(Gtk::CheckButton* chk) 00202 { 00203 m_chk_auto_update = chk; 00204 m_chk_auto_update->signal_toggled().connect( sigc::mem_fun(*this, &FuseImageListWidget::on_auto_update_toggled) ); 00205 } 00206 00207 /** Access the Dispatcher that is signalled when a new image is selected in the list of 00208 * images. 00209 * @return reference to the Dispatcher that is activated when an image is selected in the 00210 * list of images 00211 */ 00212 Glib::Dispatcher& 00213 FuseImageListWidget::image_selected() 00214 { 00215 return m_signal_image_selected; 00216 } 00217 00218 /** Get auto-update status. 00219 * @return true if auto-update is activated 00220 */ 00221 bool 00222 FuseImageListWidget::auto_update() 00223 { 00224 return m_auto_update; 00225 } 00226 00227 /** Set the auto-update status. 00228 * @param active (de-)activate auto-update 00229 * @param interval_sec the update interval in seconds 00230 */ 00231 void 00232 FuseImageListWidget::set_auto_update(bool active, unsigned int interval_sec) 00233 { 00234 m_auto_update = active; 00235 m_interval_sec = interval_sec; 00236 00237 if (m_auto_update) 00238 { 00239 #if GLIBMM_MAJOR_VERSION > 2 || ( GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION >= 14 ) 00240 m_timeout_conn = Glib::signal_timeout().connect_seconds( sigc::mem_fun(*this, &FuseImageListWidget::on_update_timeout), 00241 m_interval_sec); 00242 #else 00243 m_timeout_conn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &FuseImageListWidget::on_update_timeout), m_interval_sec); 00244 #endif 00245 } 00246 else m_timeout_conn.disconnect(); 00247 } 00248 00249 /** Get the host name, port, and image id of the selected image. 00250 * @param host_name the host name of the selected image 00251 * @param port the port of the selected image 00252 * @param image_id the id of the selected image 00253 * @param compression true if compression shall be switched on 00254 * @return true if references could be assigned 00255 */ 00256 bool 00257 FuseImageListWidget::get_selected_image( std::string& host_name, unsigned short& port, 00258 std::string& image_id, bool& compression ) 00259 { 00260 if ( !m_trv_image_list ) 00261 { return false; } 00262 00263 m_img_list_mutex.lock(); 00264 Glib::RefPtr<Gtk::TreeSelection> selection = m_trv_image_list->get_selection(); 00265 00266 if ( selection->count_selected_rows() != 1 ) 00267 { 00268 m_img_list_mutex.unlock(); 00269 return false; 00270 } 00271 00272 Gtk::TreeModel::iterator iter = selection->get_selected(); 00273 host_name = iter->get_value(m_image_record.host_name); 00274 port = iter->get_value(m_image_record.port); 00275 image_id = iter->get_value(m_image_record.image_id); 00276 m_img_list_mutex.unlock(); 00277 00278 if (m_chk_compression) 00279 { compression = m_chk_compression->get_active(); } 00280 else 00281 { compression = false; } 00282 00283 return true; 00284 } 00285 00286 00287 bool 00288 FuseImageListWidget::on_image_event(GdkEvent *event) 00289 { 00290 GdkEventButton btn = event->button; 00291 if (btn.type == GDK_BUTTON_PRESS && btn.button == 3) { 00292 #if GTK_VERSION_LT(3,0) 00293 m_popup_menu->popup(btn.button, btn.time); 00294 #endif 00295 return true; 00296 } 00297 return false; 00298 } 00299 00300 void 00301 FuseImageListWidget::on_image_selected() 00302 { 00303 m_img_list_mutex.lock(); 00304 Glib::RefPtr<Gtk::TreeSelection> selection = m_trv_image_list->get_selection(); 00305 00306 Gtk::TreeModel::iterator iter = selection->get_selected(); 00307 Glib::ustring image_id; 00308 image_id = (*iter)[m_image_record.image_id]; 00309 m_img_list_mutex.unlock(); 00310 00311 if ((image_id != m_cur_image_id) && (image_id != "invalid")) 00312 { 00313 m_cur_image_id = image_id; 00314 m_signal_image_selected(); 00315 } 00316 } 00317 00318 void 00319 FuseImageListWidget::on_auto_update_toggled() 00320 { 00321 set_auto_update( m_chk_auto_update->get_active() ); 00322 } 00323 00324 void 00325 FuseImageListWidget::on_compression_toggled() 00326 { 00327 m_signal_image_selected(); 00328 } 00329 00330 void 00331 FuseImageListWidget::get_image_list() 00332 { 00333 if (m_cur_client.active) 00334 // communication in progress 00335 { return; } 00336 00337 m_new_clients.lock(); 00338 if (m_new_clients.size() == 0) 00339 { 00340 if (m_auto_update) 00341 { 00342 #if GLIBMM_MAJOR_VERSION > 2 || ( GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION >= 14 ) 00343 m_timeout_conn = Glib::signal_timeout().connect_seconds( sigc::mem_fun(*this, &FuseImageListWidget::on_update_timeout), 00344 m_interval_sec); 00345 #else 00346 m_timeout_conn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &FuseImageListWidget::on_update_timeout), m_interval_sec); 00347 #endif 00348 } 00349 m_new_clients.unlock(); 00350 return; 00351 } 00352 00353 m_cur_client = m_new_clients.front(); 00354 m_cur_client.active = true; 00355 m_new_clients.pop_front(); 00356 m_new_clients.unlock(); 00357 00358 try 00359 { 00360 m_cur_client.client = new FuseClient( m_cur_client.host_name.c_str(), 00361 m_cur_client.port, this ); 00362 m_cur_client.client->connect(); 00363 m_cur_client.client->start(); 00364 m_cur_client.client->enqueue(FUSE_MT_GET_IMAGE_LIST); 00365 } 00366 catch (Exception& e) 00367 { 00368 e.print_trace(); 00369 m_cur_client.client->cancel(); 00370 m_cur_client.client->join(); 00371 delete m_cur_client.client; 00372 m_cur_client.active = false; 00373 } 00374 } 00375 00376 void 00377 FuseImageListWidget::delete_clients() 00378 { 00379 FuseClient* c = 0; 00380 00381 m_delete_clients.lock(); 00382 while (m_delete_clients.size() != 0) 00383 { 00384 c = m_delete_clients.front(); 00385 m_delete_clients.pop(); 00386 00387 c->disconnect(); 00388 c->cancel(); 00389 c->join(); 00390 delete c; 00391 } 00392 m_delete_clients.unlock(); 00393 } 00394 00395 bool 00396 FuseImageListWidget::on_update_timeout() 00397 { 00398 m_signal_update_image_l(); 00399 return m_auto_update; 00400 } 00401 00402 void 00403 FuseImageListWidget::update_image_list() 00404 { 00405 m_timeout_conn.disconnect(); 00406 if (m_img_list_mutex.try_lock()) 00407 { 00408 Gtk::TreeModel::Children children = m_image_list->children(); 00409 for ( Gtk::TreeModel::Children::iterator iter = children.begin(); 00410 iter != children.end(); ++iter ) 00411 { 00412 if ( (*iter)[m_image_record.image_id] == "invalid" ) 00413 { 00414 ClientData data; 00415 data.client = 0; 00416 Glib::ustring service_name = (*iter)[m_image_record.service_name]; 00417 Glib::ustring host_name = (*iter)[m_image_record.host_name]; 00418 data.service_name = std::string( service_name.c_str() ); 00419 data.host_name = std::string( host_name.c_str() ); 00420 data.port = (*iter)[m_image_record.port]; 00421 data.active = false; 00422 00423 m_new_clients.push_back_locked(data); 00424 } 00425 } 00426 m_img_list_mutex.unlock(); 00427 } 00428 00429 m_signal_get_image_list(); 00430 } 00431 00432 void 00433 FuseImageListWidget::fuse_invalid_server_version(uint32_t local_version, 00434 uint32_t remote_version) throw() 00435 { 00436 printf("Invalid versions: local: %u remote: %u\n", local_version, remote_version); 00437 } 00438 00439 void 00440 FuseImageListWidget::fuse_connection_established () throw() 00441 { 00442 } 00443 00444 void 00445 FuseImageListWidget::fuse_connection_died() throw() 00446 { 00447 if (m_cur_client.active) 00448 { 00449 m_delete_clients.push_locked(m_cur_client.client); 00450 m_cur_client.active = false; 00451 } 00452 00453 m_signal_delete_clients(); 00454 } 00455 00456 void 00457 FuseImageListWidget::fuse_inbound_received (FuseNetworkMessage *m) throw() 00458 { 00459 switch ( m->type() ) 00460 { 00461 case FUSE_MT_IMAGE_LIST: 00462 { 00463 // check whether it's already in the tree 00464 m_img_list_mutex.lock(); 00465 Gtk::TreeModel::Children children = m_image_list->children(); 00466 Gtk::TreeModel::Children::iterator iter = children.begin(); 00467 while ( iter != children.end() ) 00468 { 00469 Gtk::TreeModel::Row row = *iter; 00470 if ( row[m_image_record.service_name] == Glib::ustring(m_cur_client.service_name) ) 00471 { 00472 iter = m_image_list->erase(iter); 00473 } 00474 else 00475 { 00476 ++iter; 00477 } 00478 } 00479 00480 try 00481 { 00482 FuseImageListContent* content = m->msgc<FuseImageListContent>(); 00483 if ( content->has_next() ) 00484 { 00485 Gtk::TreeModel::Row row = *m_image_list->append(); 00486 row[m_image_record.display_text] = Glib::ustring(m_cur_client.host_name); 00487 row[m_image_record.service_name] = Glib::ustring(m_cur_client.service_name); 00488 row[m_image_record.host_name] = Glib::ustring(m_cur_client.host_name); 00489 row[m_image_record.port] = m_cur_client.port; 00490 row[m_image_record.colorspace] = 0; 00491 row[m_image_record.image_id] = "invalid"; 00492 row[m_image_record.width] = 0; 00493 row[m_image_record.height] = 0; 00494 row[m_image_record.buffer_size] = 0; 00495 00496 Gtk::TreeModel::Path path = m_image_list->get_path(row); 00497 00498 while ( content->has_next() ) 00499 { 00500 FUSE_imageinfo_t* image_info = content->next(); 00501 char image_id[IMAGE_ID_MAX_LENGTH + 1]; 00502 image_id[IMAGE_ID_MAX_LENGTH] = '\0'; 00503 strncpy(image_id, image_info->image_id, IMAGE_ID_MAX_LENGTH); 00504 00505 Gtk::TreeModel::Row childrow = *m_image_list->append( row.children() ); 00506 childrow[m_image_record.display_text] = Glib::ustring(image_id); 00507 childrow[m_image_record.service_name] = Glib::ustring(m_cur_client.service_name); 00508 childrow[m_image_record.host_name] = Glib::ustring(m_cur_client.host_name); 00509 childrow[m_image_record.port] = m_cur_client.port; 00510 childrow[m_image_record.colorspace] = ntohl(image_info->colorspace); 00511 childrow[m_image_record.image_id] = Glib::ustring(image_id); 00512 childrow[m_image_record.width] = ntohl(image_info->width); 00513 childrow[m_image_record.height] = ntohl(image_info->height); 00514 childrow[m_image_record.buffer_size] = ntohl(image_info->buffer_size); 00515 } 00516 00517 m_trv_image_list->expand_row(path, false); 00518 } 00519 00520 delete content; 00521 } 00522 catch (Exception& e) 00523 { 00524 e.print_trace(); 00525 } 00526 00527 m_img_list_mutex.unlock(); 00528 00529 m_delete_clients.push_locked(m_cur_client.client); 00530 m_cur_client.active = false; 00531 00532 m_signal_get_image_list(); 00533 m_signal_delete_clients(); 00534 00535 break; 00536 } 00537 00538 default: 00539 printf("Unhandled message type\n"); 00540 } 00541 } 00542 00543 void 00544 FuseImageListWidget::on_add_host_manually() 00545 { 00546 Gtk::Dialog* add_host = 00547 new Gtk::Dialog("Add host manually", true); 00548 add_host->add_button(Gtk::Stock::ADD, Gtk::RESPONSE_OK); 00549 add_host->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); 00550 00551 Gtk::Table* tab = Gtk::manage( new Gtk::Table(2, 2, false) ); 00552 Gtk::Label* hlab = Gtk::manage( new Gtk::Label("Host:") ); 00553 Gtk::Label* plab = Gtk::manage( new Gtk::Label("Port:") ); 00554 Gtk::Entry* hent = Gtk::manage( new Gtk::Entry() ); 00555 Gtk::HBox* pbox = Gtk::manage( new Gtk::HBox() ); 00556 00557 #if GTK_VERSION_GE(3,0) 00558 Glib::RefPtr<Gtk::Adjustment> prange = Gtk::Adjustment::create(2208, 1, 65535); 00559 #else 00560 Gtk::Adjustment prange(2208, 1, 65535); 00561 #endif 00562 Gtk::SpinButton *pent = Gtk::manage( new Gtk::SpinButton(prange) ); 00563 00564 char * fawkes_ip = getenv("FAWKES_IP"); 00565 if (fawkes_ip) hent->set_text(std::string(fawkes_ip).append(":2208")); 00566 else hent->set_text("localhost:2208"); 00567 00568 pbox->pack_start(*pent, false, false, 0); 00569 tab->attach(*hlab, 1, 2, 1, 2); 00570 tab->attach(*plab, 1, 2, 2, 3); 00571 tab->attach(*hent, 2, 3, 1, 2); 00572 tab->attach(*pbox, 2, 3, 2, 3); 00573 00574 add_host->get_vbox()->pack_start(*tab, false, true, 0); 00575 add_host->get_vbox()->show_all_children(true); 00576 00577 if (add_host->run() == Gtk::RESPONSE_OK) { 00578 std::string name = "fountain on "; 00579 std::string host = hent->get_text(); 00580 unsigned short port = 2208; 00581 00582 Glib::ustring::size_type pos; 00583 if ((pos = host.find(':')) != Glib::ustring::npos) 00584 { 00585 Glib::ustring tmp_host = ""; 00586 unsigned int tmp_port = 1234567; //Greater than max port num (i.e. 65535) 00587 std::istringstream is(host.replace(pos, 1, " ")); 00588 is >> tmp_host; 00589 is >> tmp_port; 00590 00591 if (tmp_port != 1234567 && tmp_host.size()) 00592 { 00593 host = tmp_host; 00594 port = tmp_port; 00595 } 00596 } 00597 00598 name.append(host); 00599 add_fountain_service(name.c_str(), host.c_str(), port); 00600 } 00601 00602 add_host->hide(); 00603 delete add_host; 00604 } 00605 00606 } // end namespace firevision