libyui  3.0.10
/usr/src/RPM/BUILD/libyui-3.0.10/src/YUI.cc
00001 /*
00002   Copyright (C) 2000-2012 Novell, Inc
00003   This library is free software; you can redistribute it and/or modify
00004   it under the terms of the GNU Lesser General Public License as
00005   published by the Free Software Foundation; either version 2.1 of the
00006   License, or (at your option) version 3.0 of the License. This library
00007   is distributed in the hope that it will be useful, but WITHOUT ANY
00008   WARRANTY; without even the implied warranty of MERCHANTABILITY or
00009   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
00010   License for more details. You should have received a copy of the GNU
00011   Lesser General Public License along with this library; if not, write
00012   to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
00013   Floor, Boston, MA 02110-1301 USA
00014 */
00015 
00016 
00017 /*-/
00018 
00019   File:         YUI.cc
00020 
00021   Author:       Stefan Hundhammer <sh@suse.de>
00022 
00023 /-*/
00024 
00025 
00026 #define VERBOSE_COMM 0  // VERY verbose thread communication logging
00027 
00028 #include <stdio.h>
00029 #include <string.h>     // strerror()
00030 #include <unistd.h>     // pipe()
00031 #include <fcntl.h>      // fcntl()
00032 #include <errno.h>
00033 #include <pthread.h>
00034 #include <stdlib.h>     // getenv()
00035 
00036 #include <stack>
00037 
00038 #define YUILogComponent "ui"
00039 #include "YUILog.h"
00040 
00041 #include "YUI.h"
00042 #include "YUILoader.h"
00043 #include "YUISymbols.h"
00044 #include "YDialog.h"
00045 #include "YApplication.h"
00046 #include "YMacro.h"
00047 #include "YButtonBox.h"
00048 #include "YEnvVar.h"
00049 #include "YBuiltinCaller.h"
00050 
00051 using std::endl;
00052 
00053 // Environment variable to determine button order
00054 // (set to "KDE" or "GNOME" - case insensitive)
00055 #define ENV_BUTTON_ORDER "Y2_BUTTON_ORDER"
00056 
00057 // Keep dialog stack before the YUITerminator
00058 // so that it is destroyed afterwards.
00059 // YUITerminator deletes _yui which calls YUI::~YUI
00060 // which accesses the dialog stack to remove all dialogs
00061 std::stack<YDialog *> YDialog::_dialogStack;
00062 YUI * YUI::_ui = 0;
00063 
00064 static bool uiDeleted = false;
00065 
00066 extern void * start_ui_thread( void * yui );
00067 
00068 
00069 YUI::YUI( bool withThreads )
00070     : _withThreads( withThreads )
00071     , _uiThread( 0 )
00072     , _builtinCaller( 0 )
00073     , _terminate_ui_thread( false )
00074     , _eventsBlocked( false )
00075 {
00076     yuiMilestone() << "This is libyui " << VERSION << std::endl;
00077     yuiMilestone() << "Creating UI " << ( withThreads ? "with" : "without" ) << " threads" << endl;
00078     _ui = this;
00079 }
00080 
00081 
00082 YUI::~YUI()
00083 {
00084     if ( _ui )
00085     {
00086         if ( _withThreads && _uiThread )
00087         {
00088             yuiError() << "shutdownThreads() was never called!"     << endl;
00089             yuiError() << "shutting down now - this might segfault" << endl;
00090             shutdownThreads();
00091         }
00092 
00093         if ( YDialog::openDialogsCount() > 0 )
00094             yuiError() << YDialog::openDialogsCount() << " open dialogs left over" << endl;
00095 
00096         if ( _builtinCaller )
00097             delete _builtinCaller;
00098 
00099         YDialog::deleteAllDialogs();
00100 
00101         YMacro::deleteRecorder();
00102         YMacro::deletePlayer();
00103 
00104         _ui = 0;
00105         uiDeleted = true;
00106     }
00107 }
00108 
00109 
00110 void
00111 YUI::uiThreadDestructor()
00112 {
00113     YDialog::deleteAllDialogs();
00114 }
00115 
00116 
00117 YUI *
00118 YUI::ui()
00119 {
00120     ensureUICreated();
00121     return _ui;
00122 }
00123 
00124 
00125 YWidgetFactory *
00126 YUI::widgetFactory()
00127 {
00128     static YWidgetFactory * factory = 0;
00129 
00130     ensureUICreated();
00131 
00132     if ( ! factory )
00133         factory = ui()->createWidgetFactory();
00134 
00135     YUI_CHECK_PTR( factory );
00136     return factory;
00137 }
00138 
00139 
00140 YOptionalWidgetFactory *
00141 YUI::optionalWidgetFactory()
00142 {
00143     static YOptionalWidgetFactory * factory = 0;
00144 
00145     ensureUICreated();
00146 
00147     if ( ! factory )
00148         factory = ui()->createOptionalWidgetFactory();
00149 
00150     YUI_CHECK_PTR( factory );
00151     return factory;
00152 }
00153 
00154 
00155 YApplication *
00156 YUI::app()
00157 {
00158     static YApplication * app = 0;
00159 
00160     ensureUICreated();
00161 
00162     if ( ! app )
00163         app = ui()->createApplication();
00164 
00165     YUI_CHECK_PTR( app );
00166     return app;
00167 }
00168 
00169 
00170 void YUI::ensureUICreated()
00171 {
00172     if ( _ui )
00173         return;
00174 
00175     if ( uiDeleted )
00176         YUI_THROW( YUIException( "UI already deleted" ) );
00177 
00178     YUILoader::loadUI();
00179 }
00180 
00181 
00182 void YUI::topmostConstructorHasFinished()
00183 {
00184     // The ui thread must not be started before the constructor
00185     // of the actual user interface is finished. Otherwise there
00186     // is a race condition. The ui thread would go into idleLoop()
00187     // before the ui is really setup. For example the Qt interface
00188     // does a processOneEvent in the idleLoop(). This may do a
00189     // repaint operation on the dialog that is just under construction!
00190     //
00191     // Therefore the creation of the thread is delayed and performed in this
00192     // method. It must be called at the end of the constructor of the specific
00193     // UI (the Qt UI or the NCurses UI).
00194 
00195     if ( _withThreads )
00196     {
00197         if ( pipe( pipe_from_ui ) == 0 &&
00198              pipe( pipe_to_ui   ) == 0   )
00199         {
00200 
00201             // Make fd non blockable the ui thread reads from
00202             long arg;
00203             arg = fcntl( pipe_to_ui[0], F_GETFL );
00204             if ( fcntl( pipe_to_ui[0], F_SETFL, arg | O_NONBLOCK ) < 0 )
00205             {
00206                 yuiError() << "Couldn't set O_NONBLOCK: errno: " << errno << " " << strerror( errno ) << endl;
00207                 _withThreads = false;
00208                 close( pipe_to_ui[0] );
00209                 close( pipe_to_ui[1] );
00210                 close( pipe_from_ui[0] );
00211                 close( pipe_from_ui[1] );
00212             }
00213             else
00214             {
00215 #if VERBOSE_COMM
00216                 yuiDebug() << "Inter-thread communication pipes set up" << endl;
00217 #endif
00218                 _terminate_ui_thread = false;
00219                 createUIThread();
00220             }
00221         }
00222         else
00223         {
00224             yuiError() << "pipe() failed: errno: " << errno << " " << strerror( errno ) << endl;
00225             exit(2);
00226         }
00227     }
00228     else
00229     {
00230         yuiMilestone() << "Running without threads" << endl;
00231     }
00232 }
00233 
00234 
00235 void YUI::createUIThread()
00236 {
00237     pthread_attr_t attr;
00238     pthread_attr_init( & attr );
00239     int ret = pthread_create( & _uiThread, & attr, start_ui_thread, this );
00240 
00241     if ( ret != 0 )
00242         yuiError() << "pthread_create() failed: " << errno << " " << strerror( errno ) << endl;
00243 }
00244 
00245 
00246 void YUI::terminateUIThread()
00247 {
00248     yuiDebug() << "Sending shutdown message to UI thread" << endl;
00249 
00250     _terminate_ui_thread = true;
00251     signalUIThread();
00252     waitForUIThread();
00253     pthread_join( _uiThread, 0 );
00254 
00255     yuiDebug() << "UI thread shut down correctly" << endl;
00256 }
00257 
00258 
00259 void YUI::shutdownThreads()
00260 {
00261     if ( _uiThread )
00262     {
00263         terminateUIThread();
00264         _uiThread = 0;
00265         close( pipe_to_ui[0] );
00266         close( pipe_to_ui[1] );
00267         close( pipe_from_ui[0] );
00268         close( pipe_from_ui[1] );
00269     }
00270 }
00271 
00272 
00273 void YUI::signalUIThread()
00274 {
00275     static char arbitrary = 42;
00276     if ( write ( pipe_to_ui[1], & arbitrary, 1 ) == -1 )
00277         yuiError() <<  "Writing byte to UI thread failed" << endl;
00278 
00279 #if VERBOSE_COMM
00280     yuiDebug() << "Wrote byte to UI thread" << endl;
00281 #endif
00282 }
00283 
00284 
00285 bool YUI::waitForUIThread()
00286 {
00287     char arbitrary;
00288     int result;
00289 
00290     do {
00291 #if VERBOSE_COMM
00292         yuiDebug() << "Waiting for ui thread..." << endl;
00293 #endif
00294         result = read( pipe_from_ui[0], & arbitrary, 1 );
00295         if ( result == -1 )
00296         {
00297             if ( errno == EINTR || errno == EAGAIN )
00298                 continue;
00299             else
00300                 yuiError() <<  "waitForUIThread: errno: " << errno << " " << strerror( errno ) << endl;
00301         }
00302     } while ( result == 0 );
00303 
00304 #if VERBOSE_COMM
00305     yuiDebug() << "Read byte from ui thread" << endl;
00306 #endif
00307 
00308     // return true if we really did get a signal byte
00309     return result != -1;
00310 }
00311 
00312 
00313 void YUI::signalYCPThread()
00314 {
00315     static char arbitrary;
00316     if ( write( pipe_from_ui[1], & arbitrary, 1 ) == -1 )
00317         yuiError() <<  "Writing byte to YCP thread failed" << endl;
00318 
00319 #if VERBOSE_COMM
00320     yuiDebug() << "Wrote byte to YCP thread" << endl;
00321 #endif
00322 }
00323 
00324 
00325 bool YUI::waitForYCPThread()
00326 {
00327     char arbitrary;
00328 
00329     int result;
00330     do {
00331 #if VERBOSE_COMM
00332         yuiDebug() << "Waiting for YCP thread..." << endl;
00333 #endif
00334         result = read( pipe_to_ui[0], & arbitrary, 1 );
00335         if ( result == -1 )
00336         {
00337             if ( errno == EINTR || errno == EAGAIN )
00338                 continue;
00339             else
00340                 yuiError() << "waitForYCPThread: errno: " << errno << " " << strerror( errno ) << endl;
00341         }
00342     } while ( result == 0 );
00343 
00344 #if VERBOSE_COMM
00345     yuiDebug() << "Read byte from YCP thread" << endl;
00346 #endif
00347 
00348     // return true if we really did get a signal byte
00349     return result != -1;
00350 }
00351 
00352 
00353 void YUI::uiThreadMainLoop()
00354 {
00355     while ( true )
00356     {
00357         idleLoop( pipe_to_ui[0] );
00358 
00359         // The pipe is non-blocking, so we have to check if we really read a
00360         // signal byte. Although idleLoop already does a select(), this seems to
00361         // be necessary.  Anyway: Why do we set the pipe to non-blocking if we
00362         // wait in idleLoop for it to become readable? It is needed in
00363         // YUIQt::idleLoop for QSocketNotifier.
00364 
00365         if ( ! waitForYCPThread () )
00366             continue;
00367 
00368         if ( _terminate_ui_thread )
00369         {
00370             uiThreadDestructor();
00371             signalYCPThread();
00372             yuiDebug() << "Shutting down UI main loop" << endl;
00373             return;
00374         }
00375 
00376         if ( _builtinCaller )
00377             _builtinCaller->call();
00378         else
00379             yuiError() << "No builtinCaller set" << endl;
00380 
00381         signalYCPThread();
00382     }
00383 }
00384 
00385 
00386 void YUI::setButtonOrderFromEnvironment()
00387 {
00388     YButtonOrder buttonOrder    = YButtonBox::layoutPolicy().buttonOrder;
00389     YButtonOrder oldButtonOrder = buttonOrder;
00390 
00391     YEnvVar lastEnv;
00392 
00393     //
00394     // $DESKTOP_SESSION
00395     //
00396 
00397     YEnvVar env( "DESKTOP_SESSION" );
00398     yuiDebug() << env << endl;
00399 
00400     if ( env == "kde" ||
00401          env == "xfce"  )
00402     {
00403         buttonOrder = YKDEButtonOrder;
00404         lastEnv     = env;
00405     }
00406     else if ( env == "gnome" )
00407     {
00408         buttonOrder = YGnomeButtonOrder;
00409         lastEnv     = env;
00410     }
00411 
00412     //
00413     // $WINDOWMANAGER
00414     //
00415 
00416     env = YEnvVar( "WINDOWMANAGER" );
00417     yuiDebug() << env << endl;
00418 
00419     if ( env.contains( "gnome" ) )
00420     {
00421         buttonOrder = YGnomeButtonOrder;
00422         lastEnv     = env;
00423     }
00424     else if ( env.contains( "kde" ) )
00425     {
00426         buttonOrder = YKDEButtonOrder;
00427         lastEnv     = env;
00428     }
00429 
00430 
00431     //
00432     // $Y2_BUTTON_ORDER
00433     //
00434 
00435     env = YEnvVar( ENV_BUTTON_ORDER );
00436     yuiDebug() << env << endl;
00437 
00438     if ( env == "gnome" )
00439     {
00440         buttonOrder = YGnomeButtonOrder;
00441         lastEnv     = env;
00442     }
00443     else if ( env == "kde" )
00444     {
00445         buttonOrder = YKDEButtonOrder;
00446         lastEnv     = env;
00447     }
00448     else if ( ! env.value().empty() )
00449     {
00450         yuiWarning() << "Ignoring unknown value of " << env << endl;
00451     }
00452 
00453 
00454     if ( buttonOrder != oldButtonOrder )
00455     {
00456         std::string buttonOrderStr;
00457 
00458         switch ( buttonOrder )
00459         {
00460             case YKDEButtonOrder:
00461                 buttonOrderStr = "KDE";
00462                 YButtonBox::setLayoutPolicy( YButtonBox::kdeLayoutPolicy() );
00463                 break;
00464 
00465             case YGnomeButtonOrder:
00466                 buttonOrderStr = "GNOME";
00467                 YButtonBox::setLayoutPolicy( YButtonBox::gnomeLayoutPolicy() );
00468                 break;
00469 
00470             // Intentionally omitting "default" branch so GCC can catch unhandled enums
00471         }
00472 
00473         yuiMilestone() << "Switching to " << buttonOrderStr
00474                        << " button order because of " << lastEnv
00475                        << endl;
00476     }
00477 }
00478 
00479 
00480 //
00481 // ----------------------------------------------------------------------
00482 //
00483 
00484 void * start_ui_thread( void * yui )
00485 {
00486     YUI * ui = (YUI *) yui;
00487 
00488 #if VERBOSE_COMM
00489     yuiDebug() << "Starting UI thread" << endl;
00490 #endif
00491 
00492     if ( ui )
00493         ui->uiThreadMainLoop();
00494     return 0;
00495 }
00496 
00497 
00498 //
00499 // ----------------------------------------------------------------------
00500 //
00501 
00502 
00503 /**
00504  * Helper class to make sure the UI is properly shut down.
00505  **/
00506 class YUITerminator
00507 {
00508 public:
00509     YUITerminator() {}
00510 
00511     /**
00512      * Destructor.
00513      *
00514      * If there still is a UI, it will be deleted.
00515      * If there is none, this will do nothing.
00516      **/
00517     ~YUITerminator();
00518 };
00519 
00520 
00521 YUITerminator::~YUITerminator()
00522 {
00523     if ( YUI::_ui )
00524     {
00525         yuiMilestone() << "Shutting down UI" << endl;
00526         delete YUI::_ui;
00527 
00528         YUI::_ui = 0;
00529     }
00530 }
00531 
00532 
00533 /**
00534  * Static YUITerminator instance: It will make sure the UI is deleted in its
00535  * global destructor. If the UI is already destroyed, it will do nothing. If
00536  * there still is a UI object, it will be deleted.
00537  *
00538  * This is particularly important for the NCurses UI so the terminal settings
00539  * are properly restored.
00540  **/
00541 static YUITerminator uiTerminator;
00542 
00543 
00544 
00545 // EOF
 All Classes Functions Variables Enumerations Friends