Home | Download | Screen shots | Discussion | Documentation |
---|
openvrml::browser
.
00001 // -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4; -*- 00002 // 00003 // sdl-viewer 00004 // 00005 // Copyright 2003, 2004, 2005 Braden McDaniel 00006 // 00007 // This program is free software; you can redistribute it and/or modify 00008 // it under the terms of the GNU General Public License as published by 00009 // the Free Software Foundation; either version 2 of the License, or 00010 // (at your option) any later version. 00011 // 00012 // This program is distributed in the hope that it will be useful, 00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 // GNU General Public License for more details. 00016 // 00017 // You should have received a copy of the GNU General Public License 00018 // along with this program; if not, write to the Free Software 00019 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00020 // 00021 00022 # ifdef HAVE_CONFIG_H 00023 # include <config.h> 00024 # endif 00025 00026 # include <iostream> 00027 # include <fstream> 00028 # include <boost/algorithm/string/predicate.hpp> 00029 # include <boost/utility.hpp> 00030 # include <SDL.h> 00031 # include <openvrml/browser.h> 00032 # include <openvrml/gl/viewer.h> 00033 # ifdef _WIN32 00034 # include <windows.h> 00035 # endif 00036 00037 extern "C" Uint32 update_timer_callback(Uint32 interval, void * param); 00038 00039 namespace { 00040 00041 class browser : public openvrml::browser { 00042 public: 00043 browser(); 00044 00045 private: 00046 virtual std::auto_ptr<openvrml::resource_istream> 00047 do_get_resource(const std::string & uri); 00048 }; 00049 00050 00051 class sdl_error : public std::runtime_error { 00052 public: 00053 explicit sdl_error(const std::string & message); 00054 virtual ~sdl_error() throw (); 00055 }; 00056 00057 class sdl_viewer : public openvrml::gl::viewer { 00058 friend Uint32 update_timer_callback(Uint32 interval, void * param); 00059 00060 static const Uint32 video_mode_flags; 00061 00062 SDL_TimerID update_timer_id; 00063 bool mouse_button_down; 00064 00065 public: 00066 static const int redraw_event_code = 1; 00067 static const int update_event_code = 2; 00068 00069 explicit sdl_viewer(const std::string & title) throw (sdl_error); 00070 virtual ~sdl_viewer() throw (); 00071 00072 void run(); 00073 00074 // 00075 // Window system specific methods 00076 // 00077 virtual void post_redraw(); 00078 virtual void set_cursor(cursor_style c); 00079 virtual void swap_buffers(); 00080 virtual void set_timer(double); 00081 }; 00082 } 00083 00084 int main(int argc, char * argv[]) 00085 { 00086 using std::cerr; 00087 using std::endl; 00088 00089 # ifdef _WIN32 00090 AllocConsole(); 00091 FILE * out; 00092 freopen_s(&out, "conout$", "w", stdout); 00093 freopen_s(&out, "conout$", "w", stderr); 00094 # endif 00095 00096 if (argc < 2) { 00097 cerr << "Usage: " << argv[0] << " URL" << endl; 00098 return EXIT_FAILURE; 00099 } 00100 00101 try { 00102 using std::string; 00103 using std::vector; 00104 00105 const string url = argv[1]; 00106 00107 sdl_viewer v(url); 00108 browser b; 00109 b.viewer(&v); 00110 00111 vector<string> uri(1, url); 00112 vector<string> parameter; 00113 b.load_url(uri, parameter); 00114 00115 v.run(); 00116 } catch (std::exception & ex) { 00117 cerr << ex.what() << endl; 00118 return EXIT_FAILURE; 00119 } 00120 00121 # ifdef _WIN32 00122 fclose(out); 00123 FreeConsole(); 00124 # endif 00125 00126 return EXIT_SUCCESS; 00127 } 00128 00129 namespace { 00130 00131 browser::browser(): 00132 openvrml::browser(std::cout, std::cerr) 00133 {} 00134 00135 std::auto_ptr<openvrml::resource_istream> 00136 browser::do_get_resource(const std::string & uri) 00137 { 00138 using std::auto_ptr; 00139 using std::invalid_argument; 00140 using std::string; 00141 using openvrml::resource_istream; 00142 00143 class file_resource_istream : public resource_istream { 00144 std::string url_; 00145 std::filebuf buf_; 00146 00147 public: 00148 explicit file_resource_istream(const std::string & path): 00149 resource_istream(&this->buf_) 00150 { 00151 if (!this->buf_.open(path.c_str(), 00152 ios_base::in | ios_base::binary)) { 00153 this->setstate(ios_base::badbit); 00154 } 00155 } 00156 00157 void url(const std::string & str) throw (std::bad_alloc) 00158 { 00159 this->url_ = str; 00160 } 00161 00162 private: 00163 virtual const std::string do_url() const throw () 00164 { 00165 return this->url_; 00166 } 00167 00168 virtual const std::string do_type() const throw () 00169 { 00170 // 00171 // A real application should use OS facilities for this. This 00172 // is a crude hack because sdl-viewer uses std::filebuf in 00173 // order to remain simple and portable. 00174 // 00175 using std::find; 00176 using std::string; 00177 using boost::algorithm::iequals; 00178 using boost::next; 00179 string media_type = "application/octet-stream"; 00180 const string::const_reverse_iterator dot_pos = 00181 find(this->url_.rbegin(), this->url_.rend(), '.'); 00182 if (dot_pos == this->url_.rend() 00183 || next(dot_pos.base()) == this->url_.end()) { 00184 return media_type; 00185 } 00186 const string::const_iterator hash_pos = 00187 find(next(dot_pos.base()), this->url_.end(), '#'); 00188 const string ext(dot_pos.base(), hash_pos); 00189 if (iequals(ext, "wrl")) { 00190 media_type = openvrml::vrml_media_type; 00191 } else if (iequals(ext, "x3dv")) { 00192 media_type = openvrml::x3d_vrml_media_type; 00193 } else if (iequals(ext, "png")) { 00194 media_type = "image/png"; 00195 } else if (iequals(ext, "jpg") || iequals(ext, "jpeg")) { 00196 media_type = "image/jpeg"; 00197 } 00198 return media_type; 00199 } 00200 00201 virtual bool do_data_available() const throw () 00202 { 00203 return !!(*this); 00204 } 00205 }; 00206 00207 const string scheme = uri.substr(0, uri.find_first_of(':')); 00208 if (scheme != "file") { 00209 throw invalid_argument('\"' + scheme + "\" URI scheme not " 00210 "supported"); 00211 } 00212 00213 // 00214 // file:// 00215 // ^ 00216 // 01234567 00217 static const string::size_type authority_start_index = 7; 00218 00219 // 00220 // On Windows we want to start at the drive letter, which is after the 00221 // first slash in the path. 00222 // 00223 // We ignore the content of the authority; a smarter implementation 00224 // should confirm that it is localhost, the machine name, or zero 00225 // length. 00226 // 00227 string::size_type path_start_index = 00228 # ifdef _WIN32 00229 uri.find_first_of('/', authority_start_index) + 1; 00230 # else 00231 uri.find_first_of('/', authority_start_index); 00232 # endif 00233 string path = uri.substr(path_start_index); 00234 00235 auto_ptr<resource_istream> in(new file_resource_istream(path)); 00236 static_cast<file_resource_istream *>(in.get())->url(uri); 00237 00238 return in; 00239 } 00240 00241 sdl_error::sdl_error(const std::string & message): 00242 std::runtime_error(message) 00243 {} 00244 00245 sdl_error::~sdl_error() throw () 00246 {} 00247 00248 const Uint32 sdl_viewer::video_mode_flags(SDL_OPENGL | SDL_RESIZABLE); 00249 00250 sdl_viewer::sdl_viewer(const std::string & title) throw (sdl_error): 00251 update_timer_id(0), 00252 mouse_button_down(false) 00253 { 00254 static const size_t initial_width = 640; 00255 static const size_t initial_height = 480; 00256 00257 if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) < 0) { 00258 throw sdl_error(SDL_GetError()); 00259 } 00260 if (!SDL_SetVideoMode(initial_width, 00261 initial_height, 00262 0, 00263 sdl_viewer::video_mode_flags)) { 00264 throw sdl_error(SDL_GetError()); 00265 } 00266 this->resize(initial_width, initial_height); 00267 00268 static const char * const icon = 0; 00269 SDL_WM_SetCaption(title.c_str(), icon); 00270 } 00271 00272 sdl_viewer::~sdl_viewer() throw () 00273 { 00274 SDL_Quit(); 00275 } 00276 00277 void sdl_viewer::run() 00278 { 00279 this->update(); 00280 bool done = false; 00281 bool need_update = false; 00282 bool need_redraw = false; 00283 do { 00284 SDL_Event event; 00285 sdl_viewer::event_info viewer_event_info; 00286 while (SDL_PollEvent(&event)) { 00287 switch (event.type) { 00288 case SDL_VIDEOEXPOSE: 00289 this->post_redraw(); 00290 break; 00291 case SDL_VIDEORESIZE: 00292 SDL_SetVideoMode(event.resize.w, 00293 event.resize.h, 00294 0, 00295 sdl_viewer::video_mode_flags); 00296 this->resize(event.resize.w, event.resize.h); 00297 this->post_redraw(); 00298 break; 00299 case SDL_KEYDOWN: 00300 viewer_event_info.event = sdl_viewer::event_key_down; 00301 switch (event.key.keysym.sym) { 00302 case SDLK_HOME: 00303 viewer_event_info.what = sdl_viewer::key_home; 00304 break; 00305 case SDLK_LEFT: 00306 viewer_event_info.what = sdl_viewer::key_left; 00307 break; 00308 case SDLK_UP: 00309 viewer_event_info.what = sdl_viewer::key_up; 00310 break; 00311 case SDLK_RIGHT: 00312 viewer_event_info.what = sdl_viewer::key_right; 00313 break; 00314 case SDLK_DOWN: 00315 viewer_event_info.what = sdl_viewer::key_down; 00316 break; 00317 case SDLK_PAGEDOWN: 00318 viewer_event_info.what = sdl_viewer::key_page_down; 00319 break; 00320 case SDLK_PAGEUP: 00321 viewer_event_info.what = sdl_viewer::key_page_up; 00322 break; 00323 default: 00324 break; 00325 } 00326 this->input(&viewer_event_info); 00327 break; 00328 case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: 00329 viewer_event_info.event = event.button.state == SDL_PRESSED 00330 ? sdl_viewer::event_mouse_click 00331 : sdl_viewer::event_mouse_release; 00332 viewer_event_info.what = event.button.button - 1; 00333 viewer_event_info.x = event.button.x; 00334 viewer_event_info.y = event.button.y; 00335 this->input(&viewer_event_info); 00336 break; 00337 case SDL_MOUSEMOTION: 00338 if (!event.motion.state) { 00339 viewer_event_info.event = sdl_viewer::event_mouse_move; 00340 viewer_event_info.x = event.motion.x; 00341 viewer_event_info.y = event.motion.y; 00342 this->input(&viewer_event_info); 00343 } else { 00344 for (Uint8 button = SDL_BUTTON_LEFT; 00345 button < 4; 00346 ++button) { 00347 if (event.motion.state & SDL_BUTTON(button)) { 00348 viewer_event_info.event = 00349 sdl_viewer::event_mouse_drag; 00350 viewer_event_info.what = button - 1; 00351 viewer_event_info.x = event.motion.x; 00352 viewer_event_info.y = event.motion.y; 00353 this->input(&viewer_event_info); 00354 } 00355 } 00356 } 00357 break; 00358 case SDL_QUIT: 00359 done = true; 00360 break; 00361 case SDL_USEREVENT: 00362 switch (event.user.code) { 00363 case redraw_event_code: 00364 need_redraw = true; 00365 break; 00366 case update_event_code: 00367 need_update = true; 00368 break; 00369 } 00370 break; 00371 default: 00372 break; 00373 } 00374 } 00375 if (need_update) { 00376 this->update(); 00377 need_update = false; 00378 } 00379 if (need_redraw) { 00380 this->redraw(); 00381 need_redraw = false; 00382 } 00383 } while (!done); 00384 } 00385 00386 void sdl_viewer::post_redraw() 00387 { 00388 SDL_Event redraw_event; 00389 redraw_event.type = SDL_USEREVENT; 00390 redraw_event.user.code = sdl_viewer::redraw_event_code; 00391 redraw_event.user.data1 = 0; 00392 redraw_event.user.data2 = 0; 00393 SDL_PushEvent(&redraw_event); 00394 } 00395 00396 void sdl_viewer::set_cursor(cursor_style) 00397 {} 00398 00399 void sdl_viewer::swap_buffers() 00400 { 00401 SDL_GL_SwapBuffers(); 00402 } 00403 00404 Uint32 update_timer_callback(Uint32 /* interval */, void * const param) 00405 { 00406 sdl_viewer & v = *static_cast<sdl_viewer *>(param); 00407 SDL_RemoveTimer(v.update_timer_id); 00408 v.update_timer_id = 0; 00409 SDL_Event update_event; 00410 update_event.type = SDL_USEREVENT; 00411 update_event.user.code = sdl_viewer::update_event_code; 00412 update_event.user.data1 = 0; 00413 update_event.user.data2 = 0; 00414 SDL_PushEvent(&update_event); 00415 return 0; 00416 } 00417 00418 void sdl_viewer::set_timer(const double t) 00419 { 00420 if (!this->update_timer_id) { 00421 const Uint32 interval = Uint32(1000.0 * t + 20); // milliseconds. 00422 this->update_timer_id = 00423 SDL_AddTimer(interval, update_timer_callback, this); 00424 } 00425 } 00426 }