Salome HOME
Updated copyright comment
[modules/gui.git] / src / Session / SALOME_Session_Server.cxx
1 // Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 #include <ArgvKeeper.hxx>
24 #include <Container_init_python.hxx> // this include must be the first one as it includes Python.h
25 #include <Basics_Utils.hxx>
26 #include <OpUtil.hxx>
27 #include <RegistryService.hxx>
28 #include <SALOME_LifeCycleCORBA.hxx>
29 #include <SALOME_ModuleCatalog_impl.hxx>
30 #include <SALOME_NamingService.hxx>
31 #include <Utils_CorbaException.hxx>
32 #include <Utils_ORB_INIT.hxx>
33 #include <Utils_SALOME_Exception.hxx>
34 #include <Utils_SINGLETON.hxx>
35 #include <utilities.h>
36 #include "KernelBasis.hxx"
37
38 #include "Session_Session_i.hxx"
39 #include "Session_ServerCheck.hxx"
40 #include "Session_ServerLauncher.hxx"
41 #include "Session_Promises.hxx"
42 #include "Session_NS_wrapper.hxx"
43
44 #include "GUI_version.h"
45 #include "Qtx.h"
46 #include "QtxMsgHandler.h"
47 #include "QtxSplash.h"
48 #include "SALOME_Event.h"
49 #ifdef USE_SALOME_STYLE
50 #include "Style_Salome.h"
51 #endif // USE_SALOME_STYLE
52 #include "SUIT_Application.h"
53 #include "SUIT_Desktop.h"
54 #include "SUIT_ExceptionHandler.h"
55 #include "SUIT_ResourceMgr.h"
56 #include "SUIT_Session.h"
57 #include "SUIT_Tools.h"
58
59 #include <SALOMEconfig.h>
60 #include CORBA_SERVER_HEADER(SALOME_Session)
61 #include CORBA_SERVER_HEADER(SALOMEDS)
62
63 #ifdef WIN32
64 #define sleep _sleep
65 #include <windows.h>
66 #include <stdio.h>
67 #include <shellapi.h>
68 #include <QSettings>
69 #include <QStyleFactory>
70 #endif
71 #include <time.h>
72 #include <memory>
73
74 #include <QApplication>
75 #include <QDir>
76 #include <QFile>
77 #include <QMutex>
78 #include <QRegExp>
79 #include <QTextStream>
80 #include <QWaitCondition>
81
82 #ifdef WIN32
83 extern "C" {
84 // For DWORD (see end of file)
85 #include "windef.h"
86 // Force high-performance graphics on dual-GPU systems
87   // http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf
88   __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
89   // https://community.amd.com/thread/169965
90   __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
91 }
92 #endif
93
94 //! CORBA server for SALOME GUI session
95 /*!
96  * SALOME_Session_Server launches a SALOME session servant.
97  * The servant registers to the Naming Service.
98  * See SALOME_Session.idl for interface specification.
99  * Main services offered by the servant are:
100  * - launch GUI
101  * - stop Session ( must be idle )
102  * - get session state
103  *
104  * Also, session server:
105  * - reads arguments,
106  * - defines list of embedded services to launch with their arguments,
107  * - defines list of standalone services to connect/wait,
108  * - waits for naming service to finish its initalization,
109  * - creates and runs a separate thread to launch/initialize all services.
110  */
111
112 namespace
113 {
114   std::string handleCorbaException(const CORBA::Exception &e)
115   {
116     std::string message;
117     CORBA::Any tmp;
118     tmp <<= e;
119     CORBA::TypeCode_var tc = tmp.type();
120     message += tc->name();
121     return message;
122   }
123
124   //! Custom handler to manage Qt messages
125   class MsgHandler : public QtxMsgHandlerCallback
126   {
127   public:
128     MsgHandler() {}
129     void qtMessage(QtMsgType type, const QMessageLogContext & /*context*/, const QString &message)
130     {
131       (void)message; // unused in debug mode
132       switch (type)
133       {
134       case QtDebugMsg:
135 #ifdef QT_DEBUG_MESSAGE
136         MESSAGE("Debug: " << qPrintable(message));
137 #endif
138         break;
139       case QtWarningMsg:
140         MESSAGE("Warning: " << qPrintable(message));
141         break;
142       case QtCriticalMsg:
143         MESSAGE("Critical: " << qPrintable(message));
144         break;
145       case QtFatalMsg:
146         MESSAGE("Fatal: " << qPrintable(message));
147         break;
148       case QtInfoMsg:
149       default:
150         MESSAGE("Information: " << qPrintable(message));
151         break;
152       }
153     }
154   };
155
156   //! Get version of SALOME GUI module
157   QString salomeVersion()
158   {
159     return GUI_VERSION_STR;
160   }
161
162   //! Custom resources manager, that allows customization of application name/version
163   //  via configuration/translation files.
164   class ResourceMgr : public SUIT_ResourceMgr
165   {
166   public:
167     ResourceMgr(const QString &appName = "SalomeApp") : SUIT_ResourceMgr(appName, "%1Config")
168     {
169       customize(); // activate customization
170       setCurrentFormat("xml");
171       setOption("translators", QString("%P_msg_%L.qm|%P_icons.qm|%P_images.qm"));
172     }
173
174     QString customName() const { return myCustomAppName; }
175     QString version() const { return myCustomAppVersion; }
176
177   private:
178     static void customize()
179     {
180       // Try to retrieve actual application name and version from translation files.
181       // We create temporary resource manager and load LightApp and SalomeApp translations.
182       // This procedure is supposed to be done only once, at first call.
183       if (myCustomAppName.isNull())
184       {
185         SUIT_ResourceMgr mgr("SalomeApp", "%1Config");
186         mgr.setCurrentFormat("xml");
187         mgr.setWorkingMode(IgnoreUserValues); // prevent reading data from user's file
188         mgr.loadLanguage("LightApp", "en");
189         mgr.loadLanguage("SalomeApp", "en");
190
191         // actual application name can be customized via APP_NAME resource key
192         myCustomAppName = QObject::tr("APP_NAME").trimmed();
193         if (myCustomAppName == "APP_NAME" || myCustomAppName.toLower() == "salome")
194           myCustomAppName = "SalomeApp"; // fallback name
195
196         // actual application name can be customized via APP_VERSION resource key
197         myCustomAppVersion = QObject::tr("APP_VERSION").trimmed();
198         if (myCustomAppVersion == "APP_VERSION")
199           myCustomAppVersion = myCustomAppName == "SalomeApp" ? salomeVersion() : ""; // fallback version
200       }
201     }
202
203   protected:
204     QString userFileName(const QString & /*appName*/, const bool forLoad) const
205     {
206       if (version().isEmpty())
207         return "";
208       return SUIT_ResourceMgr::userFileName(myCustomAppName, forLoad);
209     }
210
211     virtual long userFileId(const QString &_fname) const
212     {
213       //////////////////////////////////////////////////////////////////////////////////////////////
214       // In SALOME and SALOME-based applications the user preferences file is named as
215       // - <AppName>.xml.<AppVersion> on Windows
216       // - <AppName>rc.<AppVersion> on Linux
217       // where
218       //   * AppName is application name, defaults to SalomeApp. Can be customized in SALOME-based
219       //     applications, see ResourceMgr above for more details.
220       //   * AppVersion is application version, defaults to current version of SALOME GUI module
221       //     if AppName is not customize, otherwise empty. Can be customized in SALOME-based
222       //     applications, see ResourceMgr above for more details.
223       //
224       // Since version 6.5.0 of SALOME, user file is stored in the ~/.config/salome
225       // directory. For backward compatibility, when user preferences from nearest
226       // version of application is searched, user home directory is also looked through,
227       // with lower priority.
228       //
229       // Since version 6.6.0 of SALOME, user file name on Linux is no more prefixed by dot
230       // symbol since it is stored in the hidden ~/.config/salome directory. However, dot-prefixed
231       // files are also taken into account (with lower priority) for backward compatibility.
232       //
233       // Notes:
234       // - Currently the following format of version number is supported:
235       //   <major>[.<minor>[.<release>[<type><dev>]]]
236       //   Parts in square brackets are considered optional. Here:
237       //   * major   - major version id
238       //   * minor   - minor version id
239       //   * release - maintenance version id
240       //   * type    - dev or patch marker; it can be either one alphabetical symbol (from 'a' to 'z')
241       //               or 'rc' to point release candidate (case-insensitive)
242       //   * dev     - dev version or patch number
243       //   All numerical values must be of range [1-99].
244       //   Examples: 1.0, 6.5.0, 1.2.0a1, 3.3.3rc3 (release candidate 3), 11.0.0p1 (patch 1)
245       //
246       // - Versioning approach can be customized by implementing and using own resource manager class,
247       //   see QtxResurceMgr, SUIT_ResourceMgr classes, and ResourceMgr class above in this file.
248       //////////////////////////////////////////////////////////////////////////////////////////////
249
250       long id = -1;
251       if (!myCustomAppName.isEmpty())
252       {
253 #ifdef WIN32
254         // On Windows, user file name is something like SalomeApp.xml.6.5.0 where
255         // - SalomeApp is an application name (can be customized)
256         // - xml is a file format (xml or ini)
257         // - 6.5.0 is an application version, can include alfa/beta/rc marks, e.g. 6.5.0a3, 6.5.0rc1
258         QRegExp exp(QString("%1\\.%2\\.([a-zA-Z0-9.]+)").arg(myCustomAppName).arg(currentFormat()));
259 #else
260         // On Linux, user file name is something like SalomeApprc.6.5.0 where
261         // - SalomeApp is an application name (can be customized)
262         // - 6.5.0 is an application version, can include alfa/beta/rc marks, e.g. 6.5.0a3, 6.5.0rc1
263
264         // VSR 24/09/2012: issue 0021781: since version 6.6.0 user filename is not prepended with "."
265         // when it is stored in the ~/.config/<appname> directory;
266         // for backward compatibility we also check files prepended with "." with lower priority
267         QRegExp exp(QString("\\.?%1rc\\.([a-zA-Z0-9.]+)").arg(myCustomAppName));
268 #endif
269         QString fname = QFileInfo(_fname).fileName();
270         if (exp.exactMatch(fname))
271         {
272           long fid = Qtx::versionToId(exp.cap(1));
273           if (fid > 0)
274             id = fid;
275         }
276       }
277       return id;
278     }
279
280   private:
281     static QString myCustomAppName;
282     static QString myCustomAppVersion;
283   };
284   QString ResourceMgr::myCustomAppName;
285   QString ResourceMgr::myCustomAppVersion;
286
287   //! Custom session, to use custom resource manager class.
288   class Session : public SUIT_Session
289   {
290   public:
291     virtual SUIT_ResourceMgr *createResourceMgr(const QString &appName) const
292     {
293       return new ResourceMgr(appName);
294     }
295   };
296
297   //! Custom QApplication class, redefines notify() method, to transfer all events
298   //  via centralized exception handling mechanism.
299   class Application : public QApplication
300   {
301   public:
302     Application(int &argc, char **argv)
303         : QApplication(argc, argv),
304           myHandler(0)
305     {
306       myDebug = !Qtx::getenv("SALOME_DEBUG_EXCEPTIONS").isEmpty();
307     }
308
309     virtual bool notify(QObject *receiver, QEvent *e)
310     {
311       if (myDebug || !myHandler)
312       {
313         return QApplication::notify(receiver, e);
314       }
315       else
316       {
317         try
318         {
319           return myHandler->handle(receiver, e);
320         }
321         catch (std::exception &e)
322         {
323           std::cerr << "notify(): Caught exception : " << e.what() << std::endl;
324         }
325         catch (CORBA::Exception &e)
326         {
327           std::cerr << "notify(): Caught CORBA exception : " << handleCorbaException(e) << std::endl;
328         }
329         catch (...)
330         {
331           std::cerr << "notify(): Caught unknown exception : there's probably a bug in SALOME platform" << std::endl;
332         }
333         return false; // return false when exception is caught
334       }
335     }
336
337     SUIT_ExceptionHandler *handler() const { return myHandler; }
338     void setHandler(SUIT_ExceptionHandler *h) { myHandler = h; }
339
340   private:
341     SUIT_ExceptionHandler *myHandler;
342     bool myDebug;
343   };
344
345   //! Class which calls SALOME::Session::GetInterface() from another thread
346   //  to avoid mutual lock if called from the same thread as main()
347   class GetInterfaceThread : public QThread
348   {
349   public:
350     GetInterfaceThread(SALOME::Session_var s) : session(s)
351     {
352       start();
353     }
354
355   protected:
356     virtual void run()
357     {
358       if (!CORBA::is_nil(session))
359         session->GetInterface();
360       else
361         std::cerr << "FATAL ERROR: SALOME::Session object is nil! Cannot display GUI" << std::endl;
362     }
363
364   private:
365     SALOME::Session_var session;
366   };
367
368   //! Checks command line for presense of given option(s).
369   //  Option that results to \c true is specified via \a trueOption parameter.
370   //  Option that results to \c false is specified via \a falseOption parameter (empty for default).
371   //  Default value for the result (returned if both \a trueOption \a falseOption are not given) is specified via \c defValue parameter.
372   bool boolCmdOption(const QString trueOption, const QString falseOption = QString(), bool defValue = false)
373   {
374     bool value = defValue;
375
376     QStringList args = QApplication::arguments();
377     foreach (QString arg, args)
378     {
379       if (arg == trueOption)
380         value = true;
381       else if (arg == falseOption)
382         value = false;
383     }
384     return value;
385   }
386
387   // Shutdown standalone servers
388   void shutdownServers(SALOME_NamingService *theNS, bool remoteLauncher)
389   {
390     SALOME_LifeCycleCORBA lcc(theNS);
391     lcc.shutdownServers(!remoteLauncher);
392   }
393 } // end of anonymous namespace
394
395 template<class GUI_APP_STYLE>
396 int AbstractGUIAppMain(int argc, char **argv);
397
398 class GUIAppOldStyle
399 {
400 public:
401   static constexpr bool SSL_MODE = false;
402   using NamingServiceImplementation = OldStyleNS;
403   void connectToNSIfNeeded(CORBA::ORB_ptr orb) { _NS.reset(new SALOME_NamingService(orb)); }
404   void shutdownCORBAStufIfNeeded(bool shutdownAll, CORBA::ORB_ptr orb);
405   void killOtherServersIfNeeded() { SALOME_LifeCycleCORBA::killOmniNames(); }
406   SALOME::Session_var getSession();
407   void shutdownRemoteServersIfNeeded(bool remoteLauncher);
408 private:
409   std::unique_ptr<SALOME_NamingService> _NS;
410 };
411
412 class GUIAppNewStyle
413 {
414 public:
415   static constexpr bool SSL_MODE = true;
416   using NamingServiceImplementation = NewStyleNS;
417   void connectToNSIfNeeded(CORBA::ORB_ptr orb) { /*! nothing */ }
418   void shutdownCORBAStufIfNeeded(bool shutdownAll, CORBA::ORB_ptr orb) { /*! nothing */ }
419   void killOtherServersIfNeeded() { /*! nothing */ }
420   SALOME::Session_var getSession();
421   void shutdownRemoteServersIfNeeded(bool remoteLauncher) { /*! nothing */ }
422 };
423
424 void GUIAppOldStyle::shutdownCORBAStufIfNeeded(bool shutdownAll, CORBA::ORB_ptr orb)
425 {
426   try
427   {
428     orb->shutdown(0);
429   }
430   catch (...)
431   {
432     //////////////////////////////////////////////////////////////
433     // VSR: silently skip exception:
434     // CORBA.BAD_INV_ORDER.BAD_INV_ORDER_ORBHasShutdown
435     // exception is raised when orb->destroy() is called and
436     // cpp continer is launched in the embedded mode
437     //////////////////////////////////////////////////////////////
438     if (shutdownAll)
439       SALOME_LifeCycleCORBA::killOmniNames();
440     abort(); //abort program to avoid deadlock in destructors or atexit when shutdown has been interrupted
441   }
442   // Destroy ORB
443   sleep(2);
444   ORB_INIT *init = SINGLETON_<ORB_INIT>::Instance();
445   if (init)
446     init->explicit_destroy();
447 }
448
449 SALOME::Session_var GUIAppOldStyle::getSession()
450 {
451   CORBA::Object_var obj = _NS->Resolve("/Kernel/Session");
452   SALOME::Session_var session = SALOME::Session::_narrow(obj);
453   return session;
454 }
455 void GUIAppOldStyle::shutdownRemoteServersIfNeeded(bool remoteLauncher)
456 {
457   shutdownServers(_NS.get(), remoteLauncher);
458 }
459
460 SALOME::Session_var GUIAppNewStyle::getSession()
461 {
462   SALOME::Session_var session = GetSessionRefSingleton()->get_future().get();
463   return session;
464 }
465
466 // ---------------------------- MAIN -----------------------
467 template<class GUI_APP_STYLE>
468 int AbstractGUIAppMain(int argc, char **argv)
469 {
470   using NamingServiceImplementation = typename GUI_APP_STYLE::NamingServiceImplementation;
471   setSSLMode(GUI_APP_STYLE::SSL_MODE);
472   setGUIMode(true);
473   GUI_APP_STYLE self;
474   // Set-up application settings configuration (as for QSettings)
475   // Note: these are default settings which can be customized (see below)
476   QApplication::setOrganizationName("salome");
477   QApplication::setApplicationName("salome");
478   QApplication::setApplicationVersion(salomeVersion());
479
480   // Install Qt debug messages handler
481   MsgHandler msgHandler;
482   qInstallMessageHandler(QtxMsgHandler);
483
484   // Add <qtdir>/plugins dir to the pluins search path for image plugins
485   QString qtdir = Qtx::qtDir("plugins");
486   if (!qtdir.isEmpty())
487     QApplication::addLibraryPath(qtdir);
488
489   // Add application library path (to search style plugin etc...)
490   QString path = SUIT_Tools::addSlash(Qtx::getenv("GUI_ROOT_DIR")) + "bin/salome";
491   QApplication::addLibraryPath(QDir::toNativeSeparators(path));
492
493   // QSurfaceFormat should be set before creation of QApplication,
494   // so to avoid conflicts beetween SALOME and ParaView QSurfaceFormats we should merge theirs formats
495   // (see void Qtx::initDefaultSurfaceFormat()) and set the resultant format here.
496   Qtx::initDefaultSurfaceFormat();
497
498   // Create Qt application instance: this should be done as early as possible!
499   // Note: QApplication forces setting locale LC_ALL to system one: setlocale(LC_ALL, "").
500   SetArgcArgv(argc,argv);
501   Application app(argc, argv);
502
503 #ifdef WIN32
504     QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
505                     QSettings::NativeFormat);
506     if(settings.value("AppsUseLightTheme")==0){
507         app.setStyle(QStyleFactory::create("Fusion"));
508         QPalette darkPalette;
509         QColor darkColor = QColor(45,45,45);
510         QColor disabledColor = QColor(127,127,127);
511         darkPalette.setColor(QPalette::Window, darkColor);
512         darkPalette.setColor(QPalette::WindowText, Qt::white);
513         darkPalette.setColor(QPalette::Base, QColor(18,18,18));
514         darkPalette.setColor(QPalette::AlternateBase, darkColor);
515         darkPalette.setColor(QPalette::ToolTipBase, Qt::white);
516         darkPalette.setColor(QPalette::ToolTipText, Qt::white);
517         darkPalette.setColor(QPalette::Text, Qt::white);
518         darkPalette.setColor(QPalette::Disabled, QPalette::Text, disabledColor);
519         darkPalette.setColor(QPalette::Button, darkColor);
520         darkPalette.setColor(QPalette::ButtonText, Qt::white);
521         darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, disabledColor);
522         darkPalette.setColor(QPalette::BrightText, Qt::red);
523         darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
524         darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218));
525         darkPalette.setColor(QPalette::HighlightedText, Qt::black);
526         darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, disabledColor);
527         darkPalette.setColor(QPalette::PlaceholderText, Qt::white);
528         darkPalette.setColor(QPalette::NoRole, Qt::white);
529
530         app.setPalette(darkPalette);
531         app.setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
532     }
533 #endif
534   // Initialize Python (only once)
535   // Note: Python forces setting locale LC_CTYPE to system one: setlocale(LC_CTYPE, "").
536   char *py_argv[] = {(char *)""};
537   KERNEL_PYTHON::init_python(1, py_argv);
538
539   // Create auxiliary resource manager to access application settings
540   ResourceMgr resMgr;
541   resMgr.setWorkingMode(ResourceMgr::IgnoreUserValues);
542   resMgr.loadLanguage("LightApp", "en");
543   resMgr.loadLanguage("SalomeApp", "en");
544   resMgr.loadLanguage("Session");
545
546   // Set-up application settings configuration possible customized via resources
547   if (resMgr.customName() != "SalomeApp")
548   {
549     QApplication::setApplicationName(resMgr.customName());
550     QApplication::setApplicationVersion(resMgr.version());
551   }
552
553   // Force default "C" locale if requested via user's preferences
554   // Note: this does not change whole application locale (changed via setlocale() function),
555   // but only affects GUI behavior
556   resMgr.setWorkingMode(ResourceMgr::AllowUserValues); // we must take into account user preferences
557   if (resMgr.booleanValue("language", "locale", true))
558     QLocale::setDefault(QLocale::c());
559   resMgr.setWorkingMode(ResourceMgr::IgnoreUserValues);
560
561 #if defined(GLOBAL_DOUBLE_CONVERSION)
562   // VSR 30/03/2021: moved here from QtxDoubleSpinBox/QtxIntSpinBox because of crash on Qt 5.12.
563   // Disable thousands separator for spin box
564   // see issue 14540 (old id 21219)
565   QLocale locale;
566   locale.setNumberOptions(locale.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
567   QLocale::setDefault(locale);
568 #endif
569
570   bool isGUI = boolCmdOption("--show-desktop", "--hide-desktop", true);  // true by default
571   bool isSplash = boolCmdOption("--show-splash", "--hide-splash", true); // true by default
572
573   // Show splash screen (only if both the "GUI" and "SPLASH" options are true)
574   QtxSplash *splash = 0;
575   if (isGUI && isSplash)
576   {
577     splash = QtxSplash::splash(QPixmap());
578     splash->readSettings(&resMgr);
579     if (splash->pixmap().isNull())
580       splash->setPixmap(resMgr.loadPixmap("LightApp", QObject::tr("ABOUT_SPLASH")));
581     if (splash->pixmap().isNull())
582     {
583       delete splash;
584       splash = 0;
585     }
586     else
587     {
588       splash->setOption("%A", QObject::tr("APP_NAME"));
589       splash->setOption("%V", QObject::tr("ABOUT_VERSION").arg(resMgr.version()));
590       splash->setOption("%L", QObject::tr("ABOUT_LICENSE"));
591       splash->setOption("%C", QObject::tr("ABOUT_COPYRIGHT"));
592       splash->show();
593       QApplication::instance()->processEvents();
594     }
595   }
596
597   // Initialization
598   int result = -1;
599
600   CORBA::ORB_var orb;
601   PortableServer::POA_var poa;
602
603   SUIT_Session *aGUISession = 0;
604   GetInterfaceThread *guiThread = 0;
605   Session_ServerLauncher<NamingServiceImplementation> *myServerLauncher = nullptr;
606
607 #if defined(WIN32) && defined(UNICODE)
608   char **new_argv = NULL;
609 #endif
610
611   bool remoteLauncher = false;
612
613   try
614   {
615     // ...create ORB, get RootPOA object, NamingService, etc.
616     int orbArgc = 1;
617     if (std::string(argv[1]).find("-ORBInitRef") != std::string::npos)
618     {
619       orbArgc = 3;
620       remoteLauncher = true;
621     }
622     SetArgcArgv(orbArgc, argv);
623     orb = KERNEL::GetRefToORB();
624
625     CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
626     poa = PortableServer::POA::_narrow(obj);
627
628     PortableServer::POAManager_var pman = poa->the_POAManager();
629     pman->activate();
630     MESSAGE("POA manager activated");
631
632     self.connectToNSIfNeeded(orb);
633
634     result = 0;
635   }
636   catch (SALOME_Exception &e)
637   {
638     INFOS("run(): Caught SALOME_Exception : " << e.what());
639   }
640   catch (CORBA::SystemException &e)
641   {
642     INFOS("run(): Caught CORBA::SystemException : " << handleCorbaException(e));
643   }
644   catch (CORBA::Exception &e)
645   {
646     INFOS("run(): Caught CORBA::Exception : " << handleCorbaException(e));
647   }
648   catch (std::exception &e)
649   {
650     INFOS("run(): Caught exception : " << e.what());
651   }
652   catch (...)
653   {
654     INFOS("run(): Caught unknown exception");
655   }
656
657   QMutex _GUIMutex, _SessionMutex, _SplashMutex;
658   QWaitCondition _ServerLaunch, _SessionStarted, _SplashStarted;
659
660   // lock session mutex to ensure that GetInterface is not called
661   // until all initialization is done
662   _SessionMutex.lock();
663
664   if (!result)
665   {
666     // Start embedded servers launcher (Registry, SALOMEDS, etc.)
667     // ...lock mutex to block embedded servers launching thread until wait( mutex )
668     _GUIMutex.lock();
669     // ...create launcher
670 #if defined(WIN32) && defined(UNICODE)
671     LPWSTR *szArglist = NULL;
672     int nArgs;
673     int i;
674     szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
675     new_argv = new char *[nArgs];
676     for (i = 0; i < nArgs; i++)
677     {
678       new_argv[i] = (char *)Kernel_Utils::utf8_encode(szArglist[i]);
679     }
680     // Free memory allocated for CommandLineToArgvW arguments.
681     LocalFree(szArglist);
682     myServerLauncher = new Session_ServerLauncher<NamingServiceImplementation>(nArgs, new_argv, orb, poa, &_GUIMutex, &_ServerLaunch, &_SessionMutex, &_SessionStarted);
683 #else
684     myServerLauncher = new Session_ServerLauncher<NamingServiceImplementation>(argc, argv, orb, poa, &_GUIMutex, &_ServerLaunch, &_SessionMutex, &_SessionStarted);
685 #endif
686     // ...block this thread until launcher is ready
687     _ServerLaunch.wait(&_GUIMutex);
688
689     // Start servers check thread (splash)
690     if (splash)
691     {
692       // ...lock mutex to block splash thread until wait( mutex )
693       _SplashMutex.lock();
694       // ...create servers checking thread
695       Session_ServerCheck<NamingServiceImplementation> sc(&_SplashMutex, &_SplashStarted);
696       // ... set initial progress
697       splash->setProgress(0, sc.totalSteps());
698       // start check loop
699       while (true)
700       {
701         int step = sc.currentStep();
702         int total = sc.totalSteps();
703         QString msg = sc.currentMessage();
704         QString err = sc.error();
705         if (!err.isEmpty())
706         {
707           QtxSplash::setError(err);
708           QApplication::instance()->processEvents();
709           result = -1;
710           break;
711         }
712         QtxSplash::setStatus(msg, step);
713         QApplication::instance()->processEvents();
714         if (step >= total)
715           break;
716         // ...block this thread until servers checking is finished
717         _SplashStarted.wait(&_SplashMutex);
718       }
719       // ...unlock mutex 'cause it is no more needed
720       _SplashMutex.unlock();
721     }
722
723     // Finalize embedded servers launcher
724     // ...block this thread until launcher is finished
725     _ServerLaunch.wait(&_GUIMutex);
726     // ...unlock mutex 'cause it is no more needed
727     _GUIMutex.unlock();
728   }
729
730   // Obtain Session interface reference
731   SALOME::Session_var session = self.getSession();
732
733   bool shutdownAll = false;
734   bool shutdownSession = false;
735   bool debugExceptions = boolCmdOption("--no-exception-handler") ||
736                          resMgr.booleanValue("launch", "noexcepthandler", false);
737
738   if (!result)
739   {
740     // Launch GUI activator
741     if (isGUI)
742     {
743       if (splash)
744         splash->setStatus(QObject::tr("Activating desktop..."));
745       // ...create GUI launcher
746       MESSAGE("Session activated, Launch IAPP...");
747       guiThread = new GetInterfaceThread(session);
748     }
749
750     // GUI activation
751     // Allow multiple activation/deactivation of GUI
752     while (true)
753     {
754       MESSAGE("waiting wakeAll()");
755       _SessionStarted.wait(&_SessionMutex); // to be reseased by Launch server thread when ready:
756       // atomic operation lock - unlock on mutex
757       // unlock mutex: serverThread runs, calls _ServerLaunch->wakeAll()
758       // this thread wakes up, and lock mutex
759
760       _SessionMutex.unlock();
761
762       // Session might be shutdowning here, check status
763       SALOME::StatSession stat = session->GetStatSession();
764       shutdownSession = stat.state == SALOME::shutdown;
765       if (shutdownSession)
766       {
767         _SessionMutex.lock(); // lock mutex before leaving loop - it will be unlocked later
768         break;
769       }
770
771       // SUIT_Session creation
772       aGUISession = new Session();
773
774       // Load SalomeApp dynamic library
775       MESSAGE("creation SUIT_Application");
776       SUIT_Application *aGUIApp = aGUISession->startApplication(NamingServiceImplementation::LibName, 0, 0);
777       if (aGUIApp)
778       {
779 #ifdef USE_SALOME_STYLE
780         Style_Salome::initialize(aGUIApp->resourceMgr());
781         if (aGUIApp->resourceMgr()->booleanValue("Style", "use_salome_style", true))
782           Style_Salome::apply();
783 #endif // USE_SALOME_STYLE
784
785         if (!debugExceptions)
786           app.setHandler(aGUISession->handler()); // after loading SalomeApp application
787                                                   // aGUISession contains SalomeApp_ExceptionHandler
788
789         // Run GUI loop
790         MESSAGE("run(): starting the main event loop");
791
792         if (splash)
793           splash->finish(aGUIApp->desktop());
794
795         result = app.exec();
796
797         splash = 0;
798
799         if (result == SUIT_Session::NORMAL)
800         {
801           // desktop is explicitly closed by user from GUI
802           // exit flags says if it's necessary to shutdown all servers
803           // all session server only
804           shutdownAll = aGUISession->exitFlags();
805         }
806         else
807         {
808           // desktop might be closed from:
809           // - StopSesion() (temporarily) or
810           // - Shutdown() (permanently)
811           stat = session->GetStatSession();
812           shutdownSession = stat.state == SALOME::shutdown;
813           // normally "shutdown standalone servers" flag should be false here, if we come from
814           // StopSesion() or from Shutdown();
815           // but we also have to check if somebody explicitly programmatically closed session,
816           // asking to kill servers also
817           shutdownAll = aGUISession->exitFlags();
818         }
819         if (shutdownAll || shutdownSession)
820         {
821           _SessionMutex.lock(); // lock mutex before leaving loop - it will be unlocked later
822           break;
823         }
824       }
825
826       delete aGUISession;
827       aGUISession = 0;
828
829       // Prepare _GUIMutex for a new GUI activation
830       _SessionMutex.lock();
831     }
832   }
833
834   // unlock Session mutex
835   _SessionMutex.unlock();
836
837   // Shutdown embedded servers
838   if (myServerLauncher)
839     myServerLauncher->ShutdownAll();
840
841   // Shutdown standalone servers
842   if (shutdownAll)
843     self.shutdownRemoteServersIfNeeded(remoteLauncher);
844
845   // Kill embedded servers
846   if (myServerLauncher)
847     myServerLauncher->KillAll();
848
849   // Unregister session server
850   SALOME_Session_i *sessionServant =
851       dynamic_cast<SALOME_Session_i *>(poa->reference_to_servant(session.in()));
852   if (sessionServant)
853     sessionServant->NSunregister();
854
855   delete aGUISession;
856   delete guiThread;
857   delete myServerLauncher;
858 #if defined(WIN32) && defined(UNICODE)
859   delete[] new_argv;
860 #endif
861   self.shutdownCORBAStufIfNeeded(shutdownAll,orb);
862   // Finalize Python
863   if (Py_IsInitialized())
864   {
865     PyGILState_Ensure();
866     Py_Finalize();
867   }
868   // Kill omniNames process
869   if (shutdownAll)
870     self.killOtherServersIfNeeded();
871
872   MESSAGE("Salome_Session_Server:endofserver");
873   return result;
874 }