]> git.decadent.org.uk Git - videolink.git/blobdiff - webdvd.cpp
Renamed various types to fit lower_case_with_underscores convention.
[videolink.git] / webdvd.cpp
index 6fb547ff510c0431bfddba9adfdc5272b0f60598..9a5dd04e8b078af7c6de36d07c2e36e3076a73e1 100644 (file)
@@ -14,7 +14,6 @@
 #include <string>
 
 #include <stdlib.h>
-#include <unistd.h>
 
 #include <boost/shared_ptr.hpp>
 
@@ -30,6 +29,7 @@
 #include <nsIContent.h>
 #include <nsIDocShell.h>
 #include <nsIDOMAbstractView.h>
+#include <nsIDOMBarProp.h>
 #include <nsIDOMDocumentEvent.h>
 #include <nsIDOMDocumentView.h>
 #include <nsIDOMElement.h>
 #include <nsIInterfaceRequestorUtils.h>
 #include <nsIURI.h> // required before nsILink.h
 #include <nsILink.h>
+#include <nsIPrefBranch.h>
+#include <nsIPrefService.h>
 #include <nsIPresContext.h>
 #include <nsIPresShell.h>
+#include <nsIServiceManagerUtils.h>
 #include <nsIWebBrowser.h>
 #include <nsString.h>
 
-#include "browserwidget.hpp"
-#include "childiterator.hpp"
+#include "browser_widget.hpp"
+#include "child_iterator.hpp"
 #include "dvd.hpp"
-#include "framebuffer.hpp"
-#include "linkiterator.hpp"
+#include "link_iterator.hpp"
 #include "pixbufs.hpp"
-#include "stylesheets.hpp"
+#include "style_sheets.hpp"
 #include "temp_file.hpp"
 #include "video.hpp"
+#include "x_frame_buffer.hpp"
 #include "xpcom_support.hpp"
 
 using xpcom_support::check;
@@ -123,7 +126,7 @@ namespace
        result.bottom = result.top + height;
 
        // Merge bounding boxes of all child elements
-       for (ChildIterator it = ChildIterator(elem), end; it != end; ++it)
+       for (child_iterator it = child_iterator(elem), end; it != end; ++it)
        {
            nsCOMPtr<nsIDOMNode> child_node(*it);
            PRUint16 child_type;
@@ -139,10 +142,10 @@ namespace
        return result;
     }
 
-    class WebDvdWindow : public Gtk::Window
+    class webdvd_window : public Gtk::Window
     {
     public:
-       WebDvdWindow(
+       webdvd_window(
            const video::frame_params & frame_params,
            const std::string & main_page_uri,
            const std::string & output_dir);
@@ -159,25 +162,26 @@ namespace
                           nsIDOMWindow * dom_window);
        void generate_dvd();
 
-       enum ResourceType { page_resource, video_resource };
-       typedef std::pair<ResourceType, int> ResourceEntry;
+       enum resource_type { page_resource, video_resource };
+       typedef std::pair<resource_type, int> resource_entry;
        video::frame_params frame_params_;
        std::string output_dir_;
-       BrowserWidget browser_widget_;
+       browser_widget browser_widget_;
        nsCOMPtr<nsIStyleSheet> stylesheet_;
        std::queue<std::string> page_queue_;
-       std::map<std::string, ResourceEntry> resource_map_;
+       std::map<std::string, resource_entry> resource_map_;
        std::vector<std::vector<std::string> > page_links_;
        std::vector<std::string> video_paths_;
        bool pending_window_update_;
        int pending_req_count_;
+       bool have_tweaked_page_;
        std::auto_ptr<temp_file> background_temp_;
        struct page_state;
        std::auto_ptr<page_state> page_state_;
        std::vector<boost::shared_ptr<temp_file> > page_temp_files_;
     };
 
-    WebDvdWindow::WebDvdWindow(
+    webdvd_window::webdvd_window(
        const video::frame_params & frame_params,
        const std::string & main_page_uri,
        const std::string & output_dir)
@@ -185,7 +189,8 @@ namespace
              output_dir_(output_dir),
              stylesheet_(load_css("file://" WEBDVD_LIB_DIR "/webdvd.css")),
              pending_window_update_(false),
-             pending_req_count_(0)
+             pending_req_count_(0),
+             have_tweaked_page_(false)
     {
        set_size_request(frame_params_.width, frame_params_.height);
        set_resizable(false);
@@ -193,36 +198,40 @@ namespace
        add(browser_widget_);
        browser_widget_.show();
        browser_widget_.signal_net_state().connect(
-           SigC::slot(*this, &WebDvdWindow::on_net_state_change));
+           SigC::slot(*this, &webdvd_window::on_net_state_change));
 
        add_page(main_page_uri);
        load_next_page();
     }
 
