X-Git-Url: https://git.decadent.org.uk/gitweb/?p=videolink.git;a=blobdiff_plain;f=videolink.cpp;h=a15bbea1bfe125a2cf9c67e152935203a1e3e2a3;hp=74b8a21a067979a5317ba0abc9bd645558e4dc40;hb=HEAD;hpb=fcc6457245904ddccbeef52a3fc38c18efedf8ac diff --git a/videolink.cpp b/videolink.cpp index 74b8a21..a15bbea 100644 --- a/videolink.cpp +++ b/videolink.cpp @@ -1,4 +1,4 @@ -// Copyright 2005-6 Ben Hutchings . +// Copyright 2005-8 Ben Hutchings . // See the file "COPYING" for licence details. #include @@ -15,13 +15,21 @@ #include +#include #include #include #include #include #include -#include +#include "videolink.hpp" +#include "wchar_t_short.h" +#include +#if MOZ_VERSION_GE(1,9,0) +#include +/* For some reason no longer defines this */ +typedef nsCOMPtr nsWeakPtr; +#endif #include #include #include @@ -41,16 +49,21 @@ #include // required before nsILink.h #include #include -#include #include #include #include #include +#ifdef MOZILLA_INTERNAL_API #include +#else +#include +#endif +#include "wchar_t_default.h" #include "browser_widget.hpp" #include "child_iterator.hpp" #include "dvd.hpp" +#include "event_state_manager.hpp" #include "generate_dvd.hpp" #include "geometry.hpp" #include "link_iterator.hpp" @@ -68,14 +81,17 @@ using xpcom_support::check; namespace { - rectangle get_elem_rect(nsIDOMNSDocument * ns_doc, - nsIDOMElement * elem) +#if MOZ_VERSION_GE(2,0,-1) + rectangle get_elem_rect(nsIDocument * doc, nsIDOMElement * elem) +#else + rectangle get_elem_rect(nsIDOMNSDocument * doc, nsIDOMElement * elem) +#endif { rectangle result; // Start with this element's bounding box nsCOMPtr box; - check(ns_doc->GetBoxObjectFor(elem, getter_AddRefs(box))); + check(doc->GetBoxObjectFor(elem, getter_AddRefs(box))); int width, height; check(box->GetScreenX(&result.left)); check(box->GetScreenY(&result.top)); @@ -94,7 +110,7 @@ namespace { nsCOMPtr child_elem( do_QueryInterface(child_node)); - result |= get_elem_rect(ns_doc, child_elem); + result |= get_elem_rect(doc, child_elem); } } @@ -120,6 +136,7 @@ namespace {".vob", video_format_mpeg2_ps}, {".mpeg", video_format_mpeg2_ps}, {".mpeg2", video_format_mpeg2_ps}, + {".mpg", video_format_mpeg2_ps}, {".voblist", video_format_vob_list} }; for (std::size_t i = 0; @@ -135,15 +152,79 @@ namespace return video_format_none; } - - class videolink_window : public Gtk::Window + + class base_window : public Gtk::Window + { + public: + base_window(const video::frame_params & frame_params); + + protected: + video::frame_params frame_params_; + browser_widget browser_widget_; + }; + + base_window::base_window(const video::frame_params & frame_params) + : frame_params_(frame_params) + { + set_size_request(frame_params_.width, frame_params_.height); + set_resizable(false); + + add(browser_widget_); + browser_widget_.show(); + } + + class preview_window : public base_window { public: - videolink_window( - const video::frame_params & frame_params, - const std::string & main_page_uri, - const std::string & output_dir, - dvd_generator::mpeg_encoder encoder); + preview_window(const video::frame_params & frame_params, + const std::string & main_page_uri); + + private: + bool on_idle(); + bool on_key_press(GdkEventKey *); + + std::string main_page_uri_; + }; + + preview_window::preview_window(const video::frame_params & frame_params, + const std::string & main_page_uri) + : base_window(frame_params), + main_page_uri_(main_page_uri) + { + Glib::signal_idle().connect( + sigc::mem_fun(this, &preview_window::on_idle)); + signal_key_press_event().connect( + sigc::mem_fun(this, &preview_window::on_key_press)); + } + + bool preview_window::on_idle() + { + browser_widget_.load_uri(main_page_uri_); + return false; // don't call again + } + + bool preview_window::on_key_press(GdkEventKey * event) + { + switch (event->keyval) + { + case GDK_t: // = top menu + browser_widget_.load_uri(main_page_uri_); + return true; + case GDK_q: // = quit + Gtk::Main::quit(); + return true; + default: + return false; + } + } + + class conversion_window : public base_window + { + public: + conversion_window(const video::frame_params & frame_params, + const std::string & main_page_uri, + const std::string & output_dir, + dvd_generator::mpeg_encoder encoder); bool is_finished() const; @@ -171,13 +252,16 @@ namespace bool process_links( page_state * state, nsIDOMDocument * basic_doc, - nsIPresShell * pres_shell, - nsPresContext * pres_context, + nsIDocShell * doc_shell, nsIDOMWindow * dom_window); - video::frame_params frame_params_; std::string output_dir_; - browser_widget browser_widget_; + + enum { + state_initial, + state_processing, + state_finished + } state_; dvd_generator generator_; typedef std::map @@ -187,44 +271,35 @@ namespace std::queue page_queue_; bool pending_window_update_; int pending_req_count_; - bool have_tweaked_page_; std::auto_ptr page_state_; - - bool finished_; }; - videolink_window::videolink_window( + conversion_window::conversion_window( const video::frame_params & frame_params, const std::string & main_page_uri, const std::string & output_dir, dvd_generator::mpeg_encoder encoder) - : frame_params_(frame_params), - output_dir_(output_dir), - generator_(frame_params, encoder), - pending_window_update_(false), - pending_req_count_(0), - have_tweaked_page_(false), - finished_(false) + : base_window(frame_params), + output_dir_(output_dir), + state_(state_initial), + generator_(frame_params, encoder), + pending_window_update_(false), + pending_req_count_(0) { - set_size_request(frame_params_.width, frame_params_.height); - set_resizable(false); - - add(browser_widget_); - browser_widget_.show(); Glib::signal_idle().connect( - SigC::slot(*this, &videolink_window::on_idle)); + sigc::mem_fun(this, &conversion_window::on_idle)); browser_widget_.signal_net_state().connect( - SigC::slot(*this, &videolink_window::on_net_state_change)); + sigc::mem_fun(this, &conversion_window::on_net_state_change)); add_menu(main_page_uri); } - bool videolink_window::is_finished() const + bool conversion_window::is_finished() const { - return finished_; + return state_ == state_finished; } - dvd_generator::pgc_ref videolink_window::add_menu(const std::string & uri) + dvd_generator::pgc_ref conversion_window::add_menu(const std::string & uri) { dvd_generator::pgc_ref & pgc_ref = resource_map_[uri]; if (pgc_ref.type == dvd_generator::unknown_pgc) @@ -235,7 +310,7 @@ namespace return pgc_ref; } - dvd_generator::pgc_ref videolink_window::add_title(const std::string & uri, + dvd_generator::pgc_ref conversion_window::add_title(const std::string & uri, video_format format) { dvd_generator::pgc_ref & pgc_ref = resource_map_[uri]; @@ -274,34 +349,20 @@ namespace return pgc_ref; } - void videolink_window::load_next_page() + void conversion_window::load_next_page() { assert(!page_queue_.empty()); const std::string & uri = page_queue_.front(); - std::cout << "loading " << uri << std::endl; + std::cout << "INFO: Loading <" << uri << ">" << std::endl; browser_widget_.load_uri(uri); } - bool videolink_window::on_idle() - { - if (!output_dir_.empty()) - { - // Put pointer in the top-left so that no links appear in - // the hover state when we take a screenshot. - warp_pointer(get_window(), - -frame_params_.width, -frame_params_.height); - } - - load_next_page(); - return false; // don't call again thankyou - } - - void videolink_window::on_net_state_change(const char * uri, - gint flags, guint status) + void conversion_window::on_net_state_change(const char * uri, + gint flags, guint status) { # ifdef DEBUG_ON_NET_STATE_CHANGE - std::cout << "videolink_window::on_net_state_change("; + std::cout << "conversion_window::on_net_state_change("; if (uri) std::cout << '"' << uri << '"'; else @@ -355,7 +416,6 @@ namespace && flags & GTK_MOZ_EMBED_FLAG_START) { pending_window_update_ = true; - have_tweaked_page_ = false; } if (flags & GTK_MOZ_EMBED_FLAG_IS_WINDOW @@ -363,44 +423,16 @@ namespace { // Check whether the load was successful, ignoring this // pseudo-error. +#ifdef NS_IMAGELIB_ERROR_LOAD_ABORTED if (status != NS_IMAGELIB_ERROR_LOAD_ABORTED) +#endif check(status); pending_window_update_ = false; } - - if (!browser_is_busy()) - { - try - { - if (!process()) - { - finished_ = true; - Gtk::Main::quit(); - } - } - catch (std::exception & e) - { - std::cerr << "Fatal error"; - if (!page_queue_.empty()) - std::cerr << " while processing <" << page_queue_.front() - << ">"; - std::cerr << ": " << e.what() << "\n"; - Gtk::Main::quit(); - } - catch (Glib::Exception & e) - { - std::cerr << "Fatal error"; - if (!page_queue_.empty()) - std::cerr << " while processing <" << page_queue_.front() - << ">"; - std::cerr << ": " << e.what() << "\n"; - Gtk::Main::quit(); - } - } } - struct videolink_window::page_state + struct conversion_window::page_state { page_state(Glib::RefPtr norm_pixbuf, nsIDOMDocument * doc, int width, int height) @@ -420,88 +452,119 @@ namespace link_iterator links_it, links_end; rectangle link_rect; + std::string link_target; bool link_changing; }; - bool videolink_window::process() + bool conversion_window::on_idle() + { + if (state_ == state_initial) + { + // Put pointer in the top-left so that no links appear in + // the hover state when we take a screenshot. + warp_pointer(get_window(), + -frame_params_.width, -frame_params_.height); + + load_next_page(); + + state_ = state_processing; + } + else if (state_ == state_processing && !browser_is_busy()) + { + try + { + if (!process()) + { + state_ = state_finished; + Gtk::Main::quit(); + } + } + catch (...) + { + // Print context of exception. + if (!page_queue_.empty()) + { + std::cerr << "ERROR: While processing page <" + << page_queue_.front() << ">:\n"; + if (page_state_.get() && !page_state_->link_target.empty()) + std::cerr << "ERROR: While processing link to <" + << page_state_->link_target << ">:\n"; + } + + // Print exception message. + try + { + throw; + } + catch (std::exception & e) + { + std::cerr << "ERROR: " << e.what() << "\n"; + } + catch (Glib::Exception & e) + { + std::cerr << "ERROR: " << e.what() << "\n"; + } + catch (...) + { + std::cerr << "ERROR: Unknown exception\n"; + } + + Gtk::Main::quit(); + } + } + + // Call again if we're not done. + return state_ != state_finished; + } + + bool conversion_window::process() { assert(!page_queue_.empty()); nsCOMPtr browser(browser_widget_.get_browser()); nsCOMPtr doc_shell(do_GetInterface(browser)); assert(doc_shell); - nsCOMPtr pres_shell; - check(doc_shell->GetPresShell(getter_AddRefs(pres_shell))); - nsCOMPtr pres_context; - check(doc_shell->GetPresContext(getter_AddRefs(pres_context))); nsCOMPtr dom_window; check(browser->GetContentDOMWindow(getter_AddRefs(dom_window))); - // If we haven't done so already, disable scrollbars. - if (!have_tweaked_page_) - { - // 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; + nsCOMPtr basic_doc; + check(dom_window->GetDocument(getter_AddRefs(basic_doc))); - // Might need to wait a while for things to load or more - // likely for a re-layout. - if (browser_is_busy()) - return true; - } - - // All further work should only be done if we're not in preview mode. - if (!output_dir_.empty()) + // Start or continue processing links. + if (!page_state_.get()) + page_state_.reset( + new page_state( + get_screenshot(), + basic_doc, frame_params_.width, frame_params_.height)); + if (!process_links(page_state_.get(), basic_doc, doc_shell, dom_window)) { - nsCOMPtr basic_doc; - check(dom_window->GetDocument(getter_AddRefs(basic_doc))); - - // Start or continue processing links. - std::auto_ptr state(page_state_); - if (!state.get()) - state.reset( - new page_state( - get_screenshot(), - basic_doc, frame_params_.width, frame_params_.height)); - if (process_links( - state.get(), - basic_doc, pres_shell, pres_context, dom_window)) + // We've finished work on the links so generate the + // menu VOB. + quantise_rgba_pixbuf(page_state_->diff_pixbuf, + dvd::button_n_colours); + generator_.generate_menu_vob( + resource_map_[page_queue_.front()].index, + page_state_->norm_pixbuf, page_state_->diff_pixbuf); + + // Move on to the next page, if any, or else generate + // the DVD filesystem. + page_state_.reset(); + page_queue_.pop(); + if (!page_queue_.empty()) { - // Save iteration state for later. - page_state_ = state; + load_next_page(); } else { - // We've finished work on the links so generate the - // menu VOB. - quantise_rgba_pixbuf(state->diff_pixbuf, - dvd::button_n_colours); - generator_.generate_menu_vob( - resource_map_[page_queue_.front()].index, - state->norm_pixbuf, state->diff_pixbuf); - - // Move on to the next page, if any, or else generate - // the DVD filesystem. - page_queue_.pop(); - if (!page_queue_.empty()) - { - load_next_page(); - } - else - { - generator_.generate(output_dir_); - return false; - } + generator_.generate(output_dir_); + return false; } } return true; } - Glib::RefPtr videolink_window::get_screenshot() + Glib::RefPtr conversion_window::get_screenshot() { Glib::RefPtr window(get_window()); assert(window); @@ -513,11 +576,10 @@ namespace frame_params_.width, frame_params_.height); } - bool videolink_window::process_links( + bool conversion_window::process_links( page_state * state, nsIDOMDocument * basic_doc, - nsIPresShell * pres_shell, - nsPresContext * pres_context, + nsIDocShell * doc_shell, nsIDOMWindow * dom_window) { Glib::RefPtr window(get_window()); @@ -525,8 +587,14 @@ namespace nsCOMPtr ns_doc(do_QueryInterface(basic_doc)); assert(ns_doc); +#if MOZ_VERSION_GE(2,0,-1) + nsCOMPtr doc(do_QueryInterface(basic_doc)); + assert(doc); +#endif + nsCOMPtr pres_shell; + check(doc_shell->GetPresShell(getter_AddRefs(pres_shell))); nsCOMPtr event_state_man( - pres_context->EventStateManager()); // does not AddRef + get_event_state_manager(doc_shell)); assert(event_state_man); nsCOMPtr event_factory( do_QueryInterface(basic_doc)); @@ -549,21 +617,29 @@ namespace nsCOMPtr node(*state->links_it); // Find the link URI and separate any fragment from it. + nsCOMPtr uri_iface; +#if MOZ_VERSION_GE(2,0,-1) + nsCOMPtr content(do_QueryInterface(node)); + assert(content); + uri_iface = content->GetHrefURI(); + assert(uri_iface); +#else nsCOMPtr link(do_QueryInterface(node)); assert(link); - nsCOMPtr uri_iface; check(link->GetHrefURI(getter_AddRefs(uri_iface))); - std::string uri_and_fragment, uri, fragment; +#endif + std::string uri, fragment; { - nsCString uri_and_fragment_ns; - check(uri_iface->GetSpec(uri_and_fragment_ns)); - uri_and_fragment.assign(uri_and_fragment_ns.BeginReading(), - uri_and_fragment_ns.EndReading()); - - std::size_t hash_pos = uri_and_fragment.find('#'); - uri.assign(uri_and_fragment, 0, hash_pos); + nsCString link_target_ns; + check(uri_iface->GetSpec(link_target_ns)); + const char * str; + PRUint32 len = NS_CStringGetData(link_target_ns, &str); + state->link_target.assign(str, len); + + std::size_t hash_pos = state->link_target.find('#'); + uri.assign(state->link_target, 0, hash_pos); if (hash_pos != std::string::npos) - fragment.assign(uri_and_fragment, + fragment.assign(state->link_target, hash_pos + 1, std::string::npos); } @@ -574,13 +650,17 @@ namespace // window. nsCOMPtr elem(do_QueryInterface(node)); assert(elem); +#if MOZ_VERSION_GE(2,0,-1) + state->link_rect = get_elem_rect(doc, elem); +#else state->link_rect = get_elem_rect(ns_doc, elem); +#endif state->link_rect &= window_rect; if (state->link_rect.empty()) { - std::cerr << "Ignoring invisible link to " - << uri_and_fragment << "\n"; + std::cerr << "WARN: Ignoring invisible link to <" + << state->link_target << ">\n"; continue; } @@ -594,11 +674,8 @@ namespace PRBool is_file; check(uri_iface->SchemeIs("file", &is_file)); if (!is_file) - { - std::cerr << "Links to video must use the file:" - << " scheme\n"; - continue; - } + throw std::runtime_error( + "Link to video does not use file: scheme"); target = add_title(uri, format); target.sub_index = std::strtoul(fragment.c_str(), NULL, 10); @@ -708,7 +785,7 @@ namespace "Usage: " << command_name << " [gtk-options] [--preview]\n" " [--video-std {525|525/60|NTSC|ntsc" " | 625|625/50|PAL|pal}]\n" - " [--encoder {mjpegtools|mjpegtools-old}]\n" + " [--encoder {ffmpeg|mjpegtools}]\n" " menu-url [output-dir]\n"; } @@ -757,7 +834,7 @@ namespace void fatal_error(const std::string & message) { - std::cerr << "Fatal error: " << message << "\n"; + std::cerr << "ERROR: " << message << "\n"; Gtk::Main::quit(); } @@ -859,14 +936,9 @@ int main(int argc, char ** argv) { encoder = dvd_generator::mpeg_encoder_ffmpeg; } - else if (std::strcmp(argv[argi + 1], "mjpegtools-old") == 0) - { - encoder = dvd_generator::mpeg_encoder_mjpegtools_old; - } - else if (std::strcmp(argv[argi + 1], "mjpegtools") == 0 - || std::strcmp(argv[argi + 1], "mjpegtools-new") == 0) + else if (std::strcmp(argv[argi + 1], "mjpegtools") == 0) { - encoder = dvd_generator::mpeg_encoder_mjpegtools_new; + encoder = dvd_generator::mpeg_encoder_mjpegtools; } else { @@ -922,18 +994,26 @@ int main(int argc, char ** argv) null_prompt_service::install(); // Run the browser/converter - videolink_window window(frame_params, menu_url, output_dir, encoder); - window.show(); - window.signal_hide().connect(SigC::slot(&Gtk::Main::quit)); - Gtk::Main::run(); - - return ((preview_mode || window.is_finished()) - ? EXIT_SUCCESS - : EXIT_FAILURE); + if (preview_mode) + { + preview_window window(frame_params, menu_url); + window.show(); + window.signal_hide().connect(sigc::ptr_fun(Gtk::Main::quit)); + Gtk::Main::run(); + return EXIT_SUCCESS; + } + else + { + conversion_window window(frame_params, menu_url, output_dir, encoder); + window.show(); + window.signal_hide().connect(sigc::ptr_fun(Gtk::Main::quit)); + Gtk::Main::run(); + return window.is_finished() ? EXIT_SUCCESS : EXIT_FAILURE; + } } catch (std::exception & e) { - std::cerr << "Fatal error: " << e.what() << "\n"; + std::cerr << "ERROR: " << e.what() << "\n"; return EXIT_FAILURE; } }