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