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