Skip to content
43 changes: 40 additions & 3 deletions App/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
# ***** END LICENSE BLOCK *****

set(Natron_SOURCES NatronApp_main.cpp)
if(WINDOWS)
list(APPEND Natron_SOURCES ../Natron.rc)
endif()
add_executable(Natron ${Natron_SOURCES})
if(WIN32)
target_sources(Natron PRIVATE ../Natron.rc)
endif()
if(APPLE)
set_target_properties(Natron PROPERTIES
MACOSX_BUNDLE TRUE
Expand Down Expand Up @@ -59,3 +59,40 @@ install(FILES ../Gui/Resources/Images/natronIcon256_linux.png
DESTINATION "${CMAKE_INSTALL_DATADIR}/pixmaps")
install(FILES ../Gui/Resources/Images/natronProjectIcon_linux.png
DESTINATION "${CMAKE_INSTALL_DATADIR}/pixmaps")

IF(WIN32 AND DEPLOYQT_FOUND)
SET(CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/install_deps_${CMAKE_BUILD_TYPE}.cmake)

# Find and install needed DLLs
file(GENERATE OUTPUT
"${CONFIG_FILE}" CONTENT
[[
EXECUTE_PROCESS(COMMAND del ${INSTALL_BINPATH}/*.dll)
SET(TARGET_APP $<TARGET_FILE:Natron>)
FILE(GET_RUNTIME_DEPENDENCIES
RESOLVED_DEPENDENCIES_VAR deps_resolved
UNRESOLVED_DEPENDENCIES_VAR deps_unresolved
EXECUTABLES ${TARGET_APP}
DIRECTORIES ${CMAKELIBPATH}
PRE_EXCLUDE_REGEXES "api-ms-*" "ext-ms-*"
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
)
MESSAGE(STATUS "Resolving runtime dependencies for ${TARGET_APP}")
FOREACH(dep ${deps_resolved})
FILE(INSTALL ${dep} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
MESSAGE(STATUS "Installing ${dep}")
ENDFOREACH()
FOREACH(dep ${deps_unresolved})
MESSAGE(WARNING "Runtime dependency ${dep} could not be resolved.")
ENDFOREACH()
EXECUTE_PROCESS(COMMAND ${DEPLOYQT_EXE} --no-angle --compiler-runtime ${INSTALL_BINPATH}/Natron.exe)
]])

INSTALL(CODE "SET(INSTALL_BINPATH \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}\")")
INSTALL(CODE "SET(DEPLOYQT_EXE \"${DEPLOYQT_EXE}\")")
INSTALL(CODE "SET(CMAKELIBPATH \"${CMAKE_SYSTEM_LIBRARY_PATH};${CMAKE_MINGW_SYSTEM_LIBRARY_PATH};${MINGWPATH}\")")
INSTALL(SCRIPT ${CONFIG_FILE})
INSTALL(DIRECTORY ${Python3_LIBRARY_DIRS}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}
DESTINATION lib
PATTERN "*.pyc" EXCLUDE)
ENDIF()
16 changes: 15 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ if(WIN32)
endif()
find_package(Python3 COMPONENTS Interpreter Development)

if(WIN32)
# Search qtdeploy executable
find_program(DEPLOYQT_EXE windeployqt.exe)
if(NOT ${DEPLOYQT_EXE} STREQUAL "")
set(DEPLOYQT_FOUND 1)
message(STATUS "windeployqt.exe found : ${DEPLOYQT_EXE}")
else()
set(DEPLOYQT_FOUND 0)
message(STATUS "windeployqt.exe not found, will not deploy Qt dependencies")
endif()
endif()

if(IS_DEBUG_BUILD AND WIN32)
# Explicitly setting SHIBOKEN_PYTHON_LIBRARIES variable to avoid PYTHON_DEBUG_LIBRARY-NOTFOUND
# link errors on Windows debug builds.
Expand All @@ -84,7 +96,7 @@ if(NATRON_QT6)
set(SHIBOKEN_LIB Shiboken6::libshiboken)
set(PYSIDE_LIB PySide6::pyside6)
else()
find_package(Qt5 5.15 CONFIG REQUIRED COMPONENTS ${QT_COMPONENTS})
find_package(Qt5 5.15 CONFIG REQUIRED COMPONENTS ${QT_COMPONENTS} NO_SYSTEM_ENVIRONMENT_PATH)
find_package(Shiboken2 5.15 CONFIG REQUIRED COMPONENTS libshiboken2 shiboken2)

find_package(PySide2 5.15 CONFIG REQUIRED COMPONENTS pyside2)
Expand All @@ -101,6 +113,8 @@ else()
set(PYSIDE_LIB PySide2::pyside2)
endif()

message(STATUS ">>> qt dirs ${Qt5Gui_INCLUDE_DIRS}")

if(IS_DEBUG_BUILD AND WIN32)
# Remove NDEBUG from Shiboken INTERFACE_COMPILE_DEFINITIONS so it is not inherited in debug builds.
get_property(SHIBOKEN_DEFS TARGET ${SHIBOKEN_LIB} PROPERTY INTERFACE_COMPILE_DEFINITIONS)
Expand Down
8 changes: 8 additions & 0 deletions Engine/AppInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,18 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON
{
}

virtual void loadProjectGui(bool /*isAutosave*/, boost::archive::binary_iarchive & /*archive*/) const
{
}

virtual void saveProjectGui(boost::archive::xml_oarchive & /*archive*/)
{
}

virtual void saveProjectGui(boost::archive::binary_oarchive & /*archive*/)
{
}

virtual void setupViewersForViews(const std::vector<std::string>& /*viewNames*/)
{
}
Expand Down
11 changes: 9 additions & 2 deletions Engine/AppManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,9 @@ AppManager::loadFromArgs(const CLArgs& cl)
std::cout << "argv[" << i << "] = " << StrUtils::utf16_to_utf8( std::wstring(_imp->commandLineArgsWide[i]) ) << std::endl;
}
#endif
// This should fix GL widgets when undocked
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);

// This needs to be done BEFORE creating qApp because
// on Linux, X11 will create a context that would corrupt
Expand All @@ -322,6 +325,7 @@ AppManager::loadFromArgs(const CLArgs& cl)
_imp->renderingContextPool.reset( new GPUContextPool() );
initializeOpenGLFunctionsOnce(true);


// QCoreApplication will hold a reference to that appManagerArgc integer until it dies.
// Thus ensure that the QCoreApplication is destroyed when returning this function.
initializeQApp(_imp->nArgs, &_imp->commandLineArgsUtf8.front()); // calls QCoreApplication::QCoreApplication(), which calls setlocale()
Expand Down Expand Up @@ -476,6 +480,9 @@ AppManager::~AppManager()

// After this line, everything is cleaned-up (should be) and the process may resume in the main and could in theory be able to re-create a new AppManager
_imp->_qApp.reset();

// Fast cleanup
TerminateProcess(GetCurrentProcess(), 0);
}

class QuitInstanceArgs
Expand Down Expand Up @@ -3182,7 +3189,7 @@ void
AppManager::registerUNCPath(const QString& path,
const QChar& driveLetter)
{
assert( QThread::currentThread() == qApp->thread() );
//assert( QThread::currentThread() == qApp->thread() );
_imp->uncPathMapping[driveLetter] = path;
}

Expand Down Expand Up @@ -4189,7 +4196,7 @@ NATRON_PYTHON_NAMESPACE::getFunctionArguments(const std::string& pyFunc,
#endif
std::stringstream ss;
ss << "import inspect\n";
ss << "args_spec = inspect.getargspec(" << pyFunc << ")\n";
ss << "args_spec = inspect.getfullargspec(" << pyFunc << ")\n";
std::string script = ss.str();
std::string output;
bool ok = NATRON_PYTHON_NAMESPACE::interpretPythonScript(script, error, &output);
Expand Down
2 changes: 2 additions & 0 deletions Engine/EngineFwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ namespace boost {
namespace archive {
class xml_iarchive;
class xml_oarchive;
class binary_iarchive;
class binary_oarchive;
}
namespace serialization {
class access;
Expand Down
155 changes: 113 additions & 42 deletions Engine/Project.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,6 @@ Project::loadProjectInternal(const QString & path,
}

bool ret = false;
FStreamsSupport::ifstream ifile;
FStreamsSupport::open( &ifile, filePath.toStdString() );
if (!ifile) {
throw std::runtime_error( tr("Failed to open %1").arg(filePath).toStdString() );
}

if ( (NATRON_VERSION_MAJOR == 1) && (NATRON_VERSION_MINOR == 0) && (NATRON_VERSION_REVISION == 0) ) {
///Try to determine if the project was made during Natron v1.0.0 - RC2 or RC3 to detect a bug we introduced at that time
Expand All @@ -315,21 +310,28 @@ Project::loadProjectInternal(const QString & path,
}

LoadProjectSplashScreen_RAII __raii_splashscreen__(getApp(), name);
bool xml_loaded = false;

try {
bool bgProject;
boost::archive::xml_iarchive iArchive(ifile);
{
FlagSetter __raii_loadingProjectInternal__(true, &_imp->isLoadingProjectInternal, &_imp->isLoadingProjectMutex);
// XML loading tests first, then binary loading
FStreamsSupport::ifstream ifile;
FStreamsSupport::open( &ifile, filePath.toStdString());
if (ifile) {
bool bgProject;
boost::archive::xml_iarchive iArchive(ifile);
{
FlagSetter __raii_loadingProjectInternal__(true, &_imp->isLoadingProjectInternal, &_imp->isLoadingProjectMutex);

iArchive >> boost::serialization::make_nvp("Background_project", bgProject);
ProjectSerialization projectSerializationObj( getApp() );
iArchive >> boost::serialization::make_nvp("Project", projectSerializationObj);
ret = load(projectSerializationObj, name, path, mustSave);
} // __raii_loadingProjectInternal__

iArchive >> boost::serialization::make_nvp("Background_project", bgProject);
ProjectSerialization projectSerializationObj( getApp() );
iArchive >> boost::serialization::make_nvp("Project", projectSerializationObj);
ret = load(projectSerializationObj, name, path, mustSave);
} // __raii_loadingProjectInternal__

if (!bgProject) {
getApp()->loadProjectGui(isAutoSave, iArchive);
if (!bgProject) {
getApp()->loadProjectGui(isAutoSave, iArchive);
}
xml_loaded = true;
}
} catch (const std::exception &e) {
const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo();
Expand All @@ -339,7 +341,6 @@ Project::loadProjectInternal(const QString & path,
QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME));
throw std::runtime_error(message.toStdString());
}
throw std::runtime_error( tr("Unrecognized or damaged project file:").toStdString() + ' ' + e.what());
} catch (...) {
const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo();
if (pInfo.vMajor > NATRON_VERSION_MAJOR ||
Expand All @@ -348,7 +349,48 @@ Project::loadProjectInternal(const QString & path,
QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME));
throw std::runtime_error(message.toStdString());
}
throw std::runtime_error( tr("Unrecognized or damaged project file").toStdString() );
}

if (!xml_loaded){
try {
FStreamsSupport::ifstream ifile;
FStreamsSupport::open( &ifile, filePath.toStdString(), std::ios::in | std::ios::binary );
if (!ifile) {
throw std::runtime_error( tr("Failed to open %1").arg(filePath).toStdString() );
}
bool bgProject;
boost::archive::binary_iarchive iArchive(ifile);
{
FlagSetter __raii_loadingProjectInternal__(true, &_imp->isLoadingProjectInternal, &_imp->isLoadingProjectMutex);

iArchive >> boost::serialization::make_nvp("Background_project", bgProject);
ProjectSerialization projectSerializationObj( getApp() );
iArchive >> boost::serialization::make_nvp("Project", projectSerializationObj);
ret = load(projectSerializationObj, name, path, mustSave);
} // __raii_loadingProjectInternal__

if (!bgProject) {
getApp()->loadProjectGui(isAutoSave, iArchive);
}
} catch (const std::exception &e) {
const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo();
if (pInfo.vMajor > NATRON_VERSION_MAJOR ||
(pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor > NATRON_VERSION_MINOR) ||
(pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor == NATRON_VERSION_MINOR && pInfo.vRev > NATRON_VERSION_REVISION)) {
QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME));
throw std::runtime_error(message.toStdString());
}
throw std::runtime_error( tr("Unrecognized or damaged project file:").toStdString() + ' ' + e.what());
} catch (...) {
const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo();
if (pInfo.vMajor > NATRON_VERSION_MAJOR ||
(pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor > NATRON_VERSION_MINOR) ||
(pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor == NATRON_VERSION_MINOR && pInfo.vRev > NATRON_VERSION_REVISION)) {
QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME));
throw std::runtime_error(message.toStdString());
}
throw std::runtime_error( tr("Unrecognized or damaged project file").toStdString() );
}
}

