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