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