Format f;
Expand Down Expand Up @@ -599,12 +641,6 @@ Project::saveProjectInternal(const QString & path,
tmpFilename.append( QString::number( time.toMSecsSinceEpoch() ) );

{
FStreamsSupport::ofstream ofile;
FStreamsSupport::open( &ofile, tmpFilename.toStdString() );
if (!ofile) {
throw std::runtime_error( tr("Failed to open file ").toStdString() + tmpFilename.toStdString() );
}

///Fix file paths before saving.
QString oldProjectPath = QString::fromUtf8( _imp->getProjectPath().c_str() );

Expand All @@ -615,27 +651,62 @@ Project::saveProjectInternal(const QString & path,
_imp->natronVersion->setValue( generateUserFriendlyNatronVersionName() );
}

try {
boost::archive::xml_oarchive oArchive(ofile);
bool bgProject = getApp()->isBackground();
oArchive << boost::serialization::make_nvp("Background_project", bgProject);
ProjectSerialization projectSerializationObj( getApp() );
save(&projectSerializationObj);
oArchive << boost::serialization::make_nvp("Project", projectSerializationObj);
if (!bgProject) {
AppInstancePtr app = getApp();
if (app) {
app->saveProjectGui(oArchive);
if (!appPTR->getCurrentSettings()->saveAsBinary()){
try {
FStreamsSupport::ofstream ofile;
FStreamsSupport::open( &ofile, tmpFilename.toStdString(), std::ios::out);
if (!ofile) {
throw std::runtime_error( tr("Failed to open file ").toStdString() + tmpFilename.toStdString() );
}
boost::archive::xml_oarchive oArchive(ofile);
bool bgProject = getApp()->isBackground();
oArchive << boost::serialization::make_nvp("Background_project", bgProject);
ProjectSerialization projectSerializationObj( getApp() );
save(&projectSerializationObj);
oArchive << boost::serialization::make_nvp("Project", projectSerializationObj);
if (!bgProject) {
AppInstancePtr app = getApp();
if (app) {
app->saveProjectGui(oArchive);
}
}
} catch (...) {
if (!autoSave && updateProjectProperties) {
///Reset the old project path in case of failure.
_imp->autoSetProjectDirectory(oldProjectPath);
}
throw;
}
} catch (...) {
if (!autoSave && updateProjectProperties) {
///Reset the old project path in case of failure.
_imp->autoSetProjectDirectory(oldProjectPath);
}
else
{
try {
FStreamsSupport::ofstream ofile;
FStreamsSupport::open( &ofile, tmpFilename.toStdString(), std::ios::out | std::ios::binary);
if (!ofile) {
throw std::runtime_error( tr("Failed to open file ").toStdString() + tmpFilename.toStdString() );
}
boost::archive::binary_oarchive oArchive(ofile);
bool bgProject = getApp()->isBackground();
oArchive << boost::serialization::make_nvp("Background_project", bgProject);
ProjectSerialization projectSerializationObj( getApp() );
save(&projectSerializationObj);
oArchive << boost::serialization::make_nvp("Project", projectSerializationObj);
if (!bgProject) {
AppInstancePtr app = getApp();
if (app) {
app->saveProjectGui(oArchive);
}
}
} catch (...) {
if (!autoSave && updateProjectProperties) {
///Reset the old project path in case of failure.
_imp->autoSetProjectDirectory(oldProjectPath);
}
throw;
}
throw;
}
} // ofile
}

if (!autoSave) {
// rotate backups
Expand Down
Loading