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