]> SALOME platform Git repositories - modules/gui.git/blob - src/Session/SALOME_Session_Server.cxx
Salome HOME
[bos #42871] Clipping plane remains applied after being deleted
[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   // supports HDPI
481   MESSAGE("Set QApplication attributes to supports HDPI...");
482
483   // Make QIcon::pixmap() generate high-dpi pixmaps that can be larger than the requested size.
484   // Such pixmaps will have devicePixelRatio() set to a value higher than 1.
485   // After setting this attribute, application code that uses pixmap sizes in layout geometry calculations
486   // should typically divide by devicePixelRatio() to get device-independent layout geometry.
487   QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
488
489   // The lines below do the same - enables automatic scaling, based on the monitor's pixel density.
490   // This won't change the size of point-sized fonts, since point is a physical measurement unit.
491   // Using them can make an impression of large icons among small fonts.
492   // qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1");
493   // QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // Doesn't scale fonts
494
495   // Keep this line as an example of using explicit scale factor.
496   // Defines a global scale factor for the whole application, including point-sized fonts.
497   // qputenv("QT_SCALE_FACTOR", "1.5"); // Scales everything
498
499   // Install Qt debug messages handler
500   MsgHandler msgHandler;
501   qInstallMessageHandler(QtxMsgHandler);
502
503   // Add <qtdir>/plugins dir to the pluins search path for image plugins
504   QString qtdir = Qtx::qtDir("plugins");
505   if (!qtdir.isEmpty())
506     QApplication::addLibraryPath(qtdir);
507
508   // Add application library path (to search style plugin etc...)
509   QString path = SUIT_Tools::addSlash(Qtx::getenv("GUI_ROOT_DIR")) + "bin/salome";
510   QApplication::addLibraryPath(QDir::toNativeSeparators(path));
511
512   // QSurfaceFormat should be set before creation of QApplication,
513   // so to avoid conflicts beetween SALOME and ParaView QSurfaceFormats we should merge theirs formats
514   // (see void Qtx::initDefaultSurfaceFormat()) and set the resultant format here.
515   Qtx::initDefaultSurfaceFormat();
516
517   // Create Qt application instance: this should be done as early as possible!
518   // Note: QApplication forces setting locale LC_ALL to system one: setlocale(LC_ALL, "").
519   SetArgcArgv(argc,argv);
520   Application app(argc, argv);
521
522 #ifdef WIN32
523     QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
524                     QSettings::NativeFormat);
525     if(settings.value("AppsUseLightTheme")==0){
526         app.setStyle(QStyleFactory::create("Fusion"));
527         QPalette darkPalette;
528         QColor darkColor = QColor(45,45,45);
529         QColor disabledColor = QColor(127,127,127);
530         darkPalette.setColor(QPalette::Window, darkColor);
531         darkPalette.setColor(QPalette::WindowText, Qt::white);
532         darkPalette.setColor(QPalette::Base, QColor(18,18,18));
533         darkPalette.setColor(QPalette::AlternateBase, darkColor);
534         darkPalette.setColor(QPalette::ToolTipBase, Qt::white);
535         darkPalette.setColor(QPalette::ToolTipText, Qt::white);
536         darkPalette.setColor(QPalette::Text, Qt::white);
537         darkPalette.setColor(QPalette::Disabled, QPalette::Text, disabledColor);
538         darkPalette.setColor(QPalette::Button, darkColor);
539         darkPalette.setColor(QPalette::ButtonText, Qt::white);
540         darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, disabledColor);
541         darkPalette.setColor(QPalette::BrightText, Qt::red);
542         darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
543         darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218));
544         darkPalette.setColor(QPalette::HighlightedText, Qt::black);
545         darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, disabledColor);
546         darkPalette.setColor(QPalette::PlaceholderText, Qt::white);
547         darkPalette.setColor(QPalette::NoRole, Qt::white);
548
549         app.setPalette(darkPalette);
550         app.setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
551     }
552 #endif
553   // Initialize Python (only once)
554   // Note: Python forces setting locale LC_CTYPE to system one: setlocale(LC_CTYPE, "").
555   char *py_argv[] = {(char *)""};
556   KERNEL_PYTHON::init_python(1, py_argv);
557
558   // Create auxiliary resource manager to access application settings
559   ResourceMgr resMgr;
560   resMgr.setWorkingMode(ResourceMgr::IgnoreUserValues);
561   resMgr.loadLanguage("LightApp", "en");
562   resMgr.loadLanguage("SalomeApp", "en");
563   resMgr.loadLanguage("Session");
564
565   // Set-up application settings configuration possible customized via resources
566   if (resMgr.customName() != "SalomeApp")
567   {
568     QApplication::setApplicationName(resMgr.customName());
569     QApplication::setApplicationVersion(resMgr.version());
570   }
571
572   // Force default "C" locale if requested via user's preferences
573   // Note: this does not change whole application locale (changed via setlocale() function),
574   // but only affects GUI behavior
575   resMgr.setWorkingMode(ResourceMgr::AllowUserValues); // we must take into account user preferences
576   if (resMgr.booleanValue("language", "locale", true))
577     QLocale::setDefault(QLocale::c());
578   resMgr.setWorkingMode(ResourceMgr::IgnoreUserValues);
579
580 #if defined(GLOBAL_DOUBLE_CONVERSION)
581   // VSR 30/03/2021: moved here from QtxDoubleSpinBox/QtxIntSpinBox because of crash on Qt 5.12.
582   // Disable thousands separator for spin box
583   // see issue 14540 (old id 21219)
584   QLocale locale;
585   locale.setNumberOptions(locale.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
586   QLocale::setDefault(locale);
587 #endif
588
589   bool isGUI = boolCmdOption("--show-desktop", "--hide-desktop", true);  // true by default
590   bool isSplash = boolCmdOption("--show-splash", "--hide-splash", true); // true by default
591
592   // Show splash screen (only if both the "GUI" and "SPLASH" options are true)
593   QtxSplash *splash = 0;
594   if (isGUI && isSplash)
595   {
596     splash = QtxSplash::splash(QPixmap());
597     splash->readSettings(&resMgr);
598     if (splash->pixmap().isNull())
599       splash->setPixmap(resMgr.loadPixmap("LightApp", QObject::tr("ABOUT_SPLASH")));
600     if (splash->pixmap().isNull())
601     {
602       delete splash;
603       splash = 0;
604     }
605     else
606     {
607       splash->setOption("%A", QObject::tr("APP_NAME"));
608       splash->setOption("%V", QObject::tr("ABOUT_VERSION").arg(resMgr.version()));
609       splash->setOption("%L", QObject::tr("ABOUT_LICENSE"));
610       splash->setOption("%C", QObject::tr("ABOUT_COPYRIGHT"));
611       splash->show();
612       QApplication::instance()->processEvents();
613     }
614   }
615
616   // Initialization
617   int result = -1;
618
619   CORBA::ORB_var orb;
620   PortableServer::POA_var poa;
621
622   SUIT_Session *aGUISession = 0;
623   GetInterfaceThread *guiThread = 0;
624   Session_ServerLauncher<NamingServiceImplementation> *myServerLauncher = nullptr;
625
626 #if defined(WIN32) && defined(UNICODE)
627   char **new_argv = NULL;
628 #endif
629
630   bool remoteLauncher = false;
631
632   try
633   {
634     // ...create ORB, get RootPOA object, NamingService, etc.
635     int orbArgc = 1;
636     if (std::string(argv[1]).find("-ORBInitRef") != std::string::npos)
637     {
638       orbArgc = 3;
639       remoteLauncher = true;
640     }
641     SetArgcArgv(orbArgc, argv);
642     orb = KERNEL::GetRefToORB();
643
644     CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
645     poa = PortableServer::POA::_narrow(obj);
646
647     PortableServer::POAManager_var pman = poa->the_POAManager();
648     pman->activate();
649     MESSAGE("POA manager activated");
650
651     self.connectToNSIfNeeded(orb);
652
653     result = 0;
654   }
655   catch (SALOME_Exception &e)
656   {
657     INFOS("run(): Caught SALOME_Exception : " << e.what());
658   }
659   catch (CORBA::SystemException &e)
660   {
661     INFOS("run(): Caught CORBA::SystemException : " << handleCorbaException(e));
662   }
663   catch (CORBA::Exception &e)
664   {
665     INFOS("run(): Caught CORBA::Exception : " << handleCorbaException(e));
666   }
667   catch (std::exception &e)
668   {
669     INFOS("run(): Caught exception : " << e.what());
670   }
671   catch (...)
672   {
673     INFOS("run(): Caught unknown exception");
674   }
675
676   QMutex _GUIMutex, _SessionMutex, _SplashMutex;
677   QWaitCondition _ServerLaunch, _SessionStarted, _SplashStarted;
678
679   // lock session mutex to ensure that GetInterface is not called
680   // until all initialization is done
681   _SessionMutex.lock();
682
683   if (!result)
684   {
685     // Start embedded servers launcher (Registry, SALOMEDS, etc.)
686     // ...lock mutex to block embedded servers launching thread until wait( mutex )
687     _GUIMutex.lock();
688     // ...create launcher
689 #if defined(WIN32) && defined(UNICODE)
690     LPWSTR *szArglist = NULL;
691     int nArgs;
692     int i;
693     szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
694     new_argv = new char *[nArgs];
695     for (i = 0; i < nArgs; i++)
696     {
697       new_argv[i] = (char *)Kernel_Utils::utf8_encode(szArglist[i]);
698     }
699     // Free memory allocated for CommandLineToArgvW arguments.
700     LocalFree(szArglist);
701     myServerLauncher = new Session_ServerLauncher<NamingServiceImplementation>(nArgs, new_argv, orb, poa, &_GUIMutex, &_ServerLaunch, &_SessionMutex, &_SessionStarted);
702 #else
703     myServerLauncher = new Session_ServerLauncher<NamingServiceImplementation>(argc, argv, orb, poa, &_GUIMutex, &_ServerLaunch, &_SessionMutex, &_SessionStarted);
704 #endif
705     // ...block this thread until launcher is ready
706     _ServerLaunch.wait(&_GUIMutex);
707
708     // Start servers check thread (splash)
709     if (splash)
710     {
711       // ...lock mutex to block splash thread until wait( mutex )
712       _SplashMutex.lock();
713       // ...create servers checking thread
714       Session_ServerCheck<NamingServiceImplementation> sc(&_SplashMutex, &_SplashStarted);
715       // ... set initial progress
716       splash->setProgress(0, sc.totalSteps());
717       // start check loop
718       while (true)
719       {
720         int step = sc.currentStep();
721         int total = sc.totalSteps();
722         QString msg = sc.currentMessage();
723         QString err = sc.error();
724         if (!err.isEmpty())
725         {
726           QtxSplash::setError(err);
727           QApplication::instance()->processEvents();
728           result = -1;
729           break;
730         }
731         QtxSplash::setStatus(msg, step);
732         QApplication::instance()->processEvents();
733         if (step >= total)
734           break;
735         // ...block this thread until servers checking is finished
736         _SplashStarted.wait(&_SplashMutex);
737       }
738       // ...unlock mutex 'cause it is no more needed
739       _SplashMutex.unlock();
740     }
741
742     // Finalize embedded servers launcher
743     // ...block this thread until launcher is finished
744     _ServerLaunch.wait(&_GUIMutex);
745     // ...unlock mutex 'cause it is no more needed
746     _GUIMutex.unlock();
747   }
748
749   // Obtain Session interface reference
750   SALOME::Session_var session = self.getSession();
751
752   bool shutdownAll = false;
753   bool shutdownSession = false;
754   bool debugExceptions = boolCmdOption("--no-exception-handler") ||
755                          resMgr.booleanValue("launch", "noexcepthandler", false);
756
757   if (!result)
758   {
759     // Launch GUI activator
760     if (isGUI)
761     {
762       if (splash)
763         splash->setStatus(QObject::tr("Activating desktop..."));
764       // ...create GUI launcher
765       MESSAGE("Session activated, Launch IAPP...");
766       guiThread = new GetInterfaceThread(session);
767     }
768
769     // GUI activation
770     // Allow multiple activation/deactivation of GUI
771     while (true)
772     {
773       MESSAGE("waiting wakeAll()");
774       _SessionStarted.wait(&_SessionMutex); // to be reseased by Launch server thread when ready:
775       // atomic operation lock - unlock on mutex
776       // unlock mutex: serverThread runs, calls _ServerLaunch->wakeAll()
777       // this thread wakes up, and lock mutex
778
779       _SessionMutex.unlock();
780
781       // Session might be shutdowning here, check status
782       SALOME::StatSession stat = session->GetStatSession();
783       shutdownSession = stat.state == SALOME::shutdown;
784       if (shutdownSession)
785       {
786         _SessionMutex.lock(); // lock mutex before leaving loop - it will be unlocked later
787         break;
788       }
789
790       // SUIT_Session creation
791       aGUISession = new Session();
792
793       // Load SalomeApp dynamic library
794       MESSAGE("creation SUIT_Application");
795       SUIT_Application *aGUIApp = aGUISession->startApplication(NamingServiceImplementation::LibName, 0, 0);
796       if (aGUIApp)
797       {
798 #ifdef USE_SALOME_STYLE
799         Style_Salome::initialize(aGUIApp->resourceMgr());
800         if (aGUIApp->resourceMgr()->booleanValue("Style", "use_salome_style", true))
801           Style_Salome::apply();
802 #endif // USE_SALOME_STYLE
803
804         if (!debugExceptions)
805           app.setHandler(aGUISession->handler()); // after loading SalomeApp application
806                                                   // aGUISession contains SalomeApp_ExceptionHandler
807
808         // Run GUI loop
809         MESSAGE("run(): starting the main event loop");
810
811         if (splash)
812           splash->finish(aGUIApp->desktop());
813
814         result = app.exec();
815
816         splash = 0;
817
818         if (result == SUIT_Session::NORMAL)
819         {
820           // desktop is explicitly closed by user from GUI
821           // exit flags says if it's necessary to shutdown all servers
822           // all session server only
823           shutdownAll = aGUISession->exitFlags();
824         }
825         else
826         {
827           // desktop might be closed from:
828           // - StopSesion() (temporarily) or
829           // - Shutdown() (permanently)
830           stat = session->GetStatSession();
831           shutdownSession = stat.state == SALOME::shutdown;
832           // normally "shutdown standalone servers" flag should be false here, if we come from
833           // StopSesion() or from Shutdown();
834           // but we also have to check if somebody explicitly programmatically closed session,
835           // asking to kill servers also
836           shutdownAll = aGUISession->exitFlags();
837         }
838         if (shutdownAll || shutdownSession)
839         {
840           _SessionMutex.lock(); // lock mutex before leaving loop - it will be unlocked later
841           break;
842         }
843       }
844
845       delete aGUISession;
846       aGUISession = 0;
847
848       // Prepare _GUIMutex for a new GUI activation
849       _SessionMutex.lock();
850     }
851   }
852
853   // unlock Session mutex
854   _SessionMutex.unlock();
855
856   // Shutdown embedded servers
857   if (myServerLauncher)
858     myServerLauncher->ShutdownAll();
859
860   // Shutdown standalone servers
861   if (shutdownAll)
862     self.shutdownRemoteServersIfNeeded(remoteLauncher);
863
864   // Kill embedded servers
865   if (myServerLauncher)
866     myServerLauncher->KillAll();
867
868   // Unregister session server
869   SALOME_Session_i *sessionServant =
870       dynamic_cast<SALOME_Session_i *>(poa->reference_to_servant(session.in()));
871   if (sessionServant)
872     sessionServant->NSunregister();
873
874   delete aGUISession;
875   delete guiThread;
876   delete myServerLauncher;
877 #if defined(WIN32) && defined(UNICODE)
878   delete[] new_argv;
879 #endif
880   self.shutdownCORBAStufIfNeeded(shutdownAll,orb);
881   // Finalize Python
882   if (Py_IsInitialized())
883   {
884     PyGILState_Ensure();
885     Py_Finalize();
886   }
887   // Kill omniNames process
888   if (shutdownAll)
889     self.killOtherServersIfNeeded();
890
891   MESSAGE("Salome_Session_Server:endofserver");
892   return result;
893 }