libyui-ncurses  2.54.5
YNCursesUI.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: YNCursesUI.cc
20 
21  Author: Michael Andres <ma@suse.de>
22 
23 just to make the y2makepot script happy:
24 textdomain "ncurses"
25 
26 /-*/
27 
28 #include "YNCursesUI.h"
29 #include <string>
30 #include <sys/time.h>
31 #include <unistd.h>
32 #include <langinfo.h>
33 
34 #include <yui/YUI.h>
35 #include <yui/YEvent.h>
36 #include <yui/YDialog.h>
37 #include <yui/YCommandLine.h>
38 #include <yui/YButtonBox.h>
39 #include <yui/YMacro.h>
40 
41 #define YUILogComponent "ncurses"
42 #include <yui/YUILog.h>
43 
44 #include "NCstring.h"
45 #include "NCWidgetFactory.h"
46 #include "NCOptionalWidgetFactory.h"
47 #include "NCPackageSelectorPluginStub.h"
48 #include "NCPopupTextEntry.h"
49 #include "NCi18n.h"
50 
51 extern std::string language2encoding( std::string lang );
52 
54 
55 
56 YUI * createUI( bool withThreads )
57 {
58  if ( ! YNCursesUI::ui() )
59  new YNCursesUI( withThreads );
60 
61  return YNCursesUI::ui();
62 }
63 
64 YNCursesUI::YNCursesUI( bool withThreads, bool topmostConstructor )
65  : YUI( withThreads )
66 {
67  yuiMilestone() << "Start YNCursesUI" << std::endl;
68  _ui = this;
69 
70  if ( getenv( "LANG" ) != NULL )
71  {
72  setlocale ( LC_CTYPE, "" );
73  std::string language = getenv( "LANG" );
74  std::string encoding = nl_langinfo( CODESET );
75  yuiMilestone() << "getenv LANG: " << language << " encoding: " << encoding << std::endl;
76 
77  // Explicitly set LC_CTYPE so that it won't be changed if setenv( LANG ) is called elsewhere.
78  // (it's not enough to call setlocale( LC_CTYPE, .. ), set env. variable LC_CTYPE!)
79  std::string locale = setlocale( LC_CTYPE, NULL );
80  setenv( "LC_CTYPE", locale.c_str(), 1 );
81  yuiMilestone() << "setenv LC_CTYPE: " << locale << " encoding: " << encoding << std::endl;
82 
83  // The encoding of a terminal (xterm, konsole etc.) can never change; the encoding
84  // of the "real" console is changed in setConsoleFont().
85  NCstring::setTerminalEncoding( encoding );
86  app()->setLanguage( language, encoding );
87  }
88 
89  YButtonBoxMargins buttonBoxMargins;
90  buttonBoxMargins.left = 1;
91  buttonBoxMargins.right = 1;
92  buttonBoxMargins.top = 1;
93  buttonBoxMargins.bottom = 0;
94  buttonBoxMargins.spacing = 1;
95  buttonBoxMargins.helpButtonExtraSpacing = 3;
96  YButtonBox::setDefaultMargins( buttonBoxMargins );
97 
98  try
99  {
100  NCurses::init();
101  }
102  catch ( NCursesError & err )
103  {
104  yuiMilestone() << err << std::endl;
105  ::endwin();
106  abort();
107  }
108 
109  if ( topmostConstructor ) {
110  yuiDebug() << "YNCursesUI is the top most constructor" << std::endl;
111  topmostConstructorHasFinished();
112  }
113 }
114 
115 
117 {
118  //delete left-over dialogs (if any)
119  YDialog::deleteAllDialogs();
120  yuiMilestone() << "Stop YNCursesUI" << std::endl;
121 }
122 
123 
124 YWidgetFactory *
126 {
127  NCWidgetFactory * factory = new NCWidgetFactory();
128  YUI_CHECK_NEW( factory );
129 
130  return factory;
131 }
132 
133 
134 YOptionalWidgetFactory *
136 {
138  YUI_CHECK_NEW( factory );
139 
140  return factory;
141 }
142 
143 
144 YApplication *
145 YNCursesUI::createApplication()
146 {
147  NCApplication * app = new NCApplication();
148  YUI_CHECK_NEW( app );
149 
150  return app;
151 }
152 
153 
154 void YNCursesUI::idleLoop( int fd_ycp )
155 {
156 
157  int timeout = 5;
158 
159  struct timeval tv;
160  fd_set fdset;
161  int retval;
162 
163  do
164  {
165  tv.tv_sec = timeout;
166  tv.tv_usec = 0;
167 
168  FD_ZERO( &fdset );
169  FD_SET( 0, &fdset );
170  FD_SET( fd_ycp, &fdset );
171 
172  retval = select( fd_ycp + 1, &fdset, 0, 0, &tv );
173 
174  if ( retval < 0 )
175  {
176  if ( errno != EINTR )
177  yuiError() << "idleLoop error in select() (" << errno << ')' << std::endl;
178  }
179  else if ( retval != 0 )
180  {
181  //do not throw here, as current dialog may not necessarily exist yet
182  //if we have threads
183  YDialog *currentDialog = YDialog::currentDialog( false );
184 
185  if ( currentDialog )
186  {
187  NCDialog * ncd = static_cast<NCDialog *>( currentDialog );
188 
189  if ( ncd )
190  {
191  extern NCBusyIndicator* NCBusyIndicatorObject;
192 
193  if ( NCBusyIndicatorObject )
194  NCBusyIndicatorObject->handler( 0 );
195 
196  ncd->idleInput();
197  }
198  }
199  } // else no input within timeout sec.
200  }
201  while ( !FD_ISSET( fd_ycp, &fdset ) );
202 }
203 
204 
205 /**
206  * Create the package selector plugin
207  **/
209 {
210  static NCPackageSelectorPluginStub * plugin = 0;
211 
212  if ( ! plugin )
213  {
214  plugin = new NCPackageSelectorPluginStub();
215 
216  // This is a deliberate memory leak: If an application requires a
217  // PackageSelector, it is a package selection application by
218  // definition. In this case, the ncurses_pkg plugin is intentionally
219  // kept open to avoid repeated start-up cost of the plugin and libzypp.
220  }
221 
222  return plugin;
223 }
224 
225 
226 YEvent * YNCursesUI::runPkgSelection( YWidget * selector )
227 {
228  YEvent * event = 0;
229 
230  YDialog *dialog = YDialog::currentDialog();
232 
233  if ( !dialog )
234  {
235  yuiError() << "ERROR package selection: No dialog rexisting." << std::endl;
236  return 0;
237  }
238 
239  if ( !selector )
240  {
241  yuiError() << "ERROR package selection: No package selector existing." << std::endl;
242  return 0;
243  }
244 
245  // debug: dump the widget tree
246  dialog->dumpDialogWidgetTree();
247 
248  if ( plugin )
249  {
250  event = plugin->runPkgSelection( dialog, selector );
251  }
252 
253  return event;
254 }
255 
256 
257 void YNCursesUI::init_title()
258 {
259  // Fetch command line args
260  YCommandLine cmdline;
261 
262  //
263  // Retrieve program name from command line
264  //
265 
266  std::string progName = YUILog::basename( cmdline[0] );
267 
268  if ( progName == "y2base" )
269  {
270  progName = "YaST2";
271 
272  // Special case for YaST2: argv[1] is the module name -
273  // this is what we want to display in the window title
274  //
275  // '/usr/lib/whatever/y2base' 'module_name' 'selected_ui'
276  // (e.g. 'y2base' 'lan' 'ncurses') -> we need 'lan'
277 
278  if ( cmdline.size() > 1 )
279  progName += " - " + cmdline[1];
280  }
281 
282  if ( progName.find( "lt-" ) == 0 ) // progName starts with "lt-"
283  {
284  // Remove leading "lt-" from libtool-generated binaries
285  progName = progName.substr( sizeof( "lt-" ) - 1 );
286  }
287 
288 
289  //
290  // Retrieve host name (if set)
291  //
292 
293  std::string hostName;
294 
295  char hostNameBuffer[ 256 ];
296 
297  if ( gethostname( hostNameBuffer, sizeof( hostNameBuffer ) - 1 ) != -1 )
298  {
299  // gethostname() might have messed up - yet another POSIX standard that
300  // transfers the burden of doing things right to the application
301  // programmer: Possibly no null byte
302 
303  hostNameBuffer[ sizeof( hostNameBuffer ) -1 ] = '\0';
304  hostName = hostNameBuffer;
305  }
306 
307  if ( hostName == "(none)" )
308  hostName = "";
309 
310  //
311  // Build and set window title
312  //
313 
314  std::string windowTitle = progName;
315 
316  if ( ! hostName.empty() )
317  windowTitle += " @ " + hostName;
318 
319  NCurses::SetTitle( windowTitle );
320 }
321 
322 
323 bool YNCursesUI::want_colors()
324 {
325  if ( getenv( "Y2NCURSES_BW" ) != NULL )
326  {
327  yuiMilestone() << "Y2NCURSES_BW is std::set - won't use colors" << std::endl;
328  return false;
329  }
330 
331  return true;
332 }
333 
334 
335 /**
336  * Set the console font, encoding etc.
337  * This is called from Console.ycp.
338  * The terminal encoding must be std::set correctly.
339  *
340  * This doesn't belong here, but it is so utterly entangled with member
341  * variables that are not exported at all (sic!) that it's not really feasible
342  * to extract the relevant parts.
343  **/
344 void YNCursesUI::setConsoleFont( const std::string & console_magic,
345  const std::string & font,
346  const std::string & screen_map,
347  const std::string & unicode_map,
348  const std::string & lang )
349 {
350  std::string cmd( "setfont" );
351  cmd += " -C " + myTerm;
352  cmd += " " + font;
353 
354  if ( !screen_map.empty() )
355  cmd += " -m " + screen_map;
356 
357  if ( !unicode_map.empty() )
358  cmd += " -u " + unicode_map;
359 
360  yuiMilestone() << cmd << std::endl;
361 
362  int ret = system(( cmd + " >/dev/null 2>&1" ).c_str() );
363 
364  // setfont returns error if called e.g. on a xterm -> return
365  if ( ret )
366  {
367  yuiError() << cmd.c_str() << " returned " << ret << std::endl;
368  Refresh();
369  return;
370  }
371 
372  // go on in case of a "real" console
373  cmd = "(echo -en \"\\033";
374 
375  if ( console_magic.length() )
376  cmd += console_magic;
377  else
378  cmd += "(B";
379 
380  cmd += "\" >" + myTerm + ")";
381 
382  yuiMilestone() << cmd << std::endl;
383 
384  ret = system(( cmd + " >/dev/null 2>&1" ).c_str() );
385 
386  if ( ret )
387  {
388  yuiError() << cmd.c_str() << " returned " << ret << std::endl;
389  }
390 
391  // set terminal encoding for console
392  // (setConsoleFont() in Console.ycp has passed the encoding as last
393  // argument but this encoding was not correct; now Console.ycp passes the
394  // language) if the encoding is NOT UTF-8 set the console encoding
395  // according to the language
396 
397  if ( NCstring::terminalEncoding() != "UTF-8" )
398  {
399  std::string language = lang;
400  std::string::size_type pos = language.find( '.' );
401 
402  if ( pos != std::string::npos )
403  {
404  language.erase( pos );
405  }
406 
407  pos = language.find( '_' );
408 
409  if ( pos != std::string::npos )
410  {
411  language.erase( pos );
412  }
413 
414  std::string code = language2encoding( language );
415 
416  yuiMilestone() << "setConsoleFont( ENCODING: " << code << " )" << std::endl;
417 
418  if ( NCstring::setTerminalEncoding( code ) )
419  {
420  Redraw();
421  }
422  else
423  {
424  Refresh();
425  }
426  }
427  else
428  {
429  Refresh();
430  }
431 }
432 
433 
435 {
436  std::string id = NCPopupTextEntry::askForText( wpos( 0, 0 ),
437  _("Enter Widget ID:"), // label
438  "" ); // initial text
439 
440  if ( ! id.empty() )
441  {
442  try
443  {
444  return sendWidgetID( id );
445  }
446  catch ( YUIWidgetNotFoundException & ex )
447  {
448  YUI_CAUGHT( ex );
449  }
450  }
451 
452  return 0;
453 }
454 
456 {
457  // do not send anything if the events are globally blocked
458  // i.e. allow masking the events caused by changes from the code
459  if (eventsBlocked())
460  {
461 #if VERBOSE_EVENTS
462  yuiDebug() << "Events blocked, ignoring event " << event << std::endl;
463 #endif
464  return;
465  }
466 
467  NCDialog *dialog = dynamic_cast<NCDialog *>(NCDialog::currentDialog(false)); // don't throw
468 
469  if (dialog)
470  {
471  yuiDebug() << "Sending event: " << event << std::endl;
472  dialog->setPendingEvent(event);
473  }
474  else
475  {
476  yuiError() << "No dialog" << std::endl;
477  }
478 }
static YNCursesUI * _ui
Global reference to the UI.
Definition: YNCursesUI.h:86
virtual YWidgetFactory * createWidgetFactory()
Create the widget factory that provides all the createXY() methods for standard (mandatory, i.e.
Definition: YNCursesUI.cc:125
virtual void setConsoleFont(const std::string &console_magic, const std::string &font, const std::string &screen_map, const std::string &unicode_map, const std::string &lang)
Set the (text) console font according to the current encoding etc.
Definition: YNCursesUI.cc:344
Widget factory for optional ("special") widgets.
virtual YOptionalWidgetFactory * createOptionalWidgetFactory()
Create the widget factory that provides all the createXY() methods for optional ("special") widgets a...
Definition: YNCursesUI.cc:135
YWidget * askSendWidgetID()
Open a pop-up dialog to ask the user for a widget ID and then send it with sendWidgetID().
Definition: YNCursesUI.cc:434
Concrete widget factory for mandatory widgets.
static YNCursesUI * ui()
Access the global Y2NCursesUI.
Definition: YNCursesUI.h:93
void sendEvent(NCursesEvent event)
Send an event to the UI.
Definition: YNCursesUI.cc:455
Definition: position.h:109
void handler(int sig_num)
handler, called by NCBusyIndicatorHandlerWrapper
YNCursesUI(bool withThreads, bool topmostConstructor=true)
Having boolean topmostConstructor to be called only when there is topmost constructor and not a plugi...
Definition: YNCursesUI.cc:64
virtual void idleLoop(int fd_ycp)
Idle around until fd_ycp is readable.
Definition: YNCursesUI.cc:154
virtual YEvent * runPkgSelection(YDialog *currentDialog, YWidget *packageSelector)
Fills the PackageSelector widget (runs the package selection).
NCPackageSelectorPluginStub * packageSelectorPlugin()
Returns the package selector plugin singleton of this UI or creates it (including loading the plugin ...
Definition: YNCursesUI.cc:208
virtual YEvent * runPkgSelection(YWidget *packageSelector)
Fills the PackageSelector widget and runs package selection.
Definition: YNCursesUI.cc:226
~YNCursesUI()
Destructor.
Definition: YNCursesUI.cc:116