Salome HOME
55985e7eebcb65707e3c0770f20cad8e976650d2
[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 <iostream>
35 #ifndef WNT
36 #include <unistd.h>
37 #endif
38
39 #include <qdir.h>
40 #include <qfile.h>
41 #include <qapplication.h>
42 #include <qwaitcondition.h>
43 #include <qregexp.h>
44
45 #include "Utils_SALOME_Exception.hxx"
46 #include "Utils_CorbaException.hxx"
47 #include "SALOME_Event.hxx"
48
49 #include <SALOMEconfig.h>
50 #include CORBA_SERVER_HEADER(SALOME_Session)
51 #include CORBA_SERVER_HEADER(SALOMEDS)
52
53 #include <utilities.h>
54 #include "Session_Session_i.hxx"
55 #include "Session_ServerLauncher.hxx"
56 #include "Session_ServerCheck.hxx"
57
58 #include <QtxSplash.h>
59 #include "SUIT_Tools.h"
60 #include "SUIT_Session.h"
61 #include "SUIT_Application.h"
62 #include "SUIT_Desktop.h"
63 #include "SUIT_MessageBox.h"
64 #include "SUIT_ResourceMgr.h"
65 #include "SUIT_ExceptionHandler.h"
66
67 /*! - read arguments, define list of server to launch with their arguments.
68  * - wait for naming service
69  * - create and run a thread for launch of all servers
70  *
71 */
72
73 //! CORBA server for SALOME Session
74 /*!
75  * SALOME_Session Server launches a SALOME session servant.
76  * The servant registers to the Naming Service.
77  * See SALOME_Session.idl for interface specification.
78  *
79  * Main services offered by the servant are:
80  * - launch GUI
81  * - stop Session ( must be idle )
82  * - get session state
83  */
84
85 PyObject* salome_shared_modules_module = 0;
86
87 void MessageOutput( QtMsgType type, const char* msg )
88 {
89   switch ( type )
90   {
91   case QtDebugMsg:
92     MESSAGE( "Debug: " << msg );
93     break;
94   case QtWarningMsg:
95     MESSAGE( "Warning: " << msg );
96     break;
97   case QtFatalMsg:
98     MESSAGE( "Fatal: " << msg );
99     break;
100   }
101 }
102
103 /* XPM */
104 static const char* pixmap_not_found_xpm[] = {
105 "16 16 3 1",
106 "       c None",
107 ".      c #000000",
108 "+      c #A80000",
109 "                ",
110 "                ",
111 "    .     .     ",
112 "   .+.   .+.    ",
113 "  .+++. .+++.   ",
114 "   .+++.+++.    ",
115 "    .+++++.     ",
116 "     .+++.      ",
117 "    .+++++.     ",
118 "   .+++.+++.    ",
119 "  .+++. .+++.   ",
120 "   .+.   .+.    ",
121 "    .     .     ",
122 "                ",
123 "                ",
124 "                "};
125
126 QString salomeVersion()
127 {
128   QString path( ::getenv( "GUI_ROOT_DIR" ) );
129   if ( !path.isEmpty() )
130     path += QDir::separator();
131   path += QString( "bin/salome/VERSION" );
132
133   QFile vf( path );
134   if ( !vf.open( IO_ReadOnly ) )
135     return QString::null;
136
137   QString line;
138   vf.readLine( line, 1024 );
139   vf.close();
140
141   if ( line.isEmpty() )
142     return QString::null;
143
144   while ( !line.isEmpty() && line.at( line.length() - 1 ) == QChar( '\n' ) )
145     line.remove( line.length() - 1, 1 );
146
147   QString ver;
148   int idx = line.findRev( ":" );
149   if ( idx != -1 )
150     ver = line.mid( idx + 1 ).stripWhiteSpace();
151
152   return ver;
153 }
154
155 class SALOME_ResourceMgr : public SUIT_ResourceMgr
156 {
157 public:
158   SALOME_ResourceMgr( const QString& app, const QString& resVarTemplate ) : SUIT_ResourceMgr( app, resVarTemplate )
159   {
160     setCurrentFormat( "xml" );
161     setOption( "translators", QString( "%P_msg_%L.qm|%P_icons.qm|%P_images.qm" ) );
162     setDefaultPixmap( QPixmap( pixmap_not_found_xpm ) );
163   }
164   static void initResourceMgr()
165   {
166     if ( myExtAppName.isNull() || myExtAppVersion.isNull() ) {
167       SALOME_ResourceMgr resMgr( "SalomeApp", QString( "%1Config" ) );
168       resMgr.loadLanguage( "SalomeApp", "en" );
169
170       myExtAppName = QObject::tr( "APP_NAME" ).stripWhiteSpace();
171       if ( myExtAppName == "APP_NAME" || myExtAppName.lower() == "salome" ) 
172         myExtAppName = "SalomeApp";
173       myExtAppVersion = QObject::tr( "APP_VERSION" );
174       if ( myExtAppVersion == "APP_VERSION" ) {
175         if ( myExtAppName != "SalomeApp" )
176           myExtAppVersion = "";
177         else myExtAppVersion = salomeVersion();
178       }
179     }
180   }
181   QString version() const { return myExtAppVersion; }
182
183 protected:
184   QString userFileName( const QString& appName, const bool for_load ) const
185   { 
186     if ( version().isNull()  ) return ""; 
187     return SUIT_ResourceMgr::userFileName( myExtAppName, for_load );
188   }
189
190   virtual int userFileId( const QString& _fname ) const
191   {
192     QRegExp exp( "\\.SalomeApprc\\.([a-zA-Z0-9.]+)$" );
193     QRegExp vers_exp( "^([0-9]+)([A-Za-z]?)([0-9]*)$" );
194
195     QString fname = QFileInfo( _fname ).fileName();
196     if( exp.exactMatch( fname ) )
197     {
198       QStringList vers = QStringList::split( ".", exp.cap( 1 ) );
199       int major=0, minor=0;
200       major = vers[0].toInt();
201       minor = vers[1].toInt();
202       if( vers_exp.search( vers[2] )==-1 )
203         return -1;
204       int release = 0, dev1 = 0, dev2 = 0;
205       release = vers_exp.cap( 1 ).toInt();
206       dev1 = vers_exp.cap( 2 )[ 0 ].latin1();
207       dev2 = vers_exp.cap( 3 ).toInt();
208
209       int dev = dev1*100+dev2, id = major;
210       id*=100; id+=minor;
211       id*=100; id+=release;
212       id*=10000; id+=dev;
213       return id;
214     }
215
216     return -1;
217   }
218
219 public:
220   static QString myExtAppName;
221   static QString myExtAppVersion;
222 };
223
224 QString SALOME_ResourceMgr::myExtAppName    = QString::null;
225 QString SALOME_ResourceMgr::myExtAppVersion = QString::null;
226
227 class SALOME_Session : public SUIT_Session
228 {
229 public:
230   SALOME_Session() : SUIT_Session() {}
231   virtual ~SALOME_Session() {}
232
233 protected:
234   virtual SUIT_ResourceMgr* createResourceMgr( const QString& appName ) const
235   {
236     SALOME_ResourceMgr::initResourceMgr();
237     SALOME_ResourceMgr* resMgr = new SALOME_ResourceMgr( appName, QString( "%1Config" ) );
238     return resMgr;
239   }
240 };
241
242 class SALOME_QApplication : public QApplication
243 {
244 public:
245   SALOME_QApplication( int& argc, char** argv ) : QApplication( argc, argv ), myHandler ( 0 ) {}
246
247   virtual bool notify( QObject* receiver, QEvent* e )
248   {
249     return myHandler ? myHandler->handle( receiver, e ) :
250       QApplication::notify( receiver, e );
251   }
252   SUIT_ExceptionHandler* handler() const { return myHandler; }
253   void setHandler( SUIT_ExceptionHandler* h ) { myHandler = h; }
254
255 private:
256   SUIT_ExceptionHandler* myHandler;
257 };
258
259 // class which calls SALOME::Session::GetInterface() from another thread
260 // to avoid mutual lock ( if called from the same thread as main()
261 class GetInterfaceThread : public QThread
262 {
263 public:
264   GetInterfaceThread( SALOME::Session_var s ) : session ( s )
265   {
266     start();
267   }
268 protected:
269   virtual void run()
270   {
271     if ( !CORBA::is_nil( session ) )
272       session->GetInterface();
273     else
274       printf( "\nFATAL ERROR: SALOME::Session object is nil! Can not display GUI\n\n" );
275   }
276 private:
277   SALOME::Session_var session;
278 };
279
280 // returns true if 'str' is found in argv
281 bool isFound( const char* str, int argc, char** argv )
282 {
283   for ( int i = 1; i <= ( argc-1 ); i++ )
284     if ( !strcmp( argv[i], str ) )
285       return true;
286   return false;
287 }
288
289 // ---------------------------- MAIN -----------------------
290 int main( int argc, char **argv )
291 {
292   // Install Qt debug messages handler
293   qInstallMsgHandler( MessageOutput );
294   
295   // Create Qt application instance;
296   // this should be done the very first!
297   SALOME_QApplication _qappl( argc, argv );
298   ASSERT( QObject::connect( &_qappl, SIGNAL( lastWindowClosed() ), &_qappl, SLOT( quit() ) ) );
299
300   // Add application library path (to search style plugin etc...)
301   QString path = QDir::convertSeparators( SUIT_Tools::addSlash( QString( ::getenv( "GUI_ROOT_DIR" ) ) ) + QString( "bin/salome" ) );
302   _qappl.addLibraryPath( path );
303   
304   // Set SALOME style to the application
305   _qappl.setStyle( "salome" );
306
307   bool isGUI    = isFound( "GUI",    argc, argv );
308   bool isSplash = isFound( "SPLASH", argc, argv );
309   // Show splash screen (only if both the "GUI" and "SPLASH" parameters are set)
310   QtxSplash* splash = 0;
311   if ( isGUI && isSplash ) {
312     // ...create resource manager
313     SUIT_ResourceMgr resMgr( "SalomeApp", QString( "%1Config" ) );
314     resMgr.setCurrentFormat( "xml" );
315     resMgr.loadLanguage( "LightApp", "en" );
316     // ...get splash preferences
317     QString splashIcon, splashInfo, splashTextColors, splashProgressColors;
318     resMgr.value( "splash", "image",           splashIcon );
319     resMgr.value( "splash", "info",            splashInfo, false );
320     resMgr.value( "splash", "text_colors",     splashTextColors );
321     resMgr.value( "splash", "progress_colors", splashProgressColors );
322     QPixmap px( splashIcon );
323     if ( px.isNull() ) // try to get splash pixmap from resources
324       px = resMgr.loadPixmap( "LightApp", QObject::tr( "ABOUT_SPLASH" ) );
325     if ( !px.isNull() ) {
326       // ...set splash pixmap
327       splash = QtxSplash::splash( px );
328       // ...set splash text colors
329       if ( !splashTextColors.isEmpty() ) {
330         QStringList colors = QStringList::split( "|", splashTextColors );
331         QColor c1, c2;
332         if ( colors.count() > 0 ) c1 = QColor( colors[0] );
333         if ( colors.count() > 1 ) c2 = QColor( colors[1] );
334         splash->setTextColors( c1, c2 );
335       }
336       else {
337         splash->setTextColors( Qt::white, Qt::black );
338       }
339       // ...set splash progress colors
340       if ( !splashProgressColors.isEmpty() ) {
341         QStringList colors = QStringList::split( "|", splashProgressColors );
342         QColor c1, c2;
343         int gradType = QtxSplash::Vertical;
344         if ( colors.count() > 0 ) c1 = QColor( colors[0] );
345         if ( colors.count() > 1 ) c2 = QColor( colors[1] );
346         if ( colors.count() > 2 ) gradType = colors[2].toInt();
347         splash->setProgressColors( c1, c2, gradType );
348       }
349       // ...set splash text font
350       QFont f = splash->font();
351       f.setBold( true );
352       splash->setFont( f );
353       // ...show splash initial status
354       if ( !splashInfo.isEmpty() ) {
355         splashInfo.replace( QRegExp( "%A" ),  QObject::tr( "APP_NAME" ) );
356         splashInfo.replace( QRegExp( "%V" ),  QObject::tr( "ABOUT_VERSION" ).arg( salomeVersion() ) );
357         splashInfo.replace( QRegExp( "%L" ),  QObject::tr( "ABOUT_LICENSE" ) );
358         splashInfo.replace( QRegExp( "%C" ),  QObject::tr( "ABOUT_COPYRIGHT" ) );
359         splashInfo.replace( QRegExp( "\\\\n" ), "\n" );
360         splash->message( splashInfo );
361       }
362       // ...set 'hide on click' flag
363 #ifdef _DEBUG_
364       splash->setHideOnClick( true );
365 #endif
366       // ...show splash
367       splash->show();
368       qApp->processEvents();
369     }
370   }
371
372   // Initialization
373   int result = -1;
374
375   CORBA::ORB_var orb;
376   PortableServer::POA_var poa;
377
378   SUIT_Session* aGUISession = 0;
379   SALOME_NamingService* _NS = 0;
380   GetInterfaceThread* guiThread = 0;
381   Session_ServerLauncher* myServerLauncher = 0;
382
383   try {
384     // ...initialize Python (only once)
385     int   _argc   = 1;
386     char* _argv[] = {""};
387     KERNEL_PYTHON::init_python( _argc,_argv );
388     PyEval_RestoreThread( KERNEL_PYTHON::_gtstate );
389     if ( !KERNEL_PYTHON::salome_shared_modules_module ) // import only once
390       KERNEL_PYTHON::salome_shared_modules_module = PyImport_ImportModule( "salome_shared_modules" );
391     if ( !KERNEL_PYTHON::salome_shared_modules_module ) {
392       INFOS( "salome_shared_modules_module == NULL" );
393       PyErr_Print();
394     }
395     PyEval_ReleaseThread( KERNEL_PYTHON::_gtstate );
396
397     // ...create ORB, get RootPOA object, NamingService, etc.
398     ORB_INIT &init = *SINGLETON_<ORB_INIT>::Instance();
399     ASSERT( SINGLETON_<ORB_INIT>::IsAlreadyExisting() );
400     int orbArgc = 1;
401     orb = init( orbArgc, argv );
402
403     // ...install SALOME thread event handler
404     SALOME_Event::GetSessionThread();
405
406     CORBA::Object_var obj = orb->resolve_initial_references( "RootPOA" );
407     poa = PortableServer::POA::_narrow( obj );
408
409     PortableServer::POAManager_var pman = poa->the_POAManager();
410     pman->activate() ;
411     INFOS( "pman->activate()" );
412
413     _NS = new SALOME_NamingService( orb );
414
415     result = 0;
416   }
417   catch ( SALOME_Exception& e ) {
418     INFOS( "run(): SALOME::SALOME_Exception is caught: "<<e.what() );
419   }
420   catch ( CORBA::SystemException& e ) {
421     INFOS( "Caught CORBA::SystemException." );
422   }
423   catch ( CORBA::Exception& e ) {
424     INFOS( "Caught CORBA::Exception." );
425     CORBA::Any tmp;
426     tmp<<= e;
427     CORBA::TypeCode_var tc = tmp.type();
428     const char *p = tc->name();
429     INFOS ( "run(): CORBA exception of the kind : "<<p<< " is caught" );
430   }
431   catch ( exception& e ) {
432     INFOS( "run(): An exception has been caught: " <<e.what() );
433   }
434   catch (...) {
435     INFOS( "Caught unknown exception." );
436   }
437
438   QMutex _GUIMutex, _SessionMutex, _SplashMutex;
439   QWaitCondition _ServerLaunch, _SessionStarted, _SplashStarted;
440
441   // lock session mutex to ensure that GetInterface is not called
442   // until all initialization is done
443   _SessionMutex.lock();
444
445   if ( !result ) {
446     // Start embedded servers launcher (Registry, SALOMEDS, etc.)
447     // ...lock mutex to block embedded servers launching thread until wait( mutex )
448     _GUIMutex.lock();  
449     // ...create launcher
450     myServerLauncher = new Session_ServerLauncher( argc, argv, orb, poa, &_GUIMutex, &_ServerLaunch, &_SessionMutex, &_SessionStarted );
451     // ...block this thread until launcher is ready
452     _ServerLaunch.wait( &_GUIMutex );
453     
454     // Start servers check thread (splash)
455     if ( splash ) {
456       // ...lock mutex to block splash thread until wait( mutex )
457       _SplashMutex.lock();
458       // ...create servers checking thread
459       Session_ServerCheck sc( &_SplashMutex, &_SplashStarted );
460       // ...block this thread until servers checking is finished
461       _SplashStarted.wait( &_SplashMutex );
462       // ...unlock mutex 'cause it is no more needed
463       _SplashMutex.unlock();
464       // get servers checking thread status
465       result = splash->error();
466       QString info = splash->message().isEmpty() ? "%1" : QString( "%1\n%2" ).arg( splash->message() );
467       splash->setStatus( info.arg( "Activating desktop..." ) );
468     }
469
470     // Finalize embedded servers launcher 
471     // ...block this thread until launcher is finished
472     _ServerLaunch.wait( &_GUIMutex );
473     // ...unlock mutex 'cause it is no more needed
474     _GUIMutex.unlock();
475   }
476
477   if ( !result ) {
478     // Launch GUI activator
479     if ( isGUI ) {
480       // ...retrieve Session interface reference
481       CORBA::Object_var obj = _NS->Resolve( "/Kernel/Session" );
482       SALOME::Session_var session = SALOME::Session::_narrow( obj ) ;
483       ASSERT ( ! CORBA::is_nil( session ) );
484       // ...create GUI launcher
485       INFOS( "Session activated, Launch IAPP..." );
486       guiThread = new GetInterfaceThread( session );
487     }
488
489     // GUI activation
490     // Allow multiple activation/deactivation of GUI
491     while ( true ) {
492       MESSAGE( "waiting wakeAll()" );
493       _SessionStarted.wait( &_SessionMutex ); // to be reseased by Launch server thread when ready:
494       // atomic operation lock - unlock on mutex
495       // unlock mutex: serverThread runs, calls _ServerLaunch->wakeAll()
496       // this thread wakes up, and lock mutex
497
498       _SessionMutex.unlock();
499
500       // SUIT_Session creation
501       aGUISession = new SALOME_Session();
502
503       // Load SalomeApp dynamic library
504       INFOS( "creation SUIT_Application" );
505       SUIT_Application* aGUIApp = aGUISession->startApplication( "SalomeApp", 0, 0 );
506       if ( aGUIApp )
507       {
508         if ( !isFound( "noexcepthandler", argc, argv ) )
509           _qappl.setHandler( aGUISession->handler() ); // after loading SalomeApp application
510                                                        // aGUISession contains SalomeApp_ExceptionHandler
511         // Run GUI loop
512         MESSAGE( "run(): starting the main event loop" );
513
514         if ( splash )
515           splash->finish( aGUIApp->desktop() );
516           
517         result = _qappl.exec();
518         
519         if ( splash )
520           delete splash;
521         splash = 0;
522
523         if ( result == SUIT_Session::FROM_GUI ) // desktop is closed by user from GUI
524           break;
525       }
526
527       delete aGUISession;
528       aGUISession = 0;
529
530       // Prepare _GUIMutex for a new GUI activation
531       _SessionMutex.lock();
532     }
533   }
534
535   // unlock Session mutex
536   _SessionMutex.unlock();
537   
538   if ( myServerLauncher )
539     myServerLauncher->KillAll(); // kill embedded servers
540
541   delete aGUISession;
542   delete guiThread;
543   delete myServerLauncher;
544   delete _NS;
545
546   LocalTraceBufferPool *bp1 = LocalTraceBufferPool::instance();
547   LocalTraceBufferPool::deleteInstance(bp1);
548
549   return result;
550 }