1 // SALOME Session : implementation of Session.idl
3 // Copyright (C) 2003 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
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.
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.
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
20 // See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org
24 // File : SALOME_Session_Server.cxx
25 // Author : Paul RASCLE, EDF
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"
41 #include <qapplication.h>
42 #include <qwaitcondition.h>
45 #include "Utils_SALOME_Exception.hxx"
46 #include "Utils_CorbaException.hxx"
47 #include "SALOME_Event.hxx"
49 #include <SALOMEconfig.h>
50 #include CORBA_SERVER_HEADER(SALOME_Session)
51 #include CORBA_SERVER_HEADER(SALOMEDS)
53 #include <utilities.h>
54 #include "Session_Session_i.hxx"
55 #include "Session_ServerLauncher.hxx"
56 #include "Session_ServerCheck.hxx"
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"
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
73 //! CORBA server for SALOME Session
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.
79 * Main services offered by the servant are:
81 * - stop Session ( must be idle )
85 PyObject* salome_shared_modules_module = 0;
87 void MessageOutput( QtMsgType type, const char* msg )
92 MESSAGE( "Debug: " << msg );
95 MESSAGE( "Warning: " << msg );
98 MESSAGE( "Fatal: " << msg );
104 static const char* pixmap_not_found_xpm[] = {
126 QString salomeVersion()
128 QString path( ::getenv( "GUI_ROOT_DIR" ) );
129 if ( !path.isEmpty() )
130 path += QDir::separator();
131 path += QString( "bin/salome/VERSION" );
134 if ( !vf.open( IO_ReadOnly ) )
135 return QString::null;
138 vf.readLine( line, 1024 );
141 if ( line.isEmpty() )
142 return QString::null;
144 while ( !line.isEmpty() && line.at( line.length() - 1 ) == QChar( '\n' ) )
145 line.remove( line.length() - 1, 1 );
148 int idx = line.findRev( ":" );
150 ver = line.mid( idx + 1 ).stripWhiteSpace();
155 class SALOME_ResourceMgr : public SUIT_ResourceMgr
158 SALOME_ResourceMgr( const QString& app, const QString& resVarTemplate ) : SUIT_ResourceMgr( app, resVarTemplate )
160 setCurrentFormat( "xml" );
161 setOption( "translators", QString( "%P_msg_%L.qm|%P_icons.qm|%P_images.qm" ) );
162 setDefaultPixmap( QPixmap( pixmap_not_found_xpm ) );
164 static void initResourceMgr()
166 if ( myExtAppName.isNull() || myExtAppVersion.isNull() ) {
167 SALOME_ResourceMgr resMgr( "SalomeApp", QString( "%1Config" ) );
168 resMgr.loadLanguage( "SalomeApp", "en" );
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();
181 QString version() const { return myExtAppVersion; }
184 QString userFileName( const QString& appName, const bool for_load ) const
186 if ( version().isNull() ) return "";
187 return SUIT_ResourceMgr::userFileName( myExtAppName, for_load );
190 virtual int userFileId( const QString& _fname ) const
192 QRegExp exp( "\\.SalomeApprc\\.([a-zA-Z0-9.]+)$" );
193 QRegExp vers_exp( "^([0-9]+)([A-Za-z]?)([0-9]*)$" );
195 QString fname = QFileInfo( _fname ).fileName();
196 if( exp.exactMatch( fname ) )
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 )
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();
209 int dev = dev1*100+dev2, id = major;
211 id*=100; id+=release;
220 static QString myExtAppName;
221 static QString myExtAppVersion;
224 QString SALOME_ResourceMgr::myExtAppName = QString::null;
225 QString SALOME_ResourceMgr::myExtAppVersion = QString::null;
227 class SALOME_Session : public SUIT_Session
230 SALOME_Session() : SUIT_Session() {}
231 virtual ~SALOME_Session() {}
234 virtual SUIT_ResourceMgr* createResourceMgr( const QString& appName ) const
236 SALOME_ResourceMgr::initResourceMgr();
237 SALOME_ResourceMgr* resMgr = new SALOME_ResourceMgr( appName, QString( "%1Config" ) );
242 class SALOME_QApplication : public QApplication
245 SALOME_QApplication( int& argc, char** argv ) : QApplication( argc, argv ), myHandler ( 0 ) {}
247 virtual bool notify( QObject* receiver, QEvent* e )
249 return myHandler ? myHandler->handle( receiver, e ) :
250 QApplication::notify( receiver, e );
252 SUIT_ExceptionHandler* handler() const { return myHandler; }
253 void setHandler( SUIT_ExceptionHandler* h ) { myHandler = h; }
256 SUIT_ExceptionHandler* myHandler;
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
264 GetInterfaceThread( SALOME::Session_var s ) : session ( s )
271 if ( !CORBA::is_nil( session ) )
272 session->GetInterface();
274 printf( "\nFATAL ERROR: SALOME::Session object is nil! Can not display GUI\n\n" );
277 SALOME::Session_var session;
280 // returns true if 'str' is found in argv
281 bool isFound( const char* str, int argc, char** argv )
283 for ( int i = 1; i <= ( argc-1 ); i++ )
284 if ( !strcmp( argv[i], str ) )
289 // ---------------------------- MAIN -----------------------
290 int main( int argc, char **argv )
292 // Install Qt debug messages handler
293 qInstallMsgHandler( MessageOutput );
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() ) ) );
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 );
304 // Set SALOME style to the application
305 _qappl.setStyle( "salome" );
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 );
332 if ( colors.count() > 0 ) c1 = QColor( colors[0] );
333 if ( colors.count() > 1 ) c2 = QColor( colors[1] );
334 splash->setTextColors( c1, c2 );
337 splash->setTextColors( Qt::white, Qt::black );
339 // ...set splash progress colors
340 if ( !splashProgressColors.isEmpty() ) {
341 QStringList colors = QStringList::split( "|", splashProgressColors );
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 );
349 // ...set splash text font
350 QFont f = splash->font();
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 );
362 // ...set 'hide on click' flag
364 splash->setHideOnClick( true );
368 qApp->processEvents();
376 PortableServer::POA_var poa;
378 SUIT_Session* aGUISession = 0;
379 SALOME_NamingService* _NS = 0;
380 GetInterfaceThread* guiThread = 0;
381 Session_ServerLauncher* myServerLauncher = 0;
384 // ...initialize Python (only once)
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" );
395 PyEval_ReleaseThread( KERNEL_PYTHON::_gtstate );
397 // ...create ORB, get RootPOA object, NamingService, etc.
398 ORB_INIT &init = *SINGLETON_<ORB_INIT>::Instance();
399 ASSERT( SINGLETON_<ORB_INIT>::IsAlreadyExisting() );
401 orb = init( orbArgc, argv );
403 // ...install SALOME thread event handler
404 SALOME_Event::GetSessionThread();
406 CORBA::Object_var obj = orb->resolve_initial_references( "RootPOA" );
407 poa = PortableServer::POA::_narrow( obj );
409 PortableServer::POAManager_var pman = poa->the_POAManager();
411 INFOS( "pman->activate()" );
413 _NS = new SALOME_NamingService( orb );
417 catch ( SALOME_Exception& e ) {
418 INFOS( "run(): SALOME::SALOME_Exception is caught: "<<e.what() );
420 catch ( CORBA::SystemException& e ) {
421 INFOS( "Caught CORBA::SystemException." );
423 catch ( CORBA::Exception& e ) {
424 INFOS( "Caught CORBA::Exception." );
427 CORBA::TypeCode_var tc = tmp.type();
428 const char *p = tc->name();
429 INFOS ( "run(): CORBA exception of the kind : "<<p<< " is caught" );
431 catch ( exception& e ) {
432 INFOS( "run(): An exception has been caught: " <<e.what() );
435 INFOS( "Caught unknown exception." );
438 QMutex _GUIMutex, _SessionMutex, _SplashMutex;
439 QWaitCondition _ServerLaunch, _SessionStarted, _SplashStarted;
441 // lock session mutex to ensure that GetInterface is not called
442 // until all initialization is done
443 _SessionMutex.lock();
446 // Start embedded servers launcher (Registry, SALOMEDS, etc.)
447 // ...lock mutex to block embedded servers launching thread until wait( mutex )
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 );
454 // Start servers check thread (splash)
456 // ...lock mutex to block splash thread until wait( mutex )
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..." ) );
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
478 // Launch GUI activator
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 );
490 // Allow multiple activation/deactivation of GUI
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
498 _SessionMutex.unlock();
500 // SUIT_Session creation
501 aGUISession = new SALOME_Session();
503 // Load SalomeApp dynamic library
504 INFOS( "creation SUIT_Application" );
505 SUIT_Application* aGUIApp = aGUISession->startApplication( "SalomeApp", 0, 0 );
508 if ( !isFound( "noexcepthandler", argc, argv ) )
509 _qappl.setHandler( aGUISession->handler() ); // after loading SalomeApp application
510 // aGUISession contains SalomeApp_ExceptionHandler
512 MESSAGE( "run(): starting the main event loop" );
515 splash->finish( aGUIApp->desktop() );
517 result = _qappl.exec();
523 if ( result == SUIT_Session::FROM_GUI ) // desktop is closed by user from GUI
530 // Prepare _GUIMutex for a new GUI activation
531 _SessionMutex.lock();
535 // unlock Session mutex
536 _SessionMutex.unlock();
538 if ( myServerLauncher )
539 myServerLauncher->KillAll(); // kill embedded servers
543 delete myServerLauncher;
546 LocalTraceBufferPool *bp1 = LocalTraceBufferPool::instance();
547 LocalTraceBufferPool::deleteInstance(bp1);