Salome HOME
84c7f7a816a769ad18ab28304c90dc00cb79b9d2
[modules/gui.git] / src / Session / SALOME_Session_Server.cxx
1 // SALOME Session : implementation of Session.idl
2 //
3 // Copyright (C) 2003 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.
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.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org
21 //
22 //
23 //
24 // File : SALOME_Session_Server.cxx
25 // Author : Paul RASCLE, EDF
26 // Module : SALOME
27
28 #include <Container_init_python.hxx>
29 #include "Utils_ORB_INIT.hxx"
30 #include "Utils_SINGLETON.hxx"
31 #include "SALOME_NamingService.hxx"
32 #include "SALOMETraceCollector.hxx"
33
34 #include "InquireServersQThread.h" // splash
35
36 #include <iostream>
37 #ifndef WNT
38 #include <unistd.h>
39 #endif
40
41 #include <qdir.h>
42 #include <qfile.h>
43 #include <qapplication.h>
44 #include <qwaitcondition.h>
45
46 #include "Utils_SALOME_Exception.hxx"
47 #include "Utils_CorbaException.hxx"
48 #include "SALOME_Event.hxx"
49
50 #include <SALOMEconfig.h>
51 #include CORBA_SERVER_HEADER(SALOME_Session)
52 #include CORBA_SERVER_HEADER(SALOMEDS)
53
54 #include <utilities.h>
55 #include "Session_Session_i.hxx"
56 #include "Session_ServerLauncher.hxx"
57
58 #include "SUIT_Tools.h"
59 #include "SUIT_Session.h"
60 #include "SUIT_Application.h"
61 #include "SUIT_MessageBox.h"
62 #include "SUIT_ResourceMgr.h"
63
64 #include "SUIT_ExceptionHandler.h"
65
66 extern "C" int HandleSignals( QApplication *theQApplication );
67
68 /*! - read arguments, define list of server to launch with their arguments.
69  * - wait for naming service
70  * - create and run a thread for launch of all servers
71  *
72 */
73
74 //! CORBA server for SALOME Session
75 /*!
76  * SALOME_Session Server launches a SALOME session servant.
77  * The servant registers to the Naming Service.
78  * See SALOME_Session.idl for interface specification.
79  *
80  * Main services offered by the servant are:
81  * - launch GUI
82  * - stop Session ( must be idle )
83  * - get session state
84  */
85
86 PyObject* salome_shared_modules_module = 0;
87
88 void MessageOutput( QtMsgType type, const char* msg )
89 {
90   switch ( type )
91   {
92   case QtDebugMsg:
93     MESSAGE( "Debug: " << msg );
94     break;
95   case QtWarningMsg:
96     MESSAGE( "Warning: " << msg );
97     break;
98   case QtFatalMsg:
99     MESSAGE( "Fatal: " << msg );
100     break;
101   }
102 }
103
104 /* XPM */
105 static const char* pixmap_not_found_xpm[] = {
106 "16 16 3 1",
107 "       c None",
108 ".      c #000000",
109 "+      c #A80000",
110 "                ",
111 "                ",
112 "    .     .     ",
113 "   .+.   .+.    ",
114 "  .+++. .+++.   ",
115 "   .+++.+++.    ",
116 "    .+++++.     ",
117 "     .+++.      ",
118 "    .+++++.     ",
119 "   .+++.+++.    ",
120 "  .+++. .+++.   ",
121 "   .+.   .+.    ",
122 "    .     .     ",
123 "                ",
124 "                ",
125 "                "};
126
127 QString salomeVersion()
128 {
129   QString path( ::getenv( "GUI_ROOT_DIR" ) );
130   if ( !path.isEmpty() )
131     path += QDir::separator();
132   path += QString( "bin/salome/VERSION" );
133
134   QFile vf( path );
135   if ( !vf.open( IO_ReadOnly ) )
136     return QString::null;
137
138   QString line;
139   vf.readLine( line, 1024 );
140   vf.close();
141
142   if ( line.isEmpty() )
143     return QString::null;
144
145   while ( !line.isEmpty() && line.at( line.length() - 1 ) == QChar( '\n' ) )
146     line.remove( line.length() - 1, 1 );
147
148   QString ver;
149   int idx = line.findRev( ":" );
150   if ( idx != -1 )
151     ver = line.mid( idx + 1 ).stripWhiteSpace();
152
153   return ver;
154 }
155
156 class SALOME_ResourceMgr : public SUIT_ResourceMgr
157 {
158 public:
159   SALOME_ResourceMgr( const QString& app, const QString& resVarTemplate ) : SUIT_ResourceMgr( app, resVarTemplate )
160   {
161     setCurrentFormat( "xml" );
162     setOption( "translators", QString( "%P_msg_%L.qm|%P_icons.qm|%P_images.qm" ) );
163     setDefaultPixmap( QPixmap( pixmap_not_found_xpm ) );
164   }
165   static void initResourceMgr()
166   {
167     if ( myExtAppName.isNull() || myExtAppVersion.isNull() ) {
168       SALOME_ResourceMgr resMgr( "SalomeApp", QString( "%1Config" ) );
169       resMgr.loadLanguage( "SalomeApp", "en" );
170
171       myExtAppName = QObject::tr( "APP_NAME" ).stripWhiteSpace();
172       if ( myExtAppName == "APP_NAME" || myExtAppName.lower() == "salome" ) 
173         myExtAppName = "SalomeApp";
174       myExtAppVersion = QObject::tr( "APP_VERSION" );
175       if ( myExtAppVersion == "APP_VERSION" ) {
176         if ( myExtAppName != "SalomeApp" )
177           myExtAppVersion = "";
178         else myExtAppVersion = salomeVersion();
179       }
180     }
181   }
182   QString version() const { return myExtAppVersion; }
183
184 protected:
185   QString userFileName( const QString& appName ) const
186   { 
187     if ( version().isNull()  ) return ""; 
188     return SUIT_ResourceMgr::userFileName( myExtAppName );
189   }
190
191 public:
192   static QString myExtAppName;
193   static QString myExtAppVersion;
194 };
195
196 QString SALOME_ResourceMgr::myExtAppName    = QString::null;
197 QString SALOME_ResourceMgr::myExtAppVersion = QString::null;
198
199 class SALOME_Session : public SUIT_Session
200 {
201 public:
202   SALOME_Session() : SUIT_Session() {}
203   virtual ~SALOME_Session() {}
204
205 protected:
206   virtual SUIT_ResourceMgr* createResourceMgr( const QString& appName ) const
207   {
208     SALOME_ResourceMgr::initResourceMgr();
209     SALOME_ResourceMgr* resMgr = new SALOME_ResourceMgr( appName, QString( "%1Config" ) );
210     return resMgr;
211   }
212 };
213
214 class SALOME_QApplication : public QApplication
215 {
216 public:
217   SALOME_QApplication( int& argc, char** argv ) : QApplication( argc, argv ), myHandler ( 0 ) {}
218
219   virtual bool notify( QObject* receiver, QEvent* e )
220   {
221     return myHandler ? myHandler->handle( receiver, e ) :
222       QApplication::notify( receiver, e );
223   }
224   SUIT_ExceptionHandler* handler() const { return myHandler; }
225   void setHandler( SUIT_ExceptionHandler* h ) { myHandler = h; }
226
227 private:
228   SUIT_ExceptionHandler* myHandler;
229 };
230
231 // class which calls SALOME::Session::GetInterface() from another thread
232 // to avoid mutual lock ( if called from the same thread as main()
233 class GetInterfaceThread : public QThread
234 {
235 public:
236   GetInterfaceThread( SALOME::Session_var s ) : session ( s ) {}
237 protected:
238   virtual void run()
239   {
240     if ( !CORBA::is_nil( session ) )
241       session->GetInterface();
242     else
243       printf( "\nFATAL ERROR: SALOME::Session object is nil! Can not display GUI\n\n" );
244   }
245 private:
246   SALOME::Session_var session;
247 };
248
249 // returns true if 'str' is found in argv
250 bool isFound( const char* str, int argc, char** argv )
251 {
252   for ( int i = 1; i <= ( argc-1 ); i++ )
253     if ( !strcmp( argv[i], str ) )
254       return true;
255   return false;
256 }
257
258 // ---------------------------- MAIN -----------------------
259 int main( int argc, char **argv )
260 {
261   qInstallMsgHandler( MessageOutput );
262
263   // QApplication should be create before all other operations
264   // When uses QApplication::libraryPaths() ( example, QFile::encodeName() )
265   // qApp used for detection of the executable dir path.
266   SALOME_QApplication _qappl( argc, argv );
267   ASSERT( QObject::connect( &_qappl, SIGNAL( lastWindowClosed() ), &_qappl, SLOT( quit() ) ) );
268
269   QString path = QDir::convertSeparators( SUIT_Tools::addSlash( QString( ::getenv( "GUI_ROOT_DIR" ) ) ) + QString( "bin/salome" ) );
270   _qappl.addLibraryPath( path );
271   
272   _qappl.setStyle( "salome" );
273
274   int result = -1;
275
276   CORBA::ORB_var orb;
277   PortableServer::POA_var poa;
278
279   SUIT_Session* aGUISession = 0;
280   SALOME_NamingService* _NS = 0;
281   GetInterfaceThread* guiThread = 0;
282   Session_ServerLauncher* myServerLauncher = 0;
283
284   try {
285     
286     // Python initialisation : only once
287
288     int _argc = 1;
289     char* _argv[] = {""};
290     KERNEL_PYTHON::init_python( _argc,_argv );
291     PyEval_RestoreThread( KERNEL_PYTHON::_gtstate );
292     if ( !KERNEL_PYTHON::salome_shared_modules_module ) // import only once
293       KERNEL_PYTHON::salome_shared_modules_module = PyImport_ImportModule( "salome_shared_modules" );
294     if ( !KERNEL_PYTHON::salome_shared_modules_module )
295     {
296       INFOS( "salome_shared_modules_module == NULL" );
297       PyErr_Print();
298       PyErr_Clear();
299     }
300     PyEval_ReleaseThread( KERNEL_PYTHON::_gtstate );
301
302     // Create ORB, get RootPOA object, NamingService, etc.
303     ORB_INIT &init = *SINGLETON_<ORB_INIT>::Instance();
304     ASSERT( SINGLETON_<ORB_INIT>::IsAlreadyExisting() );
305     int orbArgc = 1;
306     orb = init( orbArgc, argv );
307
308     // Install SALOME thread event handler
309     SALOME_Event::GetSessionThread();
310
311     CORBA::Object_var obj = orb->resolve_initial_references( "RootPOA" );
312     poa = PortableServer::POA::_narrow( obj );
313
314     PortableServer::POAManager_var pman = poa->the_POAManager();
315     pman->activate() ;
316     INFOS( "pman->activate()" );
317
318     _NS = new SALOME_NamingService( orb );
319
320     result = 0;
321   }
322   catch ( SALOME_Exception& e ) {
323     INFOS( "run(): SALOME::SALOME_Exception is caught: "<<e.what() );
324   }
325   catch ( CORBA::SystemException& e ) {
326     INFOS( "Caught CORBA::SystemException." );
327   }
328   catch ( CORBA::Exception& e ) {
329     INFOS( "Caught CORBA::Exception." );
330     CORBA::Any tmp;
331     tmp<<= e;
332     CORBA::TypeCode_var tc = tmp.type();
333     const char *p = tc->name();
334     INFOS ( "run(): CORBA exception of the kind : "<<p<< " is caught" );
335   }
336   catch ( exception& e ) {
337     INFOS( "run(): An exception has been caught: " <<e.what() );
338   }
339   catch (...) {
340     INFOS( "Caught unknown exception." );
341   }
342
343   // CORBA Servant Launcher
344   QMutex _GUIMutex;
345   QWaitCondition _ServerLaunch, _SessionStarted;
346
347   if ( !result )
348   {
349     _GUIMutex.lock();  // to block Launch server thread until wait( mutex )
350
351     // Activate embedded CORBA servers: Registry, SALOMEDS, etc.
352     myServerLauncher = new Session_ServerLauncher( argc, argv, orb, poa, &_GUIMutex, &_ServerLaunch, &_SessionStarted );
353     myServerLauncher->start();
354
355     _ServerLaunch.wait( &_GUIMutex ); // to be reseased by Launch server thread when ready:
356     
357     // show splash screen if "SPLASH" parameter was passed ( default )
358     if ( isFound( "SPLASH", argc, argv ) )
359     {
360       // create temporary resource manager just to load splash icon
361       SUIT_ResourceMgr resMgr( "SalomeApp", QString( "%1Config" ) );
362       resMgr.setCurrentFormat( "xml" );
363       resMgr.loadLanguage( "LightApp", "en" );
364
365       // create splash object: widget ( splash with progress bar ) and "pinging" thread
366       InquireServersGUI splash;
367       splash.setPixmap( resMgr.loadPixmap( "LightApp", QObject::tr( "ABOUT_SPLASH" ) ) );
368       SUIT_Tools::centerWidget( &splash, _qappl.desktop() );
369       
370       _qappl.setMainWidget( &splash );
371       QObject::connect( &_qappl, SIGNAL( lastWindowClosed() ), &_qappl, SLOT( quit() ) );
372       splash.show(); // display splash with running progress bar
373       _qappl.exec(); // wait untill splash closes ( progress runs till end or Cancel is pressed )
374       
375       result = splash.getExitStatus(); // 1 is error
376     }
377     else
378       _SessionStarted.wait();
379   }
380
381   // call Session::GetInterface() if "GUI" parameter was passed ( default )
382   if ( !result && isFound( "GUI", argc, argv ) )
383   {
384     CORBA::Object_var obj = _NS->Resolve( "/Kernel/Session" );
385     SALOME::Session_var session = SALOME::Session::_narrow( obj ) ;
386     ASSERT ( ! CORBA::is_nil( session ) );
387
388     INFOS( "Session activated, Launch IAPP..." );
389     guiThread = new GetInterfaceThread( session );
390     guiThread->start();
391   }
392
393   if ( !result )
394   {
395
396     // GUI activation
397     // Allow multiple activation/deactivation of GUI
398     while ( true )
399     {
400       MESSAGE( "waiting wakeAll()" );
401       _ServerLaunch.wait( &_GUIMutex ); // to be reseased by Launch server thread when ready:
402       // atomic operation lock - unlock on mutex
403       // unlock mutex: serverThread runs, calls _ServerLaunch->wakeAll()
404       // this thread wakes up, and lock mutex
405
406       _GUIMutex.unlock();
407
408       // SUIT_Session creation
409       aGUISession = new SALOME_Session();
410
411       // Load SalomeApp dynamic library
412       INFOS( "creation SUIT_Application" );
413       SUIT_Application* aGUIApp = aGUISession->startApplication( "SalomeApp", 0, 0 );
414       if ( aGUIApp )
415       {
416         _qappl.setHandler( aGUISession->handler() ); // after loading SalomeApp application
417                                                      // aGUISession contains SalomeApp_ExceptionHandler
418         // Run GUI loop
419         MESSAGE( "run(): starting the main event loop" );
420         result = _qappl.exec();
421
422         if ( result == SUIT_Session::FROM_GUI ) // desktop is closed by user from GUI
423           break;
424       }
425
426       delete aGUISession;
427       aGUISession = 0;
428
429       // Prepare _GUIMutex for a new GUI activation
430       _GUIMutex.lock();
431     }
432   }
433
434   if ( myServerLauncher )
435     myServerLauncher->KillAll(); // kill embedded servers
436
437   delete aGUISession;
438   delete guiThread;
439   delete myServerLauncher;
440   delete _NS;
441
442   LocalTraceBufferPool *bp1 = LocalTraceBufferPool::instance();
443   LocalTraceBufferPool::deleteInstance(bp1);
444
445   return result;
446 }