blob: cf52de5bff67fd45697b954fb5e8a716684ee443 [file] [log] [blame]
Pierre Ossman5156d5e2011-03-09 09:42:34 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
DRCb65bb932011-06-24 03:17:00 +00003 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
Pierre Ossman5156d5e2011-03-09 09:42:34 +00004 *
5 * This is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20
Peter Åstrandc359f362011-08-23 12:04:46 +000021#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
Pierre Ossman5156d5e2011-03-09 09:42:34 +000025#include <string.h>
26#include <stdio.h>
Pierre Ossman2a7a8d62013-02-15 08:33:39 +000027#include <ctype.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000028#include <stdlib.h>
29#include <errno.h>
30#include <signal.h>
31#include <locale.h>
32#include <sys/stat.h>
33
34#ifdef WIN32
35#include <direct.h>
36#define mkdir(path, mode) _mkdir(path)
37#endif
38
Pierre Ossman6b9622d2014-07-21 16:42:12 +020039#if !defined(WIN32) && !defined(__APPLE__)
40#include <X11/Xlib.h>
41#include <X11/XKBlib.h>
42#endif
43
Pierre Ossman5156d5e2011-03-09 09:42:34 +000044#include <rfb/Logger_stdio.h>
45#include <rfb/SecurityClient.h>
46#include <rfb/Security.h>
47#ifdef HAVE_GNUTLS
48#include <rfb/CSecurityTLS.h>
49#endif
50#include <rfb/LogWriter.h>
51#include <rfb/Timer.h>
Peter Åstrand8a2b0812012-08-08 11:49:01 +000052#include <rfb/Exception.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000053#include <network/TcpSocket.h>
DRC4426f002011-10-12 20:02:55 +000054#include <os/os.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000055
56#include <FL/Fl.H>
57#include <FL/Fl_Widget.H>
Pierre Ossman8eb35082012-03-27 12:50:54 +000058#include <FL/Fl_PNG_Image.H>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000059#include <FL/fl_ask.H>
Pierre Ossmanb8858222011-04-29 11:51:38 +000060#include <FL/x.H>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000061
62#include "i18n.h"
63#include "parameters.h"
64#include "CConn.h"
Pierre Ossman561ff0c2011-05-13 14:04:59 +000065#include "ServerDialog.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000066#include "UserDialog.h"
Adam Tkac8ac4b302013-01-23 13:55:46 +000067#include "vncviewer.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000068
DRCb65bb932011-06-24 03:17:00 +000069#ifdef WIN32
Pierre Ossman8eb35082012-03-27 12:50:54 +000070#include "resource.h"
DRCb65bb932011-06-24 03:17:00 +000071#include "win32.h"
72#endif
73
Pierre Ossman5156d5e2011-03-09 09:42:34 +000074rfb::LogWriter vlog("main");
75
76using namespace network;
77using namespace rfb;
78using namespace std;
79
Pierre Ossman5945d732014-09-22 13:13:15 +020080static const char _aboutText[] = N_("TigerVNC Viewer %d-bit v%s\n"
81 "Built on: %s\n"
Pierre Ossman34e12982014-09-22 12:59:38 +020082 "Copyright (C) 1999-%d TigerVNC Team and many others (see README.txt)\n"
Pierre Ossmandc96cb42014-09-22 12:19:26 +020083 "See http://www.tigervnc.org for information on TigerVNC.");
84static char aboutText[1024];
85
Adam Tkac8ac4b302013-01-23 13:55:46 +000086char vncServerName[VNCSERVERNAMELEN] = { '\0' };
87
Pierre Ossman5156d5e2011-03-09 09:42:34 +000088static bool exitMainloop = false;
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000089static const char *exitError = NULL;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000090
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000091void exit_vncviewer(const char *error)
Pierre Ossman5156d5e2011-03-09 09:42:34 +000092{
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000093 // Prioritise the first error we get as that is probably the most
94 // relevant one.
95 if ((error != NULL) && (exitError == NULL))
96 exitError = strdup(error);
97
Pierre Ossman5156d5e2011-03-09 09:42:34 +000098 exitMainloop = true;
99}
100
Pierre Ossmanb8858222011-04-29 11:51:38 +0000101void about_vncviewer()
102{
103 fl_message_title(_("About TigerVNC Viewer"));
Pierre Ossmandc96cb42014-09-22 12:19:26 +0200104 fl_message("%s", aboutText);
Pierre Ossmanb8858222011-04-29 11:51:38 +0000105}
106
107static void about_callback(Fl_Widget *widget, void *data)
108{
109 about_vncviewer();
110}
111
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000112static void CleanupSignalHandler(int sig)
113{
114 // CleanupSignalHandler allows C++ object cleanup to happen because it calls
115 // exit() rather than the default which is to abort.
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200116 vlog.info(_("CleanupSignalHandler called"));
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000117 exit(1);
118}
119
120static void init_fltk()
121{
122 // Basic text size (10pt @ 96 dpi => 13px)
123 FL_NORMAL_SIZE = 13;
124
125#ifndef __APPLE__
126 // Select a FLTK scheme and background color that looks somewhat
127 // close to modern Linux and Windows.
128 Fl::scheme("gtk+");
129 Fl::background(220, 220, 220);
130#else
131 // On Mac OS X there is another scheme that fits better though.
132 Fl::scheme("plastic");
133#endif
134
Henrik Andersson3b837032011-09-19 13:46:55 +0000135 // Proper Gnome Shell integration requires that we set a sensible
136 // WM_CLASS for the window.
137 Fl_Window::default_xclass("vncviewer");
138
Pierre Ossman8eb35082012-03-27 12:50:54 +0000139 // Set the default icon for all windows.
140#ifdef HAVE_FLTK_ICONS
141#ifdef WIN32
142 HICON lg, sm;
143
144 lg = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
145 IMAGE_ICON, GetSystemMetrics(SM_CXICON),
146 GetSystemMetrics(SM_CYICON),
147 LR_DEFAULTCOLOR | LR_SHARED);
148 sm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
149 IMAGE_ICON, GetSystemMetrics(SM_CXSMICON),
150 GetSystemMetrics(SM_CYSMICON),
151 LR_DEFAULTCOLOR | LR_SHARED);
152
153 Fl_Window::default_icons(lg, sm);
154#elif ! defined(__APPLE__)
155 const int icon_sizes[] = {48, 32, 24, 16};
156
157 Fl_PNG_Image *icons[4];
158 int count;
159
160 count = 0;
161
162 // FIXME: Follow icon theme specification
163 for (size_t i = 0;i < sizeof(icon_sizes)/sizeof(icon_sizes[0]);i++) {
164 char icon_path[PATH_MAX];
165 bool exists;
166
Pierre Ossman6a007bd2012-09-11 10:56:21 +0000167 sprintf(icon_path, "%s/icons/hicolor/%dx%d/apps/tigervnc.png",
Pierre Ossman8eb35082012-03-27 12:50:54 +0000168 DATA_DIR, icon_sizes[i], icon_sizes[i]);
169
170#ifndef WIN32
171 struct stat st;
172 if (stat(icon_path, &st) != 0)
173#else
174 struct _stat st;
175 if (_stat(icon_path, &st) != 0)
176 return(false);
177#endif
178 exists = false;
179 else
180 exists = true;
181
182 if (exists) {
183 icons[count] = new Fl_PNG_Image(icon_path);
184 if (icons[count]->w() == 0 ||
185 icons[count]->h() == 0 ||
186 icons[count]->d() != 4) {
187 delete icons[count];
188 continue;
189 }
190
191 count++;
192 }
193 }
194
195 Fl_Window::default_icons((const Fl_RGB_Image**)icons, count);
196
197 for (int i = 0;i < count;i++)
198 delete icons[i];
199#endif
200#endif // FLTK_HAVE_ICONS
201
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000202 // This makes the "icon" in dialogs rounded, which fits better
203 // with the above schemes.
204 fl_message_icon()->box(FL_UP_BOX);
205
206 // Turn off the annoying behaviour where popups track the mouse.
207 fl_message_hotspot(false);
208
209 // Avoid empty titles for popups
210 fl_message_title_default(_("TigerVNC Viewer"));
211
212#ifdef WIN32
213 // Most "normal" Windows apps use this font for UI elements.
214 Fl::set_font(FL_HELVETICA, "Tahoma");
215#endif
216
217 // FLTK exposes these so that we can translate them.
218 fl_no = _("No");
219 fl_yes = _("Yes");
220 fl_ok = _("OK");
221 fl_cancel = _("Cancel");
222 fl_close = _("Close");
Pierre Ossmanb8858222011-04-29 11:51:38 +0000223
224#ifdef __APPLE__
Peter Åstrandb7c55242011-08-29 13:14:51 +0000225 /* Needs trailing space */
226 static char fltk_about[16];
227 snprintf(fltk_about, sizeof(fltk_about), "%s ", _("About"));
228 Fl_Mac_App_Menu::about = fltk_about;
229 static char fltk_hide[16];
230 snprintf(fltk_hide, sizeof(fltk_hide), "%s ", _("Hide"));
231 Fl_Mac_App_Menu::hide = fltk_hide;
232 static char fltk_quit[16];
233 snprintf(fltk_quit, sizeof(fltk_quit), "%s ", _("Quit"));
234 Fl_Mac_App_Menu::quit = fltk_quit;
235
Pierre Ossman41ba6032011-06-16 11:09:31 +0000236 Fl_Mac_App_Menu::print = ""; // Don't want the print item
237 Fl_Mac_App_Menu::services = _("Services");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000238 Fl_Mac_App_Menu::hide_others = _("Hide Others");
239 Fl_Mac_App_Menu::show = _("Show All");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000240
Pierre Ossmanb8858222011-04-29 11:51:38 +0000241 fl_mac_set_about(about_callback, NULL);
242#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000243}
244
245static void mkvnchomedir()
246{
247 // Create .vnc in the user's home directory if it doesn't already exist
248 char* homeDir = NULL;
249
250 if (getvnchomedir(&homeDir) == -1) {
251 vlog.error(_("Could not create VNC home directory: can't obtain home "
252 "directory path."));
253 } else {
254 int result = mkdir(homeDir, 0755);
255 if (result == -1 && errno != EEXIST)
256 vlog.error(_("Could not create VNC home directory: %s."), strerror(errno));
257 delete [] homeDir;
258 }
259}
260
261static void usage(const char *programName)
262{
263 fprintf(stderr,
264 "\nusage: %s [parameters] [host:displayNum] [parameters]\n"
265 " %s [parameters] -listen [port] [parameters]\n",
266 programName, programName);
267 fprintf(stderr,"\n"
268 "Parameters can be turned on with -<param> or off with -<param>=0\n"
269 "Parameters which take a value can be specified as "
270 "-<param> <value>\n"
271 "Other valid forms are <param>=<value> -<param>=<value> "
272 "--<param>=<value>\n"
273 "Parameter names are case-insensitive. The parameters are:\n\n");
274 Configuration::listParams(79, 14);
275 exit(1);
276}
277
Adam Tkac8ac4b302013-01-23 13:55:46 +0000278#ifndef WIN32
279static int
280interpretViaParam(char *remoteHost, int *remotePort, int localPort)
281{
282 const int SERVER_PORT_OFFSET = 5900;
283 char *pos = strchr(vncServerName, ':');
284 if (pos == NULL)
285 *remotePort = SERVER_PORT_OFFSET;
286 else {
287 int portOffset = SERVER_PORT_OFFSET;
288 size_t len;
289 *pos++ = '\0';
290 len = strlen(pos);
291 if (*pos == ':') {
292 /* Two colons is an absolute port number, not an offset. */
293 pos++;
294 len--;
295 portOffset = 0;
296 }
297 if (!len || strspn (pos, "-0123456789") != len )
298 return 1;
299 *remotePort = atoi(pos) + portOffset;
300 }
301
302 if (*vncServerName != '\0')
303 strncpy(remoteHost, vncServerName, VNCSERVERNAMELEN);
304 else
305 strncpy(remoteHost, "localhost", VNCSERVERNAMELEN);
306
307 remoteHost[VNCSERVERNAMELEN - 1] = '\0';
308
309 snprintf(vncServerName, VNCSERVERNAMELEN, "localhost::%d", localPort);
310 vncServerName[VNCSERVERNAMELEN - 1] = '\0';
311 vlog.error(vncServerName);
312
313 return 0;
314}
315
316static void
317createTunnel(const char *gatewayHost, const char *remoteHost,
318 int remotePort, int localPort)
319{
Pierre Ossmanf8d525b2014-07-09 17:02:27 +0200320 const char *cmd = getenv("VNC_VIA_CMD");
321 char *cmd2, *percent;
Adam Tkac8ac4b302013-01-23 13:55:46 +0000322 char lport[10], rport[10];
323 sprintf(lport, "%d", localPort);
324 sprintf(rport, "%d", remotePort);
325 setenv("G", gatewayHost, 1);
326 setenv("H", remoteHost, 1);
327 setenv("R", rport, 1);
328 setenv("L", lport, 1);
Adam Tkac8ac4b302013-01-23 13:55:46 +0000329 if (!cmd)
330 cmd = "/usr/bin/ssh -f -L \"$L\":\"$H\":\"$R\" \"$G\" sleep 20";
331 /* Compatibility with TigerVNC's method. */
Pierre Ossmanf8d525b2014-07-09 17:02:27 +0200332 cmd2 = strdup(cmd);
333 while ((percent = strchr(cmd2, '%')) != NULL)
Adam Tkac8ac4b302013-01-23 13:55:46 +0000334 *percent = '$';
Pierre Ossmanf8d525b2014-07-09 17:02:27 +0200335 system(cmd2);
336 free(cmd2);
Adam Tkac8ac4b302013-01-23 13:55:46 +0000337}
338
339static int mktunnel()
340{
341 const char *gatewayHost;
342 char remoteHost[VNCSERVERNAMELEN];
343 int localPort = findFreeTcpPort();
344 int remotePort;
345
346 gatewayHost = strDup(via.getValueStr());
347 if (interpretViaParam(remoteHost, &remotePort, localPort) != 0)
348 return 1;
349 createTunnel(gatewayHost, remoteHost, remotePort, localPort);
350
351 return 0;
352}
353#endif /* !WIN32 */
354
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000355int main(int argc, char** argv)
356{
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000357 UserDialog dlg;
358
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000359 setlocale(LC_ALL, "");
Pierre Ossman0878eca2012-03-27 13:03:22 +0000360 bindtextdomain(PACKAGE_NAME, LOCALE_DIR);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000361 textdomain(PACKAGE_NAME);
362
Pierre Ossmandc96cb42014-09-22 12:19:26 +0200363 // Generate the about string now that we get the proper translation
364 snprintf(aboutText, sizeof(aboutText), _aboutText,
Pierre Ossman5945d732014-09-22 13:13:15 +0200365 (int)sizeof(size_t)*8, PACKAGE_VERSION,
366 BUILD_TIMESTAMP, 2014);
Pierre Ossmandc96cb42014-09-22 12:19:26 +0200367
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000368 rfb::SecurityClient::setDefaults();
369
370 // Write about text to console, still using normal locale codeset
Pierre Ossmandc96cb42014-09-22 12:19:26 +0200371 fprintf(stderr,"\n%s\n", aboutText);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000372
373 // Set gettext codeset to what our GUI toolkit uses. Since we are
374 // passing strings from strerror/gai_strerror to the GUI, these must
375 // be in GUI codeset as well.
376 bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
377 bind_textdomain_codeset("libc", "UTF-8");
378
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000379 rfb::initStdIOLoggers();
Pierre Ossmanc8719ad2012-04-26 14:27:52 +0000380#ifdef WIN32
381 rfb::initFileLogger("C:\\temp\\vncviewer.log");
382#else
383 rfb::initFileLogger("/tmp/vncviewer.log");
384#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000385 rfb::LogWriter::setLogParams("*:stderr:30");
386
387#ifdef SIGHUP
388 signal(SIGHUP, CleanupSignalHandler);
389#endif
390 signal(SIGINT, CleanupSignalHandler);
391 signal(SIGTERM, CleanupSignalHandler);
392
393 init_fltk();
394
Pierre Ossman6b9622d2014-07-21 16:42:12 +0200395#if !defined(WIN32) && !defined(__APPLE__)
396 fl_open_display();
397 XkbSetDetectableAutoRepeat(fl_display, True, NULL);
398#endif
399
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000400 Configuration::enableViewerParams();
401
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000402 /* Load the default parameter settings */
403 const char* defaultServerName;
404 try {
405 defaultServerName = loadViewerParameters(NULL);
406 } catch (rfb::Exception& e) {
407 fl_alert("%s", e.str());
408 }
409
DRC5aa06502011-06-23 22:04:46 +0000410 int i = 1;
411 if (!Fl::args(argc, argv, i) || i < argc)
412 for (; i < argc; i++) {
413 if (Configuration::setParam(argv[i]))
414 continue;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000415
DRC5aa06502011-06-23 22:04:46 +0000416 if (argv[i][0] == '-') {
417 if (i+1 < argc) {
418 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
419 i++;
420 continue;
421 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000422 }
DRC5aa06502011-06-23 22:04:46 +0000423 usage(argv[0]);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000424 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000425
Adam Tkac8ac4b302013-01-23 13:55:46 +0000426 strncpy(vncServerName, argv[i], VNCSERVERNAMELEN);
427 vncServerName[VNCSERVERNAMELEN - 1] = '\0';
DRC5aa06502011-06-23 22:04:46 +0000428 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000429
430 if (!::autoSelect.hasBeenSet()) {
431 // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000432 if (::preferredEncoding.hasBeenSet() || ::fullColour.hasBeenSet() ||
433 ::fullColourAlias.hasBeenSet()) {
434 ::autoSelect.setParam(false);
435 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000436 }
437 if (!::fullColour.hasBeenSet() && !::fullColourAlias.hasBeenSet()) {
438 // Default to FullColor=0 if AutoSelect=0 && LowColorLevel is set
439 if (!::autoSelect && (::lowColourLevel.hasBeenSet() ||
440 ::lowColourLevelAlias.hasBeenSet())) {
441 ::fullColour.setParam(false);
442 }
443 }
444 if (!::customCompressLevel.hasBeenSet()) {
445 // Default to CustomCompressLevel=1 if CompressLevel is used.
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000446 if(::compressLevel.hasBeenSet()) {
447 ::customCompressLevel.setParam(true);
448 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000449 }
450
451 mkvnchomedir();
452
453 CSecurity::upg = &dlg;
454#ifdef HAVE_GNUTLS
455 CSecurityTLS::msg = &dlg;
456#endif
457
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000458 Socket *sock = NULL;
459
DRC3e7ed812013-02-26 10:34:22 +0000460#ifndef WIN32
Adam Tkac571089b2013-02-19 14:30:32 +0000461 /* Specifying -via and -listen together is nonsense */
462 if (listenMode && strlen(via.getValueStr()) > 0) {
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200463 vlog.error(_("Parameters -listen and -via are incompatible"));
464 fl_alert(_("Parameters -listen and -via are incompatible"));
Adam Tkac571089b2013-02-19 14:30:32 +0000465 exit_vncviewer();
466 return 1;
467 }
DRC3e7ed812013-02-26 10:34:22 +0000468#endif
Adam Tkac571089b2013-02-19 14:30:32 +0000469
470 if (listenMode) {
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000471 try {
472 int port = 5500;
473 if (isdigit(vncServerName[0]))
474 port = atoi(vncServerName);
475
476 TcpListener listener(NULL, port);
477
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200478 vlog.info(_("Listening on port %d\n"), port);
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000479 sock = listener.accept();
480 } catch (rdr::Exception& e) {
481 vlog.error("%s", e.str());
482 fl_alert("%s", e.str());
483 exit_vncviewer();
484 return 1;
485 }
486
487 } else {
488 if (vncServerName[0] == '\0') {
489 ServerDialog::run(defaultServerName, vncServerName);
490 if (vncServerName[0] == '\0')
491 return 1;
492 }
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000493
Adam Tkac8ac4b302013-01-23 13:55:46 +0000494#ifndef WIN32
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000495 if (strlen (via.getValueStr()) > 0 && mktunnel() != 0)
496 usage(argv[0]);
Adam Tkac8ac4b302013-01-23 13:55:46 +0000497#endif
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000498 }
Adam Tkac8ac4b302013-01-23 13:55:46 +0000499
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000500 CConn *cc = new CConn(vncServerName, sock);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000501
502 while (!exitMainloop) {
503 int next_timer;
504
505 next_timer = Timer::checkTimeouts();
506 if (next_timer == 0)
507 next_timer = INT_MAX;
508
509 if (Fl::wait((double)next_timer / 1000.0) < 0.0) {
510 vlog.error(_("Internal FLTK error. Exiting."));
511 break;
512 }
513 }
514
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000515 delete cc;
516
517 if (exitError != NULL)
Pierre Ossmanf52740e2012-04-25 15:43:56 +0000518 fl_alert("%s", exitError);
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000519
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000520 return 0;
521}