sdl_viewer.cpp

Basic example using 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 }