libyui  3.10.0
YUI.cc
1 /*
2  Copyright (C) 2000-2012 Novell, Inc
3  This library is free software; you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as
5  published by the Free Software Foundation; either version 2.1 of the
6  License, or (at your option) version 3.0 of the License. This library
7  is distributed in the hope that it will be useful, but WITHOUT ANY
8  WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10  License for more details. You should have received a copy of the GNU
11  Lesser General Public License along with this library; if not, write
12  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
13  Floor, Boston, MA 02110-1301 USA
14 */
15 
16 
17 /*-/
18 
19  File: YUI.cc
20 
21  Author: Stefan Hundhammer <sh@suse.de>
22 
23 /-*/
24 
25 
26 #define VERBOSE_COMM 0 // VERY verbose thread communication logging
27 
28 #include <stdio.h>
29 #include <string.h> // strerror()
30 #include <unistd.h> // pipe()
31 #include <fcntl.h> // fcntl()
32 #include <errno.h>
33 #include <pthread.h>
34 #include <stdlib.h> // getenv()
35 
36 #include <stack>
37 
38 #define YUILogComponent "ui"
39 #include "YUILog.h"
40 
41 #include "YUI.h"
42 #include "YUILoader.h"
43 #include "YUISymbols.h"
44 #include "YDialog.h"
45 #include "YApplication.h"
46 #include "YMacro.h"
47 #include "YButtonBox.h"
48 #include "YEnvVar.h"
49 #include "YBuiltinCaller.h"
50 #include "YWidgetID.h"
51 #include "YUIPlugin.h"
52 
53 
54 using std::string;
55 using std::stack;
56 
57 
58 // Environment variable to determine button order
59 // (set to "KDE" or "GNOME" - case insensitive)
60 #define ENV_BUTTON_ORDER "Y2_BUTTON_ORDER"
61 
62 // Keep dialog stack before the YUITerminator
63 // so that it is destroyed afterwards.
64 // YUITerminator deletes _yui which calls YUI::~YUI
65 // which accesses the dialog stack to remove all dialogs
66 stack<YDialog *> YDialog::_dialogStack;
67 YUI * YUI::_ui = 0;
68 
69 static bool uiDeleted = false;
70 
71 extern void * start_ui_thread( void * yui );
72 
73 
74 YUI::YUI( bool withThreads )
75  : _withThreads( withThreads )
76  , _uiThread( 0 )
77  , _builtinCaller( 0 )
78  , _terminate_ui_thread( false )
79  , _eventsBlocked( false )
80 {
81  yuiMilestone() << "This is libyui " << VERSION << endl;
82  yuiMilestone() << "Creating UI " << ( withThreads ? "with" : "without" ) << " threads" << endl;
83 
84  _ui = this;
85 }
86 
87 
89 {
90  if ( _ui )
91  {
92  if ( _withThreads && _uiThread )
93  {
94  yuiError() << "shutdownThreads() was never called!" << endl;
95  yuiError() << "shutting down now - this might segfault" << endl;
97  }
98 
99  if ( YDialog::openDialogsCount() > 0 )
100  yuiError() << YDialog::openDialogsCount() << " open dialogs left over" << endl;
101 
102  if ( _builtinCaller )
103  delete _builtinCaller;
104 
106 
109 
110  _ui = 0;
111  uiDeleted = true;
112  }
113 }
114 
115 
116 void
118 {
120 }
121 
122 
123 YUI *
125 {
126  ensureUICreated();
127  return _ui;
128 }
129 
130 
133 {
134  static YWidgetFactory * factory = 0;
135 
136  ensureUICreated();
137 
138  if ( ! factory )
139  factory = ui()->createWidgetFactory();
140 
141  YUI_CHECK_PTR( factory );
142  return factory;
143 }
144 
145 
148 {
149  static YOptionalWidgetFactory * factory = 0;
150 
151  ensureUICreated();
152 
153  if ( ! factory )
154  factory = ui()->createOptionalWidgetFactory();
155 
156  YUI_CHECK_PTR( factory );
157  return factory;
158 }
159 
160 
161 YApplication *
163 {
164  static YApplication * app = 0;
165 
166  ensureUICreated();
167 
168  if ( ! app )
169  app = ui()->createApplication();
170 
171  YUI_CHECK_PTR( app );
172  return app;
173 }
174 
175 
177 {
178  if ( _ui )
179  return;
180 
181  if ( uiDeleted )
182  YUI_THROW( YUIException( "UI already deleted" ) );
183 
185 }
186 
187 
189 {
190  // The ui thread must not be started before the constructor
191  // of the actual user interface is finished. Otherwise there
192  // is a race condition. The ui thread would go into idleLoop()
193  // before the ui is really setup. For example the Qt interface
194  // does a processOneEvent in the idleLoop(). This may do a
195  // repaint operation on the dialog that is just under construction!
196  //
197  // Therefore the creation of the thread is delayed and performed in this
198  // method. It must be called at the end of the constructor of the specific
199  // UI (the Qt UI or the NCurses UI).
200 
201  if ( _withThreads )
202  {
203  if ( pipe( pipe_from_ui ) == 0 &&
204  pipe( pipe_to_ui ) == 0 )
205  {
206 
207  // Make fd non blockable the ui thread reads from
208  long arg;
209  arg = fcntl( pipe_to_ui[0], F_GETFL );
210  if ( fcntl( pipe_to_ui[0], F_SETFL, arg | O_NONBLOCK ) < 0 )
211  {
212  yuiError() << "Couldn't set O_NONBLOCK: errno: " << errno << " " << strerror( errno ) << endl;
213  _withThreads = false;
214  close( pipe_to_ui[0] );
215  close( pipe_to_ui[1] );
216  close( pipe_from_ui[0] );
217  close( pipe_from_ui[1] );
218  }
219  else
220  {
221 #if VERBOSE_COMM
222  yuiDebug() << "Inter-thread communication pipes set up" << endl;
223 #endif
224  _terminate_ui_thread = false;
225  createUIThread();
226  }
227  }
228  else
229  {
230  yuiError() << "pipe() failed: errno: " << errno << " " << strerror( errno ) << endl;
231  exit(2);
232  }
233  }
234  else
235  {
236  yuiMilestone() << "Running without threads" << endl;
237  }
238 }
239 
240 
242 {
243  pthread_attr_t attr;
244  pthread_attr_init( & attr );
245  int ret = pthread_create( & _uiThread, & attr, start_ui_thread, this );
246 
247  if ( ret != 0 )
248  yuiError() << "pthread_create() failed: " << errno << " " << strerror( errno ) << endl;
249 }
250 
251 
253 {
254  yuiDebug() << "Sending shutdown message to UI thread" << endl;
255 
256  _terminate_ui_thread = true;
257  signalUIThread();
258  waitForUIThread();
259  pthread_join( _uiThread, 0 );
260 
261  yuiDebug() << "UI thread shut down correctly" << endl;
262 }
263 
264 
266 {
267  if ( _uiThread )
268  {
270  _uiThread = 0;
271  close( pipe_to_ui[0] );
272  close( pipe_to_ui[1] );
273  close( pipe_from_ui[0] );
274  close( pipe_from_ui[1] );
275  }
276 }
277 
278 
280 {
281  static char arbitrary = 42;
282  if ( write ( pipe_to_ui[1], & arbitrary, 1 ) == -1 )
283  yuiError() << "Writing byte to UI thread failed" << endl;
284 
285 #if VERBOSE_COMM
286  yuiDebug() << "Wrote byte to UI thread" << endl;
287 #endif
288 }
289 
290 
292 {
293  char arbitrary;
294  int result;
295 
296  do {
297 #if VERBOSE_COMM
298  yuiDebug() << "Waiting for ui thread..." << endl;
299 #endif
300  result = read( pipe_from_ui[0], & arbitrary, 1 );
301  if ( result == -1 )
302  {
303  if ( errno == EINTR || errno == EAGAIN )
304  continue;
305  else
306  yuiError() << "waitForUIThread: errno: " << errno << " " << strerror( errno ) << endl;
307  }
308  } while ( result == 0 );
309 
310 #if VERBOSE_COMM
311  yuiDebug() << "Read byte from ui thread" << endl;
312 #endif
313 
314  // return true if we really did get a signal byte
315  return result != -1;
316 }
317 
318 
320 {
321  static char arbitrary;
322  if ( write( pipe_from_ui[1], & arbitrary, 1 ) == -1 )
323  yuiError() << "Writing byte to YCP thread failed" << endl;
324 
325 #if VERBOSE_COMM
326  yuiDebug() << "Wrote byte to YCP thread" << endl;
327 #endif
328 }
329 
330 
332 {
333  char arbitrary;
334 
335  int result;
336  do {
337 #if VERBOSE_COMM
338  yuiDebug() << "Waiting for YCP thread..." << endl;
339 #endif
340  result = read( pipe_to_ui[0], & arbitrary, 1 );
341  if ( result == -1 )
342  {
343  if ( errno == EINTR || errno == EAGAIN )
344  continue;
345  else
346  yuiError() << "waitForYCPThread: errno: " << errno << " " << strerror( errno ) << endl;
347  }
348  } while ( result == 0 );
349 
350 #if VERBOSE_COMM
351  yuiDebug() << "Read byte from YCP thread" << endl;
352 #endif
353 
354  // return true if we really did get a signal byte
355  return result != -1;
356 }
357 
358 
360 {
361  while ( true )
362  {
363  idleLoop( pipe_to_ui[0] );
364 
365  // The pipe is non-blocking, so we have to check if we really read a
366  // signal byte. Although idleLoop already does a select(), this seems to
367  // be necessary. Anyway: Why do we set the pipe to non-blocking if we
368  // wait in idleLoop for it to become readable? It is needed in
369  // YUIQt::idleLoop for QSocketNotifier.
370 
371  if ( ! waitForYCPThread () )
372  continue;
373 
374  if ( _terminate_ui_thread )
375  {
377  signalYCPThread();
378  yuiDebug() << "Shutting down UI main loop" << endl;
379  return;
380  }
381 
382  if ( _builtinCaller )
383  _builtinCaller->call();
384  else
385  yuiError() << "No builtinCaller set" << endl;
386 
387  signalYCPThread();
388  }
389 }
390 
391 
393 {
394  YButtonOrder buttonOrder = YButtonBox::layoutPolicy().buttonOrder;
395  YButtonOrder oldButtonOrder = buttonOrder;
396 
397  YEnvVar lastEnv;
398 
399  //
400  // $DESKTOP_SESSION
401  //
402 
403  YEnvVar env( "DESKTOP_SESSION" );
404  yuiDebug() << env << endl;
405 
406  if ( env == "kde" ||
407  env == "xfce" )
408  {
409  buttonOrder = YKDEButtonOrder;
410  lastEnv = env;
411  }
412  else if ( env == "gnome" )
413  {
414  buttonOrder = YGnomeButtonOrder;
415  lastEnv = env;
416  }
417 
418  //
419  // $WINDOWMANAGER
420  //
421 
422  env = YEnvVar( "WINDOWMANAGER" );
423  yuiDebug() << env << endl;
424 
425  if ( env.contains( "gnome" ) )
426  {
427  buttonOrder = YGnomeButtonOrder;
428  lastEnv = env;
429  }
430  else if ( env.contains( "kde" ) )
431  {
432  buttonOrder = YKDEButtonOrder;
433  lastEnv = env;
434  }
435 
436 
437  //
438  // $Y2_BUTTON_ORDER
439  //
440 
441  env = YEnvVar( ENV_BUTTON_ORDER );
442  yuiDebug() << env << endl;
443 
444  if ( env == "gnome" )
445  {
446  buttonOrder = YGnomeButtonOrder;
447  lastEnv = env;
448  }
449  else if ( env == "kde" )
450  {
451  buttonOrder = YKDEButtonOrder;
452  lastEnv = env;
453  }
454  else if ( ! env.value().empty() )
455  {
456  yuiWarning() << "Ignoring unknown value of " << env << endl;
457  }
458 
459 
460  if ( buttonOrder != oldButtonOrder )
461  {
462  string buttonOrderStr;
463 
464  switch ( buttonOrder )
465  {
466  case YKDEButtonOrder:
467  buttonOrderStr = "KDE";
469  break;
470 
471  case YGnomeButtonOrder:
472  buttonOrderStr = "GNOME";
474  break;
475 
476  // Intentionally omitting "default" branch so GCC can catch unhandled enums
477  }
478 
479  yuiMilestone() << "Switching to " << buttonOrderStr
480  << " button order because of " << lastEnv
481  << endl;
482  }
483 }
484 
485 
486 YWidget *
487 YUI::sendWidgetID( const string & id_str )
488 {
489  yuiMilestone() << "Sending ID \"" << id_str << "\"" << endl;
490  YWidget * widget = 0;
491 
492  try
493  {
494  YDialog * dialog = YDialog::currentDialog(); // may throw
495  YStringWidgetID id( id_str );
496  widget = dialog->findWidget( &id );
497  widget->setKeyboardFocus();
498  }
499  catch ( YUINoDialogException & ex )
500  {
501  YUI_CAUGHT( ex );
502  }
503  // Cascading any YUIWidgetNotFoundException one level up
504 
505  return widget;
506 }
507 
508 
509 
510 
511 //
512 // ----------------------------------------------------------------------
513 //
514 
515 void * start_ui_thread( void * yui )
516 {
517  YUI * ui = (YUI *) yui;
518 
519 #if VERBOSE_COMM
520  yuiDebug() << "Starting UI thread" << endl;
521 #endif
522 
523  if ( ui )
524  ui->uiThreadMainLoop();
525  return 0;
526 }
527 
528 // EOF
YUI::pipe_from_ui
int pipe_from_ui[2]
Used to synchronize data transfer with the ui thread.
Definition: YUI.h:353
YButtonBox::gnomeLayoutPolicy
static YButtonBoxLayoutPolicy gnomeLayoutPolicy()
Predefined layout policy for GNOME-like behaviour.
Definition: YButtonBox.cc:111
YUINoDialogException
Definition: YUIException.h:467
YWidget
Abstract base class of all UI widgets.
Definition: YWidget.h:54
YDialog::deleteAllDialogs
static void deleteAllDialogs()
Delete all open dialogs.
Definition: YDialog.cc:570
YOptionalWidgetFactory
Abstract widget factory for optional ("special") widgets.
Definition: YOptionalWidgetFactory.h:56
YUI::~YUI
virtual ~YUI()
Destructor.
Definition: YUI.cc:88
YUI::terminateUIThread
void terminateUIThread()
Tells the ui thread that it should terminate and waits until it does so.
Definition: YUI.cc:252
YUI
Abstract base class of a libYUI user interface.
Definition: YUI.h:48
YWidget::id
YWidgetID * id() const
Returns this widget's ID.
Definition: YWidget.cc:355
YUI::ui
static YUI * ui()
Access the global UI.
Definition: YUI.cc:124
YUILoader::loadUI
static void loadUI(bool withThreads=false)
Load any of the available UI-plugins by this order and criteria:
Definition: YUILoader.cc:51
YUI::_terminate_ui_thread
bool _terminate_ui_thread
This is a flag that signals the ui thread that it should terminate.
Definition: YUI.h:361
YUI::shutdownThreads
void shutdownThreads()
Shut down multithreading.
Definition: YUI.cc:265
YButtonBox::setLayoutPolicy
static void setLayoutPolicy(const YButtonBoxLayoutPolicy &layoutPolicy)
Set the button policy for all future YButtonBox widgets: Button order, alignment if there is any exce...
Definition: YButtonBox.cc:83
YWidgetFactory
Abstract widget factory for mandatory widgets.
Definition: YWidgetFactory.h:78
YUI::setButtonOrderFromEnvironment
void setButtonOrderFromEnvironment()
Set the button order (in YButtonBox widgets) from environment variables:
Definition: YUI.cc:392
YWidget::findWidget
YWidget * findWidget(YWidgetID *id, bool doThrow=true) const
Recursively find a widget by its ID.
Definition: YWidget.cc:607
YUI::createOptionalWidgetFactory
virtual YOptionalWidgetFactory * createOptionalWidgetFactory()=0
Create the widget factory that provides all the createXY() methods for optional ("special") widgets a...
YUI::optionalWidgetFactory
static YOptionalWidgetFactory * optionalWidgetFactory()
Return the widget factory that provides all the createXY() methods for optional ("special") widgets a...
Definition: YUI.cc:147
YUI::sendWidgetID
YWidget * sendWidgetID(const std::string &id)
Send a widget ID.
Definition: YUI.cc:487
YUI::_builtinCaller
YBuiltinCaller * _builtinCaller
Inter-thread communication between the YCP thread and the UI thread: The YCP thread supplies data her...
Definition: YUI.h:339
YBuiltinCaller::call
virtual void call()=0
Call the built-in.
YMacro::deleteRecorder
static void deleteRecorder()
Delete the current macro recorder if there is one.
Definition: YMacro.cc:101
YUI::waitForYCPThread
bool waitForYCPThread()
Waits for the ycp thread to send one byte through the pipe to the ycp thread and reads this byte from...
Definition: YUI.cc:331
YApplication
Class for application-wide values and functions.
Definition: YApplication.h:45
YEnvVar::contains
bool contains(const std::string &str, bool caseSensitive=false) const
Return 'true' if the environment variable is set and the value contains 'str'.
Definition: YEnvVar.cc:68
YDialog::_dialogStack
static std::stack< YDialog * > _dialogStack
Stack holding all currently existing dialogs.
Definition: YDialog.h:442
YUI::waitForUIThread
bool waitForUIThread()
Waits for the ui thread to send one byte through the pipe to the ycp thread and reads this byte from ...
Definition: YUI.cc:291
YUI::createWidgetFactory
virtual YWidgetFactory * createWidgetFactory()=0
Create the widget factory that provides all the createXY() methods for standard (mandatory,...
YEnvVar
Helper class to represent an environment variable and its value.
Definition: YEnvVar.h:36
YUI::createApplication
virtual YApplication * createApplication()=0
Create the YApplication object that provides global methods.
YDialog::openDialogsCount
static int openDialogsCount()
Returns the number of currently open dialogs (from 1 on), i.e., the depth of the dialog stack.
Definition: YDialog.cc:601
YUI::signalUIThread
void signalUIThread()
Signals the ui thread by sending one byte through the pipe to it.
Definition: YUI.cc:279
YButtonBox::kdeLayoutPolicy
static YButtonBoxLayoutPolicy kdeLayoutPolicy()
Predefined layout policy for KDE-like behaviour.
Definition: YButtonBox.cc:97
YUI::createUIThread
void createUIThread()
Creates and launches the ui thread.
Definition: YUI.cc:241
YStringWidgetID
Simple widget ID class based on strings.
Definition: YWidgetID.h:72
YUI::topmostConstructorHasFinished
void topmostConstructorHasFinished()
Must be called after the constructor of the Qt/NCurses ui is ready.
Definition: YUI.cc:188
YUI::app
static YApplication * app()
Return the global YApplication object.
Definition: YUI.cc:162
YUI::idleLoop
virtual void idleLoop(int fd_ycp)=0
This virtual method is called when threads are activated in case the execution control is currently o...
YUI::uiThreadDestructor
virtual void uiThreadDestructor()
Destructor for the UI thread.
Definition: YUI.cc:117
YUI::YUI
YUI(bool withThreads)
Constructor.
Definition: YUI.cc:74
YUI::_withThreads
bool _withThreads
true if a seperate UI thread is created
Definition: YUI.h:325
YUI::ensureUICreated
static void ensureUICreated()
Make sure there is a UI (with a UI plug-in) created.
Definition: YUI.cc:176
YUI::widgetFactory
static YWidgetFactory * widgetFactory()
Return the widget factory that provides all the createXY() methods for standard (mandatory,...
Definition: YUI.cc:132
YMacro::deletePlayer
static void deletePlayer()
Delete the current macro player if there is one.
Definition: YMacro.cc:108
YButtonBox::layoutPolicy
static YButtonBoxLayoutPolicy layoutPolicy()
Return the layout policy.
Definition: YButtonBox.cc:90
YUI::_uiThread
pthread_t _uiThread
Handle to the ui thread.
Definition: YUI.h:330
YUIException
Base class for UI Exceptions.
Definition: YUIException.h:297
YWidget::setKeyboardFocus
virtual bool setKeyboardFocus()
Set the keyboard focus to this widget.
Definition: YWidget.cc:599
YDialog
A window in the desktop environment.
Definition: YDialog.h:47
YUI::signalYCPThread
void signalYCPThread()
Signals the ycp thread by sending one byte through the pipe to it.
Definition: YUI.cc:319
YEnvVar::value
std::string value() const
Return the value of the environment variable.
Definition: YEnvVar.h:59
YDialog::currentDialog
static YDialog * currentDialog(bool doThrow=true)
Return the current (topmost) dialog.
Definition: YDialog.cc:539
YUI::pipe_to_ui
int pipe_to_ui[2]
Used to synchronize data transfer with the ui thread.
Definition: YUI.h:346
YUI::uiThreadMainLoop
void uiThreadMainLoop()
This method implements the UI thread in case it is existing.
Definition: YUI.cc:359