blob: a512213b529ff59387b66de1f3f15b07b1e4f21b [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>
27#include <stdlib.h>
28#include <errno.h>
29#include <signal.h>
30#include <locale.h>
31#include <sys/stat.h>
32
33#ifdef WIN32
34#include <direct.h>
35#define mkdir(path, mode) _mkdir(path)
36#endif
37
Pierre Ossman5156d5e2011-03-09 09:42:34 +000038#include <rfb/Logger_stdio.h>
39#include <rfb/SecurityClient.h>
40#include <rfb/Security.h>
41#ifdef HAVE_GNUTLS
42#include <rfb/CSecurityTLS.h>
43#endif
44#include <rfb/LogWriter.h>
45#include <rfb/Timer.h>
Peter Åstrand8a2b0812012-08-08 11:49:01 +000046#include <rfb/Exception.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000047#include <network/TcpSocket.h>
DRC4426f002011-10-12 20:02:55 +000048#include <os/os.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000049
50#include <FL/Fl.H>
51#include <FL/Fl_Widget.H>
Pierre Ossman8eb35082012-03-27 12:50:54 +000052#include <FL/Fl_PNG_Image.H>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000053#include <FL/fl_ask.H>
Pierre Ossmanb8858222011-04-29 11:51:38 +000054#include <FL/x.H>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000055
56#include "i18n.h"
57#include "parameters.h"
58#include "CConn.h"
Pierre Ossman561ff0c2011-05-13 14:04:59 +000059#include "ServerDialog.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000060#include "UserDialog.h"
61
DRCb65bb932011-06-24 03:17:00 +000062#ifdef WIN32
Pierre Ossman8eb35082012-03-27 12:50:54 +000063#include "resource.h"
DRCb65bb932011-06-24 03:17:00 +000064#include "win32.h"
65#endif
66
Pierre Ossman5156d5e2011-03-09 09:42:34 +000067rfb::LogWriter vlog("main");
68
69using namespace network;
70using namespace rfb;
71using namespace std;
72
Pierre Ossmanf52740e2012-04-25 15:43:56 +000073static const char aboutText[] = N_("TigerVNC Viewer %d-bit v%s (%s)\n"
74 "%s\n"
75 "Copyright (C) 1999-2011 TigerVNC Team and many others (see README.txt)\n"
76 "See http://www.tigervnc.org for information on TigerVNC.");
DRCd8e93dc2011-07-28 22:13:40 +000077extern const char* buildTime;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000078
79static bool exitMainloop = false;
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000080static const char *exitError = NULL;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000081
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000082void exit_vncviewer(const char *error)
Pierre Ossman5156d5e2011-03-09 09:42:34 +000083{
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000084 // Prioritise the first error we get as that is probably the most
85 // relevant one.
86 if ((error != NULL) && (exitError == NULL))
87 exitError = strdup(error);
88
Pierre Ossman5156d5e2011-03-09 09:42:34 +000089 exitMainloop = true;
90}
91
Pierre Ossmanb8858222011-04-29 11:51:38 +000092void about_vncviewer()
93{
94 fl_message_title(_("About TigerVNC Viewer"));
Pierre Ossmanf52740e2012-04-25 15:43:56 +000095 fl_message(gettext(aboutText), (int)sizeof(size_t)*8,
96 PACKAGE_VERSION, __BUILD__, buildTime);
Pierre Ossmanb8858222011-04-29 11:51:38 +000097}
98
99static void about_callback(Fl_Widget *widget, void *data)
100{
101 about_vncviewer();
102}
103
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000104static void CleanupSignalHandler(int sig)
105{
106 // CleanupSignalHandler allows C++ object cleanup to happen because it calls
107 // exit() rather than the default which is to abort.
108 vlog.info("CleanupSignalHandler called");
109 exit(1);
110}
111
112static void init_fltk()
113{
114 // Basic text size (10pt @ 96 dpi => 13px)
115 FL_NORMAL_SIZE = 13;
116
117#ifndef __APPLE__
118 // Select a FLTK scheme and background color that looks somewhat
119 // close to modern Linux and Windows.
120 Fl::scheme("gtk+");
121 Fl::background(220, 220, 220);
122#else
123 // On Mac OS X there is another scheme that fits better though.
124 Fl::scheme("plastic");
125#endif
126
Henrik Andersson3b837032011-09-19 13:46:55 +0000127 // Proper Gnome Shell integration requires that we set a sensible
128 // WM_CLASS for the window.
129 Fl_Window::default_xclass("vncviewer");
130
Pierre Ossman8eb35082012-03-27 12:50:54 +0000131 // Set the default icon for all windows.
132#ifdef HAVE_FLTK_ICONS
133#ifdef WIN32
134 HICON lg, sm;
135
136 lg = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
137 IMAGE_ICON, GetSystemMetrics(SM_CXICON),
138 GetSystemMetrics(SM_CYICON),
139 LR_DEFAULTCOLOR | LR_SHARED);
140 sm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
141 IMAGE_ICON, GetSystemMetrics(SM_CXSMICON),
142 GetSystemMetrics(SM_CYSMICON),
143 LR_DEFAULTCOLOR | LR_SHARED);
144
145 Fl_Window::default_icons(lg, sm);
146#elif ! defined(__APPLE__)
147 const int icon_sizes[] = {48, 32, 24, 16};
148
149 Fl_PNG_Image *icons[4];
150 int count;
151
152 count = 0;
153
154 // FIXME: Follow icon theme specification
155 for (size_t i = 0;i < sizeof(icon_sizes)/sizeof(icon_sizes[0]);i++) {
156 char icon_path[PATH_MAX];
157 bool exists;
158
Pierre Ossman6a007bd2012-09-11 10:56:21 +0000159 sprintf(icon_path, "%s/icons/hicolor/%dx%d/apps/tigervnc.png",
Pierre Ossman8eb35082012-03-27 12:50:54 +0000160 DATA_DIR, icon_sizes[i], icon_sizes[i]);
161
162#ifndef WIN32
163 struct stat st;
164 if (stat(icon_path, &st) != 0)
165#else
166 struct _stat st;
167 if (_stat(icon_path, &st) != 0)
168 return(false);
169#endif
170 exists = false;
171 else
172 exists = true;
173
174 if (exists) {
175 icons[count] = new Fl_PNG_Image(icon_path);
176 if (icons[count]->w() == 0 ||
177 icons[count]->h() == 0 ||
178 icons[count]->d() != 4) {
179 delete icons[count];
180 continue;
181 }
182
183 count++;
184 }
185 }
186
187 Fl_Window::default_icons((const Fl_RGB_Image**)icons, count);
188
189 for (int i = 0;i < count;i++)
190 delete icons[i];
191#endif
192#endif // FLTK_HAVE_ICONS
193
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000194 // This makes the "icon" in dialogs rounded, which fits better
195 // with the above schemes.
196 fl_message_icon()->box(FL_UP_BOX);
197
198 // Turn off the annoying behaviour where popups track the mouse.
199 fl_message_hotspot(false);
200
201 // Avoid empty titles for popups
202 fl_message_title_default(_("TigerVNC Viewer"));
203
204#ifdef WIN32
205 // Most "normal" Windows apps use this font for UI elements.
206 Fl::set_font(FL_HELVETICA, "Tahoma");
207#endif
208
209 // FLTK exposes these so that we can translate them.
210 fl_no = _("No");
211 fl_yes = _("Yes");
212 fl_ok = _("OK");
213 fl_cancel = _("Cancel");
214 fl_close = _("Close");
Pierre Ossmanb8858222011-04-29 11:51:38 +0000215
216#ifdef __APPLE__
Peter Åstrandb7c55242011-08-29 13:14:51 +0000217 /* Needs trailing space */
218 static char fltk_about[16];
219 snprintf(fltk_about, sizeof(fltk_about), "%s ", _("About"));
220 Fl_Mac_App_Menu::about = fltk_about;
221 static char fltk_hide[16];
222 snprintf(fltk_hide, sizeof(fltk_hide), "%s ", _("Hide"));
223 Fl_Mac_App_Menu::hide = fltk_hide;
224 static char fltk_quit[16];
225 snprintf(fltk_quit, sizeof(fltk_quit), "%s ", _("Quit"));
226 Fl_Mac_App_Menu::quit = fltk_quit;
227
Pierre Ossman41ba6032011-06-16 11:09:31 +0000228 Fl_Mac_App_Menu::print = ""; // Don't want the print item
229 Fl_Mac_App_Menu::services = _("Services");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000230 Fl_Mac_App_Menu::hide_others = _("Hide Others");
231 Fl_Mac_App_Menu::show = _("Show All");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000232
Pierre Ossmanb8858222011-04-29 11:51:38 +0000233 fl_mac_set_about(about_callback, NULL);
234#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000235}
236
237static void mkvnchomedir()
238{
239 // Create .vnc in the user's home directory if it doesn't already exist
240 char* homeDir = NULL;
241
242 if (getvnchomedir(&homeDir) == -1) {
243 vlog.error(_("Could not create VNC home directory: can't obtain home "
244 "directory path."));
245 } else {
246 int result = mkdir(homeDir, 0755);
247 if (result == -1 && errno != EEXIST)
248 vlog.error(_("Could not create VNC home directory: %s."), strerror(errno));
249 delete [] homeDir;
250 }
251}
252
253static void usage(const char *programName)
254{
255 fprintf(stderr,
256 "\nusage: %s [parameters] [host:displayNum] [parameters]\n"
257 " %s [parameters] -listen [port] [parameters]\n",
258 programName, programName);
259 fprintf(stderr,"\n"
260 "Parameters can be turned on with -<param> or off with -<param>=0\n"
261 "Parameters which take a value can be specified as "
262 "-<param> <value>\n"
263 "Other valid forms are <param>=<value> -<param>=<value> "
264 "--<param>=<value>\n"
265 "Parameter names are case-insensitive. The parameters are:\n\n");
266 Configuration::listParams(79, 14);
267 exit(1);
268}
269
270int main(int argc, char** argv)
271{
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000272 const char* vncServerName = NULL;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000273 UserDialog dlg;
274
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000275 setlocale(LC_ALL, "");
Pierre Ossman0878eca2012-03-27 13:03:22 +0000276 bindtextdomain(PACKAGE_NAME, LOCALE_DIR);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000277 textdomain(PACKAGE_NAME);
278
279 rfb::SecurityClient::setDefaults();
280
281 // Write about text to console, still using normal locale codeset
Pierre Ossmanf52740e2012-04-25 15:43:56 +0000282 fprintf(stderr,"\n");
283 fprintf(stderr, gettext(aboutText), (int)sizeof(size_t)*8,
284 PACKAGE_VERSION, __BUILD__, buildTime);
285 fprintf(stderr,"\n");
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000286
287 // Set gettext codeset to what our GUI toolkit uses. Since we are
288 // passing strings from strerror/gai_strerror to the GUI, these must
289 // be in GUI codeset as well.
290 bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
291 bind_textdomain_codeset("libc", "UTF-8");
292
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000293 rfb::initStdIOLoggers();
Pierre Ossmanc8719ad2012-04-26 14:27:52 +0000294#ifdef WIN32
295 rfb::initFileLogger("C:\\temp\\vncviewer.log");
296#else
297 rfb::initFileLogger("/tmp/vncviewer.log");
298#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000299 rfb::LogWriter::setLogParams("*:stderr:30");
300
301#ifdef SIGHUP
302 signal(SIGHUP, CleanupSignalHandler);
303#endif
304 signal(SIGINT, CleanupSignalHandler);
305 signal(SIGTERM, CleanupSignalHandler);
306
307 init_fltk();
308
309 Configuration::enableViewerParams();
310
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000311 /* Load the default parameter settings */
312 const char* defaultServerName;
313 try {
314 defaultServerName = loadViewerParameters(NULL);
315 } catch (rfb::Exception& e) {
316 fl_alert("%s", e.str());
317 }
318
DRC5aa06502011-06-23 22:04:46 +0000319 int i = 1;
320 if (!Fl::args(argc, argv, i) || i < argc)
321 for (; i < argc; i++) {
322 if (Configuration::setParam(argv[i]))
323 continue;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000324
DRC5aa06502011-06-23 22:04:46 +0000325 if (argv[i][0] == '-') {
326 if (i+1 < argc) {
327 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
328 i++;
329 continue;
330 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000331 }
DRC5aa06502011-06-23 22:04:46 +0000332 usage(argv[0]);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000333 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000334
DRC5aa06502011-06-23 22:04:46 +0000335 vncServerName = argv[i];
336 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000337
338 if (!::autoSelect.hasBeenSet()) {
339 // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000340 if (::preferredEncoding.hasBeenSet() || ::fullColour.hasBeenSet() ||
341 ::fullColourAlias.hasBeenSet()) {
342 ::autoSelect.setParam(false);
343 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000344 }
345 if (!::fullColour.hasBeenSet() && !::fullColourAlias.hasBeenSet()) {
346 // Default to FullColor=0 if AutoSelect=0 && LowColorLevel is set
347 if (!::autoSelect && (::lowColourLevel.hasBeenSet() ||
348 ::lowColourLevelAlias.hasBeenSet())) {
349 ::fullColour.setParam(false);
350 }
351 }
352 if (!::customCompressLevel.hasBeenSet()) {
353 // Default to CustomCompressLevel=1 if CompressLevel is used.
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000354 if(::compressLevel.hasBeenSet()) {
355 ::customCompressLevel.setParam(true);
356 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000357 }
358
359 mkvnchomedir();
360
361 CSecurity::upg = &dlg;
362#ifdef HAVE_GNUTLS
363 CSecurityTLS::msg = &dlg;
364#endif
365
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000366 if (vncServerName == NULL) {
Peter Åstrand8a2b0812012-08-08 11:49:01 +0000367 vncServerName = ServerDialog::run(defaultServerName);
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000368 if ((vncServerName == NULL) || (vncServerName[0] == '\0'))
369 return 1;
370 }
371
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000372 CConn *cc = new CConn(vncServerName);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000373
374 while (!exitMainloop) {
375 int next_timer;
376
377 next_timer = Timer::checkTimeouts();
378 if (next_timer == 0)
379 next_timer = INT_MAX;
380
381 if (Fl::wait((double)next_timer / 1000.0) < 0.0) {
382 vlog.error(_("Internal FLTK error. Exiting."));
383 break;
384 }
385 }
386
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000387 delete cc;
388
389 if (exitError != NULL)
Pierre Ossmanf52740e2012-04-25 15:43:56 +0000390 fl_alert("%s", exitError);
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000391
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000392 return 0;
393}