-    void WebDvdWindow::add_page(const std::string & uri)
+    void webdvd_window::add_page(const std::string & uri)
     {
        if (resource_map_.insert(
-               std::make_pair(uri, ResourceEntry(page_resource, 0)))
+               std::make_pair(uri, resource_entry(page_resource, 0)))
            .second)
        {
            page_queue_.push(uri);
        }
     }
 
-    void WebDvdWindow::add_video(const std::string & uri)
+    void webdvd_window::add_video(const std::string & uri)
     {
        if (resource_map_.insert(
-               std::make_pair(uri, ResourceEntry(video_resource,
-                                                 video_paths_.size() + 1)))
+               std::make_pair(uri, resource_entry(video_resource,
+                                                  video_paths_.size() + 1)))
            .second)
        {
            Glib::ustring hostname;
-           video_paths_.push_back(Glib::filename_from_uri(uri, hostname));
+           std::string filename(Glib::filename_from_uri(uri, hostname));
            // FIXME: Should check the hostname
+           if (!Glib::file_test(filename, Glib::FILE_TEST_IS_REGULAR))
+               throw std::runtime_error(
+                   filename + " is missing or not a regular file");
+           video_paths_.push_back(filename);
        }
     }
 
-    void WebDvdWindow::load_next_page()
+    void webdvd_window::load_next_page()
     {
        assert(!page_queue_.empty());
        const std::string & uri = page_queue_.front();
@@ -232,13 +241,51 @@ namespace
        resource_map_[uri].second = ++page_count;
        page_links_.resize(page_count);
 
-       pending_window_update_ = true;
        browser_widget_.load_uri(uri);
     }
 
-    void WebDvdWindow::on_net_state_change(const char * uri,
+    void webdvd_window::on_net_state_change(const char * uri,
                                           gint flags, guint status)
     {
+#       ifdef DEBUG_ON_NET_STATE_CHANGE
+       std::cout << "webdvd_window::on_net_state_change(";
+       if (uri)
+           std::cout << '"' << uri << '"';
+       else
+           std::cout << "NULL";
+       std::cout << ", ";
+       {
+           gint flags_left = flags;
+           static const struct {
+               gint value;
+               const char * name;
+           } flag_names[] = {
+               { GTK_MOZ_EMBED_FLAG_START, "STATE_START" },
+               { GTK_MOZ_EMBED_FLAG_REDIRECTING, "STATE_REDIRECTING" },
+               { GTK_MOZ_EMBED_FLAG_TRANSFERRING, "STATE_TRANSFERRING" },
+               { GTK_MOZ_EMBED_FLAG_NEGOTIATING, "STATE_NEGOTIATING" },
+               { GTK_MOZ_EMBED_FLAG_STOP, "STATE_STOP" },
+               { GTK_MOZ_EMBED_FLAG_IS_REQUEST, "STATE_IS_REQUEST" },
+               { GTK_MOZ_EMBED_FLAG_IS_DOCUMENT, "STATE_IS_DOCUMENT" },
+               { GTK_MOZ_EMBED_FLAG_IS_NETWORK, "STATE_IS_NETWORK" },
+               { GTK_MOZ_EMBED_FLAG_IS_WINDOW, "STATE_IS_WINDOW" }
+           };
+           for (int i = 0; i != sizeof(flag_names)/sizeof(flag_names[0]); ++i)
+           {
+               if (flags & flag_names[i].value)
+               {
+                   std::cout << flag_names[i].name;
+                   flags_left -= flag_names[i].value;
+                   if (flags_left)
+                       std::cout << " | ";
+               }
+           }
+           if (flags_left)
+               std::cout << "0x" << std::setbase(16) << flags_left;
+       }
+       std::cout << ", " << "0x" << std::setbase(16) << status << ")\n";
+#       endif // DEBUG_ON_NET_STATE_CHANGE
+
        if (flags & GTK_MOZ_EMBED_FLAG_IS_REQUEST)
        {
            if (flags & GTK_MOZ_EMBED_FLAG_START)
@@ -251,8 +298,15 @@ namespace
            }
        }
            
-       if (flags & GTK_MOZ_EMBED_FLAG_STOP
-           && flags & GTK_MOZ_EMBED_FLAG_IS_WINDOW)
+       if (flags & GTK_MOZ_EMBED_FLAG_IS_DOCUMENT
+           && flags & GTK_MOZ_EMBED_FLAG_START)
+       {
+           pending_window_update_ = true;
+           have_tweaked_page_ = false;
+       }
+
+       if (flags & GTK_MOZ_EMBED_FLAG_IS_WINDOW
+           && flags & GTK_MOZ_EMBED_FLAG_STOP)
        {
            // Check whether the load was successful, ignoring this
            // pseudo-error.
@@ -281,7 +335,7 @@ namespace
        }
     }
 
