blob: fd1243ec7b87ae43b8c0c3dcaea2154b68c1cc75 [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>
46#include <network/TcpSocket.h>
DRC4426f002011-10-12 20:02:55 +000047#include <os/os.h>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000048
49#include <FL/Fl.H>
50#include <FL/Fl_Widget.H>
Pierre Ossman8eb35082012-03-27 12:50:54 +000051#include <FL/Fl_PNG_Image.H>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000052#include <FL/fl_ask.H>
Pierre Ossmanb8858222011-04-29 11:51:38 +000053#include <FL/x.H>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000054
55#include "i18n.h"
56#include "parameters.h"
57#include "CConn.h"
Pierre Ossman561ff0c2011-05-13 14:04:59 +000058#include "ServerDialog.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000059#include "UserDialog.h"
60
DRCb65bb932011-06-24 03:17:00 +000061#ifdef WIN32
Pierre Ossman8eb35082012-03-27 12:50:54 +000062#include "resource.h"
DRCb65bb932011-06-24 03:17:00 +000063#include "win32.h"
64#endif
65
Pierre Ossman5156d5e2011-03-09 09:42:34 +000066rfb::LogWriter vlog("main");
67
68using namespace network;
69using namespace rfb;
70using namespace std;
71
Pierre Ossmanf52740e2012-04-25 15:43:56 +000072static const char aboutText[] = N_("TigerVNC Viewer %d-bit v%s (%s)\n"
73 "%s\n"
74 "Copyright (C) 1999-2011 TigerVNC Team and many others (see README.txt)\n"
75 "See http://www.tigervnc.org for information on TigerVNC.");
DRCd8e93dc2011-07-28 22:13:40 +000076extern const char* buildTime;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000077
78static bool exitMainloop = false;
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000079static const char *exitError = NULL;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000080
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000081void exit_vncviewer(const char *error)
Pierre Ossman5156d5e2011-03-09 09:42:34 +000082{
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000083 // Prioritise the first error we get as that is probably the most
84 // relevant one.
85 if ((error != NULL) && (exitError == NULL))
86 exitError = strdup(error);
87
Pierre Ossman5156d5e2011-03-09 09:42:34 +000088 exitMainloop = true;
89}
90
Pierre Ossmanb8858222011-04-29 11:51:38 +000091void about_vncviewer()
92{
93 fl_message_title(_("About TigerVNC Viewer"));
Pierre Ossmanf52740e2012-04-25 15:43:56 +000094 fl_message(gettext(aboutText), (int)sizeof(size_t)*8,
95 PACKAGE_VERSION, __BUILD__, buildTime);
Pierre Ossmanb8858222011-04-29 11:51:38 +000096}
97
98static void about_callback(Fl_Widget *widget, void *data)
99{
100 about_vncviewer();
101}
102
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000103static void CleanupSignalHandler(int sig)
104{
105 // CleanupSignalHandler allows C++ object cleanup to happen because it calls
106 // exit() rather than the default which is to abort.
107 vlog.info("CleanupSignalHandler called");
108 exit(1);
109}
110
111static void init_fltk()
112{
113 // Basic text size (10pt @ 96 dpi => 13px)
114 FL_NORMAL_SIZE = 13;
115
116#ifndef __APPLE__
117 // Select a FLTK scheme and background color that looks somewhat
118 // close to modern Linux and Windows.
119 Fl::scheme("gtk+");
120 Fl::background(220, 220, 220);
121#else
122 // On Mac OS X there is another scheme that fits better though.
123 Fl::scheme("plastic");
124#endif
125
Henrik Andersson3b837032011-09-19 13:46:55 +0000126 // Proper Gnome Shell integration requires that we set a sensible
127 // WM_CLASS for the window.
128 Fl_Window::default_xclass("vncviewer");
129
Pierre Ossman8eb35082012-03-27 12:50:54 +0000130 // Set the default icon for all windows.
131#ifdef HAVE_FLTK_ICONS
132#ifdef WIN32
133 HICON lg, sm;
134
135 lg = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
136 IMAGE_ICON, GetSystemMetrics(SM_CXICON),
137 GetSystemMetrics(SM_CYICON),
138 LR_DEFAULTCOLOR | LR_SHARED);
139 sm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
140 IMAGE_ICON, GetSystemMetrics(SM_CXSMICON),
141 GetSystemMetrics(SM_CYSMICON),
142 LR_DEFAULTCOLOR | LR_SHARED);
143
144 Fl_Window::default_icons(lg, sm);
145#elif ! defined(__APPLE__)
146 const int icon_sizes[] = {48, 32, 24, 16};
147
148 Fl_PNG_Image *icons[4];
149 int count;
150
151 count = 0;
152
153 // FIXME: Follow icon theme specification
154 for (size_t i = 0;i < sizeof(icon_sizes)/sizeof(icon_sizes[0]);i++) {
155 char icon_path[PATH_MAX];
156 bool exists;
157
158 sprintf(icon_path, "%s/icons/hicolor/%dx%d/tigervnc.png",
159 DATA_DIR, icon_sizes[i], icon_sizes[i]);
160
161#ifndef WIN32
162 struct stat st;
163 if (stat(icon_path, &st) != 0)
164#else
165 struct _stat st;
166 if (_stat(icon_path, &st) != 0)
167 return(false);
168#endif
169 exists = false;
170 else
171 exists = true;
172
173 if (exists) {
174 icons[count] = new Fl_PNG_Image(icon_path);
175 if (icons[count]->w() == 0 ||
176 icons[count]->h() == 0 ||
177 icons[count]->d() != 4) {
178 delete icons[count];
179 continue;
180 }
181
182 count++;
183 }
184 }
185
186 Fl_Window::default_icons((const Fl_RGB_Image**)icons, count);
187
188 for (int i = 0;i < count;i++)
189 delete icons[i];
190#endif
191#endif // FLTK_HAVE_ICONS
192
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000193 // This makes the "icon" in dialogs rounded, which fits better
194 // with the above schemes.
195 fl_message_icon()->box(FL_UP_BOX);
196
197 // Turn off the annoying behaviour where popups track the mouse.
198 fl_message_hotspot(false);
199
200 // Avoid empty titles for popups
201 fl_message_title_default(_("TigerVNC Viewer"));
202
203#ifdef WIN32
204 // Most "normal" Windows apps use this font for UI elements.
205 Fl::set_font(FL_HELVETICA, "Tahoma");
206#endif
207
208 // FLTK exposes these so that we can translate them.
209 fl_no = _("No");
210 fl_yes = _("Yes");
211 fl_ok = _("OK");
212 fl_cancel = _("Cancel");
213 fl_close = _("Close");
Pierre Ossmanb8858222011-04-29 11:51:38 +0000214
215#ifdef __APPLE__
Peter Åstrandb7c55242011-08-29 13:14:51 +0000216 /* Needs trailing space */
217 static char fltk_about[16];
218 snprintf(fltk_about, sizeof(fltk_about), "%s ", _("About"));
219 Fl_Mac_App_Menu::about = fltk_about;
220 static char fltk_hide[16];
221 snprintf(fltk_hide, sizeof(fltk_hide), "%s ", _("Hide"));
222 Fl_Mac_App_Menu::hide = fltk_hide;
223 static char fltk_quit[16];
224 snprintf(fltk_quit, sizeof(fltk_quit), "%s ", _("Quit"));
225 Fl_Mac_App_Menu::quit = fltk_quit;
226
Pierre Ossman41ba6032011-06-16 11:09:31 +0000227 Fl_Mac_App_Menu::print = ""; // Don't want the print item
228 Fl_Mac_App_Menu::services = _("Services");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000229 Fl_Mac_App_Menu::hide_others = _("Hide Others");
230 Fl_Mac_App_Menu::show = _("Show All");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000231
Pierre Ossmanb8858222011-04-29 11:51:38 +0000232 fl_mac_set_about(about_callback, NULL);
233#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000234}
235
236static void mkvnchomedir()
237{
238 // Create .vnc in the user's home directory if it doesn't already exist
239 char* homeDir = NULL;
240
241 if (getvnchomedir(&homeDir) == -1) {
242 vlog.error(_("Could not create VNC home directory: can't obtain home "
243 "directory path."));
244 } else {
245 int result = mkdir(homeDir, 0755);
246 if (result == -1 && errno != EEXIST)
247 vlog.error(_("Could not create VNC home directory: %s."), strerror(errno));
248 delete [] homeDir;
249 }
250}
251
252static void usage(const char *programName)
253{
254 fprintf(stderr,
255 "\nusage: %s [parameters] [host:displayNum] [parameters]\n"
256 " %s [parameters] -listen [port] [parameters]\n",
257 programName, programName);
258 fprintf(stderr,"\n"
259 "Parameters can be turned on with -<param> or off with -<param>=0\n"
260 "Parameters which take a value can be specified as "
261 "-<param> <value>\n"
262 "Other valid forms are <param>=<value> -<param>=<value> "
263 "--<param>=<value>\n"
264 "Parameter names are case-insensitive. The parameters are:\n\n");
265 Configuration::listParams(79, 14);
266 exit(1);
267}
268
269int main(int argc, char** argv)
270{
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000271 const char* vncServerName = NULL;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000272 UserDialog dlg;
273
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000274 setlocale(LC_ALL, "");
Pierre Ossman0878eca2012-03-27 13:03:22 +0000275 bindtextdomain(PACKAGE_NAME, LOCALE_DIR);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000276 textdomain(PACKAGE_NAME);
277
278 rfb::SecurityClient::setDefaults();
279
280 // Write about text to console, still using normal locale codeset
Pierre Ossmanf52740e2012-04-25 15:43:56 +0000281 fprintf(stderr,"\n");
282 fprintf(stderr, gettext(aboutText), (int)sizeof(size_t)*8,
283 PACKAGE_VERSION, __BUILD__, buildTime);
284 fprintf(stderr,"\n");
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000285
286 // Set gettext codeset to what our GUI toolkit uses. Since we are
287 // passing strings from strerror/gai_strerror to the GUI, these must
288 // be in GUI codeset as well.
289 bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
290 bind_textdomain_codeset("libc", "UTF-8");
291
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000292 rfb::initStdIOLoggers();
Pierre Ossmanc8719ad2012-04-26 14:27:52 +0000293#ifdef WIN32
294 rfb::initFileLogger("C:\\temp\\vncviewer.log");
295#else
296 rfb::initFileLogger("/tmp/vncviewer.log");
297#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000298 rfb::LogWriter::setLogParams("*:stderr:30");
299
300#ifdef SIGHUP
301 signal(SIGHUP, CleanupSignalHandler);
302#endif
303 signal(SIGINT, CleanupSignalHandler);
304 signal(SIGTERM, CleanupSignalHandler);
305
306 init_fltk();
307
308 Configuration::enableViewerParams();
309
DRC5aa06502011-06-23 22:04:46 +0000310 int i = 1;
311 if (!Fl::args(argc, argv, i) || i < argc)
312 for (; i < argc; i++) {
313 if (Configuration::setParam(argv[i]))
314 continue;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000315
DRC5aa06502011-06-23 22:04:46 +0000316 if (argv[i][0] == '-') {
317 if (i+1 < argc) {
318 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
319 i++;
320 continue;
321 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000322 }
DRC5aa06502011-06-23 22:04:46 +0000323 usage(argv[0]);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000324 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000325
DRC5aa06502011-06-23 22:04:46 +0000326 vncServerName = argv[i];
327 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000328
329 if (!::autoSelect.hasBeenSet()) {
330 // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used
331 ::autoSelect.setParam(!::preferredEncoding.hasBeenSet() &&
332 !::fullColour.hasBeenSet() &&
333 !::fullColourAlias.hasBeenSet());
334 }
335 if (!::fullColour.hasBeenSet() && !::fullColourAlias.hasBeenSet()) {
336 // Default to FullColor=0 if AutoSelect=0 && LowColorLevel is set
337 if (!::autoSelect && (::lowColourLevel.hasBeenSet() ||
338 ::lowColourLevelAlias.hasBeenSet())) {
339 ::fullColour.setParam(false);
340 }
341 }
342 if (!::customCompressLevel.hasBeenSet()) {
343 // Default to CustomCompressLevel=1 if CompressLevel is used.
344 ::customCompressLevel.setParam(::compressLevel.hasBeenSet());
345 }
346
347 mkvnchomedir();
348
349 CSecurity::upg = &dlg;
350#ifdef HAVE_GNUTLS
351 CSecurityTLS::msg = &dlg;
352#endif
353
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000354 if (vncServerName == NULL) {
355 vncServerName = ServerDialog::run();
356 if ((vncServerName == NULL) || (vncServerName[0] == '\0'))
357 return 1;
358 }
359
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000360 CConn *cc = new CConn(vncServerName);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000361
362 while (!exitMainloop) {
363 int next_timer;
364
365 next_timer = Timer::checkTimeouts();
366 if (next_timer == 0)
367 next_timer = INT_MAX;
368
369 if (Fl::wait((double)next_timer / 1000.0) < 0.0) {
370 vlog.error(_("Internal FLTK error. Exiting."));
371 break;
372 }
373 }
374
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000375 delete cc;
376
377 if (exitError != NULL)
Pierre Ossmanf52740e2012-04-25 15:43:56 +0000378 fl_alert("%s", exitError);
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000379
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000380 return 0;
381}