From c4863e5f893d34b0eb21355cac3771e5e45fcb6f Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 18 May 2017 16:38:09 +0200 Subject: [PATCH] Added support for opening images in an already-running RT instance This is enabled by the new -R switch of rawtherapee. The idea is that you first start the "server" with `$ rawtherapee -R` and then open images on this server by using `$ rawtherapee -R /path/to/raw.file` Currently disables on OSX (there seems to be some "legacy" support for this in rtwindow, but I have no way of testing it and/or integrating it with this at the moment) --- rtgui/main-cli.cc | 2 +- rtgui/main.cc | 565 ++++++++++++++++++++++++++++------------------ 2 files changed, 346 insertions(+), 221 deletions(-) diff --git a/rtgui/main-cli.cc b/rtgui/main-cli.cc index f6df47119..679990980 100644 --- a/rtgui/main-cli.cc +++ b/rtgui/main-cli.cc @@ -59,7 +59,7 @@ Glib::ustring argv0; Glib::ustring creditsPath; Glib::ustring licensePath; Glib::ustring argv1; -bool simpleEditor; +//bool simpleEditor; //Glib::Threads::Thread* mainThread; namespace diff --git a/rtgui/main.cc b/rtgui/main.cc index a0938e4c0..2798dd12c 100644 --- a/rtgui/main.cc +++ b/rtgui/main.cc @@ -63,6 +63,7 @@ Glib::ustring argv1; Glib::ustring argv2; bool simpleEditor = false; bool gimpPlugin = false; +bool remote = false; Glib::RefPtr cssForced; Glib::RefPtr cssRT; //Glib::Threads::Thread* mainThread; @@ -89,8 +90,6 @@ Glib::ustring fname_to_utf8 (const char* fname) #endif } -} - // This recursive mutex will be used by gdk_threads_enter/leave instead of a simple mutex static Glib::Threads::RecMutex myGdkRecMutex; @@ -110,6 +109,7 @@ static void myGdkLockLeave() myGdkRecMutex.unlock(); } + /* Process line command options * Returns * 0 if process in batch has executed @@ -118,20 +118,319 @@ static void myGdkLockLeave() * -1 if there is an error in parameters * -2 if an error occurred during processing * -3 if at least one required procparam file was not found */ -int processLineParams( int argc, char **argv ); +int processLineParams( int argc, char **argv ) +{ + for( int iArg = 1; iArg < argc; iArg++) { + Glib::ustring currParam(argv[iArg]); +#if ECLIPSE_ARGS + currParam = currParam.substr(1, currParam.length()-2); +#endif + if( currParam.at(0) == '-' ) { + switch( currParam.at(1) ) { +#ifdef WIN32 + + case 'w': // This case is handled outside this function + break; +#endif + case 'v': + return 0; + +#ifndef __APPLE__ // TODO agriggio - there seems to be already some "single instance app" support for OSX in rtwindow. Disabling it here until I understand how to merge the two + case 'R': + if (!gimpPlugin) { + remote = true; + } + break; +#endif + + case 'g': + if (currParam == "-gimp") { + gimpPlugin = true; + simpleEditor = true; + remote = false; + break; + } + // no break here on purpose + + case 'h': + case '?': + default: { + Glib::ustring pparamsExt = paramFileExtension.substr(1); + std::cout << " An advanced, cross-platform program for developing raw photos." << std::endl; + std::cout << std::endl; + std::cout << " Website: http://www.rawtherapee.com/" << std::endl; + std::cout << " Documentation: http://rawpedia.rawtherapee.com/" << std::endl; + std::cout << " Forum: https://discuss.pixls.us/c/software/rawtherapee" << std::endl; + std::cout << " Code and bug reports: https://github.com/Beep6581/RawTherapee" << std::endl; + std::cout << std::endl; + std::cout << "Symbols:" << std::endl; + std::cout << " indicate parameters you can change." << std::endl; + //std::cout << " [Square brackets] mean the parameter is optional." << std::endl; + //std::cout << " The pipe symbol | indicates a choice of one or the other." << std::endl; + //std::cout << " The dash symbol - denotes a range of possible values from one to the other." << std::endl; + std::cout << std::endl; + std::cout << "Usage:" << std::endl; + std::cout << " " << Glib::path_get_basename(argv[0]) << " Start File Browser inside folder." << std::endl; + std::cout << " " << Glib::path_get_basename(argv[0]) << " Start Image Editor with file." << std::endl; + std::cout << std::endl; + std::cout << "Options:" << std::endl; +#ifdef WIN32 + std::cout << " -w Do not open the Windows console" << std::endl; +#endif + std::cout << " -v Print RawTherapee version number and exit" << std::endl; +#ifndef __APPLE__ + std::cout << " -R Raise an already running RawTherapee instance (if available)" << std::endl; +#endif + std::cout << " -h -? Display this help message" << std::endl; + return -1; + } + } + } else { + if (argv1.empty()) { + argv1 = Glib::ustring(fname_to_utf8(argv[iArg])); +#if ECLIPSE_ARGS + argv1 = argv1.substr(1, argv1.length()-2); +#endif + } else if (gimpPlugin) { + argv2 = Glib::ustring(fname_to_utf8(argv[iArg])); + break; + } + if (!gimpPlugin) { + break; + } + } + } + + return 1; +} + + +bool init_rt() +{ + if (!Options::load ()) { + return false; + } + + extProgStore->init(); + SoundManager::init(); + + if( !options.rtSettings.verbose ) { + TIFFSetWarningHandler(nullptr); // avoid annoying message boxes + } + +#ifndef WIN32 + + // Move the old path to the new one if the new does not exist + if (Glib::file_test(Glib::build_filename(options.rtdir, "cache"), Glib::FILE_TEST_IS_DIR) && !Glib::file_test(options.cacheBaseDir, Glib::FILE_TEST_IS_DIR)) { + g_rename(Glib::build_filename (options.rtdir, "cache").c_str (), options.cacheBaseDir.c_str ()); + } + +#endif + + return true; +} + + +void cleanup_rt() +{ + rtengine::cleanup(); +} + + +RTWindow *create_rt_window() +{ + Glib::ustring icon_path = Glib::build_filename(argv0, "images"); + Glib::RefPtr defaultIconTheme = Gtk::IconTheme::get_default(); + defaultIconTheme->append_search_path(icon_path); + + rtengine::setPaths(options); + MyExpander::init(); // has to stay AFTER rtengine::setPaths + + // ------- loading theme files + + Glib::RefPtr screen = Gdk::Screen::get_default(); + + if (screen) { + Gtk::Settings::get_for_screen(screen)->property_gtk_theme_name() = "Adwaita"; + Gtk::Settings::get_for_screen(screen)->property_gtk_application_prefer_dark_theme() = true; + + Glib::RefPtr regex = Glib::Regex::create(THEMEREGEXSTR, Glib::RegexCompileFlags::REGEX_CASELESS); + Glib::ustring filename = Glib::build_filename(argv0, "themes", options.theme + ".css"); + if (!regex->match(options.theme + ".css") || !Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { + options.theme = "RawTherapee-GTK"; + // We're not testing GTK_MAJOR_VERSION == 3 here, since this branch requires Gtk3 only + if (GTK_MINOR_VERSION < 20) { + options.theme = options.theme + "3-_19"; + } else { + options.theme = options.theme + "3-20_"; + } + filename = Glib::build_filename(argv0, "themes", options.theme + ".css"); + } + cssRT = Gtk::CssProvider::create(); + + try { + cssRT->load_from_path (filename); + Gtk::StyleContext::add_provider_for_screen(screen, cssRT, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } catch (Glib::Error &err) { + printf("Error: Can't load css file \"%s\"\nMessage: %s\n", filename.c_str(), err.what().c_str()); + } catch (...) { + printf("Error: Can't load css file \"%s\"\n", filename.c_str()); + } + + // Set the font face and size + if (options.fontFamily != "default") { + try { + cssForced = Gtk::CssProvider::create(); + //GTK318 +#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 20 + cssForced->load_from_data (Glib::ustring::compose("* { font-family: %1; font-size: %2px }", options.fontFamily, options.fontSize)); +#else + cssForced->load_from_data (Glib::ustring::compose("* { font-family: %1; font-size: %2pt }", options.fontFamily, options.fontSize)); +#endif + //GTK318 + Gtk::StyleContext::add_provider_for_screen(screen, cssForced, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } catch (Glib::Error &err) { + printf("Error: \"%s\"\n", err.what().c_str()); + } catch (...) { + printf("Error: Can't find the font named \"%s\"\n", options.fontFamily.c_str()); + } + } + } + +#ifndef NDEBUG + else if (!screen) { + printf("ERROR: Can't get default screen!\n"); + } + +#endif + + // ------- end loading theme files + + //gdk_threads_enter (); + RTWindow *rtWindow = new RTWindow(); + + // alerting users if the default raw and image profiles are missing + if (options.is_defProfRawMissing()) { + Gtk::MessageDialog msgd (Glib::ustring::compose(M("OPTIONS_DEFRAW_MISSING"), options.defProfRaw), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + msgd.run (); + } + + if (options.is_defProfImgMissing()) { + Gtk::MessageDialog msgd (Glib::ustring::compose(M("OPTIONS_DEFIMG_MISSING"), options.defProfImg), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + msgd.run (); + } + + return rtWindow; +} + + +class RTApplication: public Gtk::Application { +public: + RTApplication(): + Gtk::Application("com.rawtherapee.application", + Gio::APPLICATION_HANDLES_OPEN), + rtWindow(nullptr) + { + } + + ~RTApplication() + { + if (rtWindow) { + delete rtWindow; + } + cleanup_rt(); + } + +private: + bool create_window() + { + if (rtWindow) { + return true; + } + + if (!init_rt()) { + Gtk::MessageDialog msgd ("Fatal error!\nThe RT_SETTINGS and/or RT_PATH environment variables are set, but use a relative path. The path must be absolute!", true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + add_window(msgd); + msgd.run (); + return false; + } else { + rtWindow = create_rt_window(); + add_window(*rtWindow); + return true; + } + } + + // Override default signal handlers: + void on_activate() override + { + if (create_window()) { + rtWindow->present(); + } + } + + void on_open(const Gio::Application::type_vec_files& files, + const Glib::ustring& hint) override + { + if (create_window()) { + struct Data { + std::vector entries; + Glib::ustring lastfilename; + FileCatalog *filecatalog; + }; + Data *d = new Data; + d->filecatalog = rtWindow->fpanel->fileCatalog; + + for (const auto &f : files) { + Thumbnail *thm = cacheMgr->getEntry(f->get_path()); + if (thm) { + d->entries.push_back(thm); + d->lastfilename = f->get_path(); + } + } + + if (!d->entries.empty()) { + const auto doit = + [](gpointer data) -> gboolean + { + Data *d = static_cast(data); + d->filecatalog->openRequested(d->entries); + d->filecatalog->selectImage(d->lastfilename, true); + delete d; + return FALSE; + }; + gdk_threads_add_idle(doit, d); + } else { + delete d; + } + rtWindow->present(); + } + } + +private: + RTWindow *rtWindow; +}; + +} // namespace + int main(int argc, char **argv) { setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); // to set decimal point to "." + simpleEditor = false; + gimpPlugin = false; + remote = false; + argv0 = ""; + argv1 = ""; + argv2 = ""; + Glib::init(); // called by Gtk::Main, but this may be important for thread handling, so we call it ourselves now gdk_threads_set_lock_functions(G_CALLBACK(myGdkLockEnter), (G_CALLBACK(myGdkLockLeave))); gdk_threads_init(); gtk_init (&argc, &argv); // use the "--g-fatal-warnings" command line flag to make warnings fatal Gio::init (); - //mainThread = Glib::Threads::Thread::self(); #ifdef BUILD_BUNDLE char exname[512] = {0}; @@ -174,17 +473,8 @@ int main(int argc, char **argv) creditsPath = CREDITS_SEARCH_PATH; licensePath = LICENCE_SEARCH_PATH; #endif - - if (!Options::load ()) { - Gtk::Main m(&argc, &argv); - Gtk::MessageDialog msgd ("Fatal error!\nThe RT_SETTINGS and/or RT_PATH environment variables are set, but use a relative path. The path must be absolute!", true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - msgd.run (); - return -2; - } - - extProgStore->init(); - SoundManager::init(); - + + #ifdef WIN32 bool consoleOpened = false; @@ -281,24 +571,6 @@ int main(int argc, char **argv) #endif - if( !options.rtSettings.verbose ) { - TIFFSetWarningHandler(nullptr); // avoid annoying message boxes - } - -#ifndef WIN32 - - // Move the old path to the new one if the new does not exist - if (Glib::file_test(Glib::build_filename(options.rtdir, "cache"), Glib::FILE_TEST_IS_DIR) && !Glib::file_test(options.cacheBaseDir, Glib::FILE_TEST_IS_DIR)) { - g_rename(Glib::build_filename (options.rtdir, "cache").c_str (), options.cacheBaseDir.c_str ()); - } - -#endif - - if (!argv1.empty()) { - if( Glib::file_test(argv1, Glib::FILE_TEST_EXISTS) && !Glib::file_test(argv1, Glib::FILE_TEST_IS_DIR)) { - simpleEditor = true; - } - } if (gimpPlugin) { if (!Glib::file_test(argv1, Glib::FILE_TEST_EXISTS) || Glib::file_test(argv1, Glib::FILE_TEST_IS_DIR)) { printf("Error: argv1 doesn't exist\n"); @@ -308,108 +580,50 @@ int main(int argc, char **argv) printf("Error: -gimp requires two arguments\n"); return 1; } + } else if (!remote && Glib::file_test(argv1, Glib::FILE_TEST_EXISTS)) { + simpleEditor = true; } - Gtk::Main m(&argc, &argv); - - Glib::ustring icon_path = Glib::build_filename(argv0, "images"); - Glib::RefPtr defaultIconTheme = Gtk::IconTheme::get_default(); - defaultIconTheme->append_search_path(icon_path); - - rtengine::setPaths(options); - MyExpander::init(); // has to stay AFTER rtengine::setPaths - - // ------- loading theme files - - Glib::RefPtr screen = Gdk::Screen::get_default(); - - if (screen) { - Gtk::Settings::get_for_screen(screen)->property_gtk_theme_name() = "Adwaita"; - Gtk::Settings::get_for_screen(screen)->property_gtk_application_prefer_dark_theme() = true; - - Glib::RefPtr regex = Glib::Regex::create(THEMEREGEXSTR, Glib::RegexCompileFlags::REGEX_CASELESS); - Glib::ustring filename = Glib::build_filename(argv0, "themes", options.theme + ".css"); - if (!regex->match(options.theme + ".css") || !Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { - options.theme = "RawTherapee-GTK"; - // We're not testing GTK_MAJOR_VERSION == 3 here, since this branch requires Gtk3 only - if (GTK_MINOR_VERSION < 20) { - options.theme = options.theme + "3-_19"; - } else { - options.theme = options.theme + "3-20_"; - } - filename = Glib::build_filename(argv0, "themes", options.theme + ".css"); + int ret = 0; + if (remote) { + char *app_argv[2] = { nullptr, nullptr }; + app_argv[0] = const_cast(argv0.c_str()); + int app_argc = 1; + if (!argv1.empty()) { + app_argc = 2; + app_argv[1] = const_cast(argv1.c_str()); } - cssRT = Gtk::CssProvider::create(); + RTApplication app; + ret = app.run(app_argc, app_argv); + } else { + if (init_rt()) { + Gtk::Main m(&argc, &argv); + gdk_threads_enter(); + RTWindow *rtWindow = create_rt_window(); + m.run(*rtWindow); + gdk_threads_leave(); - try { - cssRT->load_from_path (filename); - Gtk::StyleContext::add_provider_for_screen(screen, cssRT, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - } catch (Glib::Error &err) { - printf("Error: Can't load css file \"%s\"\nMessage: %s\n", filename.c_str(), err.what().c_str()); - } catch (...) { - printf("Error: Can't load css file \"%s\"\n", filename.c_str()); - } - - // Set the font face and size - if (options.fontFamily != "default") { - try { - cssForced = Gtk::CssProvider::create(); - //GTK318 - #if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 20 - cssForced->load_from_data (Glib::ustring::compose("* { font-family: %1; font-size: %2px }", options.fontFamily, options.fontSize)); - #else - cssForced->load_from_data (Glib::ustring::compose("* { font-family: %1; font-size: %2pt }", options.fontFamily, options.fontSize)); - #endif - //GTK318 - Gtk::StyleContext::add_provider_for_screen(screen, cssForced, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - } catch (Glib::Error &err) { - printf("Error: \"%s\"\n", err.what().c_str()); - } catch (...) { - printf("Error: Can't find the font named \"%s\"\n", options.fontFamily.c_str()); - } - } - } - -#ifndef NDEBUG - else if (!screen) { - printf("ERROR: Can't get default screen!\n"); - } - -#endif - - // ------- end loading theme files - - gdk_threads_enter (); - RTWindow *rtWindow = new class RTWindow(); - - // alerting users if the default raw and image profiles are missing - if (options.is_defProfRawMissing()) { - Gtk::MessageDialog msgd (Glib::ustring::compose(M("OPTIONS_DEFRAW_MISSING"), options.defProfRaw), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - msgd.run (); - } - - if (options.is_defProfImgMissing()) { - Gtk::MessageDialog msgd (Glib::ustring::compose(M("OPTIONS_DEFIMG_MISSING"), options.defProfImg), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); - msgd.run (); - } - - // opening the main window - m.run(*rtWindow); - - if (gimpPlugin && rtWindow->epanel && rtWindow->epanel->isRealized()) { - SaveFormat sf; - sf.format = "tif"; - sf.tiffBits = 16; - sf.tiffUncompressed = true; - sf.saveParams = true; + if (gimpPlugin && + rtWindow->epanel && rtWindow->epanel->isRealized()) { + SaveFormat sf; + sf.format = "tif"; + sf.tiffBits = 16; + sf.tiffUncompressed = true; + sf.saveParams = true; - rtWindow->epanel->saveImmediately(argv2, sf); - } - - gdk_threads_leave (); - - delete rtWindow; - rtengine::cleanup(); + if (!rtWindow->epanel->saveImmediately(argv2, sf)) { + ret = -2; + } + } + delete rtWindow; + cleanup_rt(); + } else { + Gtk::Main m(&argc, &argv); + Gtk::MessageDialog msgd ("Fatal error!\nThe RT_SETTINGS and/or RT_PATH environment variables are set, but use a relative path. The path must be absolute!", true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + msgd.run (); + ret = -2; + } + } #ifdef WIN32 @@ -421,95 +635,6 @@ int main(int argc, char **argv) #endif - return 0; + return ret; } -void deleteProcParams(std::vector &pparams) -{ - for (unsigned int i = 0; i < pparams.size(); i++) { - pparams[i]->deleteInstance(); - delete pparams[i]; - pparams[i] = NULL; - } - - return; -} - -int processLineParams( int argc, char **argv ) -{ - - for( int iArg = 1; iArg < argc; iArg++) { - Glib::ustring currParam(argv[iArg]); -#if ECLIPSE_ARGS - currParam = currParam.substr(1, currParam.length()-2); -#endif - if( currParam.at(0) == '-' ) { - switch( currParam.at(1) ) { -#ifdef WIN32 - - case 'w': // This case is handled outside this function - break; -#endif - case 'g': - if (currParam == "-gimp") { - simpleEditor = true; - gimpPlugin = true; - break; - } - // no break here on purpose - - case 'v': - return 0; - - case 'h': - case '?': - default: { - Glib::ustring pparamsExt = paramFileExtension.substr(1); - std::cout << " An advanced, cross-platform program for developing raw photos." << std::endl; - std::cout << std::endl; - std::cout << " Website: http://www.rawtherapee.com/" << std::endl; - std::cout << " Documentation: http://rawpedia.rawtherapee.com/" << std::endl; - std::cout << " Forum: https://discuss.pixls.us/c/software/rawtherapee" << std::endl; - std::cout << " Code and bug reports: https://github.com/Beep6581/RawTherapee" << std::endl; - std::cout << std::endl; - std::cout << "Symbols:" << std::endl; - std::cout << " indicate parameters you can change." << std::endl; - //std::cout << " [Square brackets] mean the parameter is optional." << std::endl; - //std::cout << " The pipe symbol | indicates a choice of one or the other." << std::endl; - //std::cout << " The dash symbol - denotes a range of possible values from one to the other." << std::endl; - std::cout << std::endl; - std::cout << "Usage:" << std::endl; - std::cout << " " << Glib::path_get_basename(argv[0]) << " Start File Browser inside folder." << std::endl; - std::cout << " " << Glib::path_get_basename(argv[0]) << " Start Image Editor with file." << std::endl; - std::cout << std::endl; - std::cout << "Options:" << std::endl; -#ifdef WIN32 - std::cout << " -w Do not open the Windows console" << std::endl; -#endif - std::cout << " -v Print RawTherapee version number and exit" << std::endl; - std::cout << " -h -? Display this help message" << std::endl; - return -1; - } - } - } else { - if (argv1.empty()) { - argv1 = Glib::ustring(fname_to_utf8(argv[iArg])); -#if ECLIPSE_ARGS - argv1 = argv1.substr(1, argv1.length()-2); -#endif - } else if (gimpPlugin) { - argv2 = Glib::ustring(fname_to_utf8(argv[iArg])); - break; - } - if (!gimpPlugin) { - break; - } - } - } - - if( !argv1.empty() ) { - return 1; - } - - return 0; -}