X-Git-Url: https://git.decadent.org.uk/gitweb/?p=videolink.git;a=blobdiff_plain;f=webdvd.cpp;h=9a5dd04e8b078af7c6de36d07c2e36e3076a73e1;hp=6c6419e70afd8cf27ff686f8dcfeb5abfc9dbf22;hb=0acb5f1329d294faf42e247f8c2daf68d82150f6;hpb=bbca2d6c4eb3d42392999f58876c078074996fee diff --git a/webdvd.cpp b/webdvd.cpp index 6c6419e..9a5dd04 100644 --- a/webdvd.cpp +++ b/webdvd.cpp @@ -2,6 +2,7 @@ // See the file "COPYING" for licence details. #include +#include #include #include #include @@ -13,7 +14,6 @@ #include #include -#include #include @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -41,20 +42,23 @@ #include #include // required before nsILink.h #include +#include +#include #include #include +#include #include #include -#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; @@ -122,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 child_node(*it); PRUint16 child_type; @@ -138,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); @@ -158,25 +162,26 @@ namespace nsIDOMWindow * dom_window); void generate_dvd(); - enum ResourceType { page_resource, video_resource }; - typedef std::pair ResourceEntry; + enum resource_type { page_resource, video_resource }; + typedef std::pair resource_entry; video::frame_params frame_params_; std::string output_dir_; - BrowserWidget browser_widget_; + browser_widget browser_widget_; nsCOMPtr stylesheet_; std::queue page_queue_; - std::map resource_map_; + std::map resource_map_; std::vector > page_links_; std::vector video_paths_; bool pending_window_update_; int pending_req_count_; + bool have_tweaked_page_; std::auto_ptr background_temp_; struct page_state; std::auto_ptr page_state_; std::vector > 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) @@ -184,44 +189,49 @@ 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_default_size(frame_params_.width, frame_params_.height); + set_size_request(frame_params_.width, frame_params_.height); + set_resizable(false); + 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) { - // FIXME: Should accept some slightly different URI prefixes - // (e.g. file://localhost/) and decode any URI-escaped - // characters in the path. - assert(uri.compare(0, 8, "file:///") == 0); - video_paths_.push_back(uri.substr(7)); + Glib::ustring 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(); @@ -231,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) @@ -250,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. @@ -280,7 +335,7 @@ namespace } } - bool WebDvdWindow::process_page() + bool webdvd_window::process_page() { assert(!page_queue_.empty()); @@ -294,22 +349,32 @@ namespace nsCOMPtr 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 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); @@ -334,7 +399,7 @@ namespace return true; } - void WebDvdWindow::save_screenshot() + void webdvd_window::save_screenshot() { Glib::RefPtr window(get_window()); assert(window); @@ -350,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( @@ -375,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 norm_pixbuf; }; - void WebDvdWindow::process_links(nsIPresShell * pres_shell, + void webdvd_window::process_links(nsIPresShell * pres_shell, nsIPresContext * pres_context, nsIDOMWindow * dom_window) { @@ -479,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)); @@ -639,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(); @@ -750,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 << @@ -787,10 +855,25 @@ namespace " \n" " \n" // Record calling page/menu. - "
 g12 = g1; 
\n" + "
 g12 = g1; 
\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 - " \n" + file << " \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, @@ -895,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 pref_service; + static const nsCID pref_service_cid = NS_PREFSERVICE_CID; + check(CallGetService(pref_service_cid, + getter_AddRefs(pref_service))); + nsCOMPtr 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) @@ -903,8 +1010,10 @@ int main(int argc, char ** argv) { video::frame_params frame_params = video::pal_params; bool preview_mode = false; + std::string menu_url; + std::string output_dir; - // Do initial argument parsing. We have to do this before + // Do initial option parsing. We have to do this before // letting Gtk parse the arguments since we may need to spawn // Xvfb first. int argi = 1; @@ -941,22 +1050,23 @@ int main(int argc, char ** argv) } } - std::auto_ptr fb; + std::auto_ptr 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 Gtk::Main kit(argc, argv); - // Complete argument parsing with Gtk's options out of the way. + // Complete option parsing with Gtk's options out of the way. argi = 1; while (argi != argc) { @@ -984,18 +1094,36 @@ int main(int argc, char ** argv) break; } } - if (argc - argi != (preview_mode ? 1 : 2)) + + // Look for a starting URL or filename and (except in preview + // mode) an output directory after the options. + if (argc - argi != (preview_mode ? 1 : 2)) { print_usage(std::cerr, argv[0]); return EXIT_FAILURE; } + if (std::strstr(argv[argi], "://")) + { + // It appears to be an absolute URL, so use it as-is. + menu_url = argv[argi]; + } + else + { + // Assume it's a filename. Resolve it to an absolute URL. + std::string path(argv[argi]); + if (!Glib::path_is_absolute(path)) + path = Glib::build_filename(Glib::get_current_dir(), path); + menu_url = Glib::filename_to_uri(path); + } + if (!preview_mode) + output_dir = argv[argi + 1]; // Initialise Mozilla - BrowserWidget::init(); + browser_widget::initialiser browser_init; + set_browser_preferences(); - WebDvdWindow window(frame_params, - argv[argi], - preview_mode ? "" : argv[argi + 1]); + // Run the browser/converter + webdvd_window window(frame_params, menu_url, output_dir); Gtk::Main::run(window); } catch (std::exception & e)