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