-    bool WebDvdWindow::process_page()
+    bool webdvd_window::process_page()
     {
        assert(!page_queue_.empty());
 
@@ -295,22 +349,32 @@ namespace
        nsCOMPtr<nsIDOMWindow> dom_window;
        check(browser->GetContentDOMWindow(getter_AddRefs(dom_window)));
 
-       if (output_dir_.empty())
+       // If we haven't done so already, apply the stylesheet and
+       // disable scrollbars.
+       if (!have_tweaked_page_)
        {
-           // In preview mode, just apply the stylesheet and let the
-           // user select links.
            apply_style_sheet(stylesheet_, pres_shell);
+
+           // This actually only needs to be done once.
+           nsCOMPtr<nsIDOMBarProp> dom_bar_prop;
+           check(dom_window->GetScrollbars(getter_AddRefs(dom_bar_prop)));
+           check(dom_bar_prop->SetVisible(false));
+
+           have_tweaked_page_ = true;
+
+           // Might need to wait a while for things to load or more
+           // likely for a re-layout.
+           if (pending_req_count_ > 0)
+               return true;
        }
-       else
+
+       // All further work should only be done if we're not in preview mode.
+       if (!output_dir_.empty())
        {
-           // If we haven't already started work on this page, apply
-           // the stylesheet and save a screenshot of its normal
-           // appearance.
+           // If we haven't already started work on this page, save a
+           // screenshot of its normal appearance.
            if (!page_state_.get())
-           {
-               apply_style_sheet(stylesheet_, pres_shell);
                save_screenshot();
-           }
 
            // Start or continue processing links.
            process_links(pres_shell, pres_context, dom_window);
@@ -335,7 +399,7 @@ namespace
        return true;
     }
 
-    void WebDvdWindow::save_screenshot()
+    void webdvd_window::save_screenshot()
     {
        Glib::RefPtr<Gdk::Window> window(get_window());
        assert(window);
@@ -351,7 +415,7 @@ namespace
            ->save(background_temp_->get_name(), "png");
     }
 
-    struct WebDvdWindow::page_state
+    struct webdvd_window::page_state
     {
        page_state(nsIDOMDocument * doc, int width, int height)
                : diff_pixbuf(Gdk::Pixbuf::create(
@@ -376,14 +440,14 @@ namespace
        temp_file links_temp;
 
        int link_num;
-       LinkIterator links_it, links_end;
+       link_iterator links_it, links_end;
 
        rectangle link_rect;
        bool link_changing;
        Glib::RefPtr<Gdk::Pixbuf> norm_pixbuf;
     };
 
-    void WebDvdWindow::process_links(nsIPresShell * pres_shell,
+    void webdvd_window::process_links(nsIPresShell * pres_shell,
                                     nsIPresContext * pres_context,
                                     nsIDOMWindow * dom_window)
     {
@@ -480,8 +544,11 @@ namespace
                check(uri->GetPath(path));
                // FIXME: This is a bit of a hack.  Perhaps we could decide
                // later based on the MIME type determined by Mozilla?
-               if (path.Length() > 4
-                   && std::strcmp(path.EndReading() - 4, ".vob") == 0)
+               if ((path.Length() > 4
+                    && std::strcmp(path.EndReading() - 4, ".vob") == 0)
+                   || (path.Length() > 8
+                       && std::strcmp(path.EndReading() - 8, ".voblist")
+                          == 0))
                {
                    PRBool is_file;
                    check(uri->SchemeIs("file", &is_file));
@@ -640,7 +707,7 @@ namespace
     void generate_page_dispatch(std::ostream &, int indent,
                                int first_page, int last_page);
 
-    void WebDvdWindow::generate_dvd()
+    void webdvd_window::generate_dvd()
     {
        temp_file temp("webdvd-dvdauthor-");
        temp.close();
@@ -751,7 +818,7 @@ namespace
                    " g1 = " << link_num * link_mult + page_num << ";";
 
                // Jump to appropriate resource.
-               const ResourceEntry & resource_loc =
+               const resource_entry & resource_loc =
                    resource_map_[page_links[link_num - 1]];
                if (resource_loc.first == page_resource)
                    file <<
@@ -788,10 +855,25 @@ namespace
                "    <titles>\n"
                "      <pgc>\n"
                // Record calling page/menu.
-               "        <pre> g12 = g1; </pre>\n"
+               "        <pre> g12 = g1; </pre>\n";
+
+           // Write a reference to a linked VOB file, or the contents
+           // of a linked VOB list file.
+           const std::string & video_path = video_paths_[video_num - 1];
+           if (video_path.compare(video_path.size() - 4, 4, ".vob") == 0)
+           {
                // FIXME: Should XML-escape the path
-               "        <vob file='" << video_paths_[video_num - 1]
-                << "'/>\n"
+               file << "        <vob file='" << video_path << "'/>\n";
+           }
+           else
+           {
+               assert(video_path.compare(video_path.size() - 8, 8,
+                                         ".voblist") == 0);
+               // TODO: Validate the file contents;
+               file << Glib::file_get_contents(video_path);
+           }
+
+           file <<
                // If page/menu location has not been changed during the
                // video, change the location to be the following
                // link/button when returning to it.  In any case,
@@ -896,6 +978,30 @@ namespace
                   " [--preview] menu-url [output-dir]\n");
     }
     
+    void set_browser_preferences()
+    {
+       // Disable IE-compatibility kluge that causes backgrounds to
+       // sometimes/usually be missing from snapshots.  This is only
+       // effective from Mozilla 1.8 onward.
+#      if MOZ_VERSION_MAJOR > 1                                 \
+           || (MOZ_VERSION_MAJOR == 1 && MOZ_VERSION_MINOR >= 8)
+       nsCOMPtr<nsIPrefService> pref_service;
+       static const nsCID pref_service_cid = NS_PREFSERVICE_CID;
+       check(CallGetService<nsIPrefService>(pref_service_cid,
+                                            getter_AddRefs(pref_service)));
+       nsCOMPtr<nsIPrefBranch> pref_branch;
+       check(pref_service->GetDefaultBranch("layout",
+                                            getter_AddRefs(pref_branch)));
+       check(pref_branch->SetBoolPref(
+                 "fire_onload_after_image_background_loads",
+                 true));
+#      endif
+
+       // TODO: Set display resolution?  Unfortunately Mozilla doesn't
+       // support non-square pixels (and neither do fontconfig or Xft
+       // anyway).
+    }
+
 } // namespace
 
 int main(int argc, char ** argv)
@@ -944,16 +1050,17 @@ int main(int argc, char ** argv)
            }
        }
 
-       std::auto_ptr<FrameBuffer> fb;
+       std::auto_ptr<x_frame_buffer> fb;
        if (!preview_mode)
        {
            // Spawn Xvfb and set env variables so that Xlib will use it
            // Use 8 bits each for RGB components, which should translate into
            // "enough" bits for YUV components.
-           fb.reset(new FrameBuffer(frame_params.width, frame_params.height,
-                                    3 * 8));
-           setenv("XAUTHORITY", fb->get_x_authority().c_str(), true);
-           setenv("DISPLAY", fb->get_x_display().c_str(), true);
+           fb.reset(new x_frame_buffer(frame_params.width,
+                                       frame_params.height,
+                                       3 * 8));
+           setenv("XAUTHORITY", fb->get_authority().c_str(), true);
+           setenv("DISPLAY", fb->get_display().c_str(), true);
        }
 
        // Initialise Gtk
@@ -1012,10 +1119,11 @@ int main(int argc, char ** argv)
            output_dir = argv[argi + 1];
 
        // Initialise Mozilla
-       BrowserWidget::init();
+       browser_widget::initialiser browser_init;
+       set_browser_preferences();
 
        // Run the browser/converter
-       WebDvdWindow window(frame_params, menu_url, output_dir);
+       webdvd_window window(frame_params, menu_url, output_dir);
        Gtk::Main::run(window);
     }
     catch (std::exception & e)