sdl_viewer.cpp

Basic example using openvrml::browser.

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