Salome HOME
368b63c8df0c6bdd714010e1e41cc5cfc84c2399
[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 //  $Header$
28
29 #include <Container_init_python.hxx>
30 #include "Utils_ORB_INIT.hxx"
31 #include "Utils_SINGLETON.hxx"
32 #include "SALOME_NamingService.hxx"
33 #include "SALOMETraceCollector.hxx"
34
35 #include "InquireServersQThread.h" // splash
36
37 #include <iostream>
38 #include <unistd.h>
39
40 #include <qdir.h>
41 #include <qfile.h>
42 #include <qapplication.h>
43 #include <qwaitcondition.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
57 #include "SUIT_Session.h"
58 #include "SUIT_Application.h"
59 #include "SUIT_MessageBox.h"
60 #include "SUIT_Tools.h"
61
62
63 #include "SUIT_ExceptionHandler.h"
64 using namespace std;
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 QString salomeVersion()
87 {
88   QString path( ::getenv( "GUI_ROOT_DIR" ) );
89   if ( !path.isEmpty() )
90     path += QDir::separator();
91   path += QString( "bin/salome/VERSION" );
92
93   QFile vf( path );
94   if ( !vf.open( IO_ReadOnly ) )
95     return QString::null;
96
97   QString line;
98   vf.readLine( line, 1024 );
99   vf.close();
100
101   if ( line.isEmpty() )
102     return QString::null;
103
104   while ( !line.isEmpty() && line.at( line.length() - 1 ) == QChar( '\n' ) )
105     line.remove( line.length() - 1, 1 );
106
107   QString ver;
108   int idx = line.findRev( ":" );
109   if ( idx != -1 )
110     ver = line.mid( idx + 1 ).stripWhiteSpace();
111
112   return ver;
113 }
114
115 PyObject *salome_shared_modules_module = NULL;
116
117 void MessageOutput( QtMsgType type, const char *msg )
118 {
119   switch ( type ) {
120   case QtDebugMsg:
121     MESSAGE( "Debug: " << msg );
122     break;
123   case QtWarningMsg:
124     MESSAGE( "Warning: " << msg );
125     break;
126   case QtFatalMsg:
127     MESSAGE( "Fatal: " << msg );
128     break;
129   }
130 }
131
132 class SALOME_Session : public SUIT_Session
133 {
134 public:
135   SALOME_Session() : SUIT_Session() {}
136   virtual ~SALOME_Session() {}
137
138 protected:
139   virtual SUIT_ResourceMgr* createResourceMgr( const QString& appName ) const
140   {
141     SUIT_ResourceMgr* resMgr = new SUIT_ResourceMgr( appName, QString( "%1Config" ) );
142     resMgr->setVersion( salomeVersion() );
143     resMgr->setCurrentFormat( "xml" );
144     resMgr->setOption( "translators", QString( "%P_msg_%L.qm|%P_icons.qm|%P_images.qm" ) );
145     return resMgr;
146   }
147 };
148
149 class SALOME_QApplication : public QApplication 
150 {
151 public:
152   SALOME_QApplication( int& argc, char** argv ) : QApplication( argc, argv ) 
153   {
154     myHandler = 0;
155   }
156   
157   virtual bool notify( QObject* receiver, QEvent* e ) 
158   {
159     return myHandler ? myHandler->handle( receiver, e ) :
160                        QApplication::notify( receiver, e );
161   }
162   SUIT_ExceptionHandler*  handler() const { return myHandler; }
163   void setHandler( SUIT_ExceptionHandler* h ) { myHandler = h; }
164
165 private:
166   SUIT_ExceptionHandler*  myHandler;
167 };
168
169 // class which calls SALOME::Session::GetInterface() from another thread
170 // to avoid mutual lock (if called from the same thread as main()
171 class GetInterfaceThread : public QThread
172 {
173 public: 
174   GetInterfaceThread( SALOME::Session_var s ) : session ( s ) {}
175 protected:
176   virtual void run()
177   {
178     if ( !CORBA::is_nil( session ) )
179       session->GetInterface(); 
180     else
181       printf( "\nFATAL ERROR: SALOME::Session object is nil!  Can not display GUI\n\n" );
182   }
183 private:
184   SALOME::Session_var session;
185 };
186
187 // returns true if 'str' is found in argv
188 bool isFound( const char* str, int argc, char** argv )
189 {
190   for ( int i = 1; i <= (argc-1); i++ )
191     if ( !strcmp( argv[i], str ) )
192       return true;
193   return false;
194 }
195
196 // ---------------------------- MAIN -----------------------
197 int main(int argc, char **argv)
198 {
199   qInstallMsgHandler( MessageOutput );
200
201   // QApplication should be create before all other operations
202   // When uses QApplication::libraryPaths() (example, QFile::encodeName())
203   // qApp used for detection of the executable dir path.
204   SALOME_QApplication _qappl( argc, argv );
205   ASSERT( QObject::connect(&_qappl, SIGNAL( lastWindowClosed() ), &_qappl, SLOT( quit() ) ) );
206   QString path( ::getenv( "GUI_ROOT_DIR" ) );
207   if ( !path.isEmpty() )
208     path += QDir::separator();
209   path += QString( "bin/salome" );
210   _qappl.addLibraryPath( path );
211   _qappl.setStyle( "salome" );
212
213   /*
214     Python initialisation : only once
215   */
216
217   int _argc = 1;
218   char* _argv[] = {""};
219   KERNEL_PYTHON::init_python(_argc,_argv);
220   PyEval_RestoreThread(KERNEL_PYTHON::_gtstate);
221   if ( !KERNEL_PYTHON::salome_shared_modules_module ) // import only once
222   {
223     KERNEL_PYTHON::salome_shared_modules_module =
224       PyImport_ImportModule("salome_shared_modules");
225   }
226   if ( !KERNEL_PYTHON::salome_shared_modules_module )
227   {
228     INFOS("salome_shared_modules_module == NULL");
229     PyErr_Print();
230     PyErr_Clear();
231   }
232   PyEval_ReleaseThread(KERNEL_PYTHON::_gtstate);
233
234   int result = -1;
235
236   // 1. create ORB, get RootPOA object, NamingService, etc.
237   ORB_INIT &init = *SINGLETON_<ORB_INIT>::Instance() ;
238   ASSERT(SINGLETON_<ORB_INIT>::IsAlreadyExisting()) ;
239   int orbArgc = 1;
240   CORBA::ORB_var &orb = init( orbArgc , argv ) ;
241   SALOMETraceCollector *myThreadTrace = SALOMETraceCollector::instance(orb);
242   GetInterfaceThread* guiThread = 0;
243
244   try
245   {
246     SALOME_Event::GetSessionThread();
247
248     CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
249     PortableServer::POA_var poa = PortableServer::POA::_narrow(obj);
250
251     PortableServer::POAManager_var pman = poa->the_POAManager() ;
252     pman->activate() ;
253     INFOS("pman->activate()");
254
255     SALOME_NamingService *_NS = new SALOME_NamingService(orb);
256
257     // CORBA Servant Launcher
258     QMutex _GUIMutex ;
259     QWaitCondition _ServerLaunch;
260     _GUIMutex.lock();     // to block Launch server thread until wait(mutex)
261     
262     // 2. activate embedded CORBA servers: Registry, SALOMEDS, etc.
263     Session_ServerLauncher* myServerLauncher
264       = new Session_ServerLauncher(argc, argv, orb, poa, &_GUIMutex, &_ServerLaunch);
265     myServerLauncher->start();
266     
267     _ServerLaunch.wait(&_GUIMutex); // to be reseased by Launch server thread when ready:
268     // show splash screen if "SPLASH" parameter was passed (default)
269     if ( isFound( "SPLASH", argc, argv ) ) 
270     {
271       // create temporary resource manager just to load splash icon
272       SUIT_ResourceMgr resMgr( "SalomeApp", QString( "%1Config" ) );
273       resMgr.setVersion( salomeVersion() );
274       resMgr.setCurrentFormat( "xml" );
275       resMgr.loadLanguage( "SalomeApp", "en" );
276       // create splash object: widget (splash with progress bar) and "pinging" thread
277       InquireServersGUI splash;
278       splash.setPixmap( resMgr.loadPixmap( "SalomeApp", QObject::tr( "ABOUT" ) ) );
279       SUIT_Tools::centerWidget( &splash, _qappl.desktop() );
280
281       _qappl.setMainWidget( &splash );
282       QObject::connect( &_qappl, SIGNAL( lastWindowClosed() ), &_qappl, SLOT( quit() ) );
283       splash.show(); // display splash with running progress bar 
284       _qappl.exec(); // wait untill splash closes (progress runs till end or Cancel is pressed)
285
286       //int q = 0;
287       //while ( q++ < 10 )
288       //  sleep( 1 );
289     
290       if ( splash.getExitStatus() ) // 1 is error
291         exit( splash.getExitStatus() ); // quit applicaiton
292     }
293
294     // call Session::GetInterface() if "GUI" parameter was passed (default) 
295     if ( isFound( "GUI", argc, argv ) ) 
296     {
297       CORBA::Object_var obj = _NS->Resolve("/Kernel/Session");
298       SALOME::Session_var session = SALOME::Session::_narrow( obj ) ;
299       ASSERT (! CORBA::is_nil( session ) );
300
301       INFOS("Session activated, Launch IAPP...");
302       guiThread = new GetInterfaceThread( session );
303       guiThread->start();
304     }
305
306     // 3. GUI activation
307     // Allow multiple activation/deactivation of GUI
308     while ( 1 ) 
309     {
310       MESSAGE("waiting wakeAll()");
311       _ServerLaunch.wait(&_GUIMutex); // to be reseased by Launch server thread when ready:
312       // atomic operation lock - unlock on mutex
313       // unlock mutex: serverThread runs, calls  _ServerLaunch->wakeAll()
314       // this thread wakes up, and lock mutex
315             
316       _GUIMutex.unlock();
317
318       // 3.1 SUIT_Session creation      
319       SUIT_Session* aGUISession = new SALOME_Session();
320       INFOS("creation SUIT_Application");
321       
322       SCRUTE(_NS);
323       
324       // 3.3 run GUI loop
325       // T2.12 - catch exceptions thrown on attempts to modified a locked study
326       MESSAGE("run(): starting the main event loop");
327       
328       // 3.2 load SalomeApp dynamic library
329       SUIT_Application* aGUIApp = aGUISession->startApplication( "SalomeApp", 0, 0 );
330       if ( aGUIApp ) 
331       {
332         _qappl.setHandler( aGUISession->handler() ); // after loading SalomeApp application
333                                                      // aGUISession contains SalomeApp_ExceptionHandler
334         result = _qappl.exec();
335
336         if ( result == SUIT_Session::FROM_GUI ) // desktop is closed by user from GUI
337           break;
338       }
339
340       // Prepare _GUIMutex for a new GUI activation
341       _GUIMutex.lock(); 
342     }
343
344     //orb->shutdown(0);
345     myServerLauncher->KillAll();  // kill embedded servers
346   }
347   catch (SALOME_Exception& e)
348   {
349     INFOS("run(): SALOME::SALOME_Exception is caught: "<<e.what());
350   }
351   catch (CORBA::SystemException& e)
352   {
353     INFOS("Caught CORBA::SystemException.");
354   }
355   catch (CORBA::Exception& e)
356   {
357     INFOS("Caught CORBA::Exception.");
358     CORBA::Any tmp;
359     tmp<<= e;
360     CORBA::TypeCode_var tc = tmp.type();
361     const char *p = tc->name();
362     INFOS ("run(): CORBA exception of the kind : "<<p<< " is caught");
363   }
364   catch(exception& e)
365   {
366     INFOS("run(): An exception has been caught: " <<e.what());
367   }
368   catch (...)
369   {
370     INFOS("Caught unknown exception.");
371   }
372   delete myThreadTrace;
373   if ( guiThread )
374     delete guiThread;
375   return result ;
376 }