blob: 1f6fca56218550d14af254416a0603824d9e703b [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 Ossmanb8858222011-04-29 11:51:38 +000072static char aboutText[1024];
DRCd8e93dc2011-07-28 22:13:40 +000073extern const char* buildTime;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000074
75static bool exitMainloop = false;
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000076static const char *exitError = NULL;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000077
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000078void exit_vncviewer(const char *error)
Pierre Ossman5156d5e2011-03-09 09:42:34 +000079{
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000080 // Prioritise the first error we get as that is probably the most
81 // relevant one.
82 if ((error != NULL) && (exitError == NULL))
83 exitError = strdup(error);
84
Pierre Ossman5156d5e2011-03-09 09:42:34 +000085 exitMainloop = true;
86}
87
Pierre Ossmanb8858222011-04-29 11:51:38 +000088void about_vncviewer()
89{
90 fl_message_title(_("About TigerVNC Viewer"));
91 fl_message(aboutText);
92}
93
94static void about_callback(Fl_Widget *widget, void *data)
95{
96 about_vncviewer();
97}
98
Pierre Ossman5156d5e2011-03-09 09:42:34 +000099static void CleanupSignalHandler(int sig)
100{
101 // CleanupSignalHandler allows C++ object cleanup to happen because it calls
102 // exit() rather than the default which is to abort.
103 vlog.info("CleanupSignalHandler called");
104 exit(1);
105}
106
107static void init_fltk()
108{
109 // Basic text size (10pt @ 96 dpi => 13px)
110 FL_NORMAL_SIZE = 13;
111
112#ifndef __APPLE__
113 // Select a FLTK scheme and background color that looks somewhat
114 // close to modern Linux and Windows.
115 Fl::scheme("gtk+");
116 Fl::background(220, 220, 220);
117#else
118 // On Mac OS X there is another scheme that fits better though.
119 Fl::scheme("plastic");
120#endif
121
Henrik Andersson3b837032011-09-19 13:46:55 +0000122 // Proper Gnome Shell integration requires that we set a sensible
123 // WM_CLASS for the window.
124 Fl_Window::default_xclass("vncviewer");
125
Pierre Ossman8eb35082012-03-27 12:50:54 +0000126 // Set the default icon for all windows.
127#ifdef HAVE_FLTK_ICONS
128#ifdef WIN32
129 HICON lg, sm;
130
131 lg = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
132 IMAGE_ICON, GetSystemMetrics(SM_CXICON),
133 GetSystemMetrics(SM_CYICON),
134 LR_DEFAULTCOLOR | LR_SHARED);
135 sm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON),
136 IMAGE_ICON, GetSystemMetrics(SM_CXSMICON),
137 GetSystemMetrics(SM_CYSMICON),
138 LR_DEFAULTCOLOR | LR_SHARED);
139
140 Fl_Window::default_icons(lg, sm);
141#elif ! defined(__APPLE__)
142 const int icon_sizes[] = {48, 32, 24, 16};
143
144 Fl_PNG_Image *icons[4];
145 int count;
146
147 count = 0;
148
149 // FIXME: Follow icon theme specification
150 for (size_t i = 0;i < sizeof(icon_sizes)/sizeof(icon_sizes[0]);i++) {
151 char icon_path[PATH_MAX];
152 bool exists;
153
154 sprintf(icon_path, "%s/icons/hicolor/%dx%d/tigervnc.png",
155 DATA_DIR, icon_sizes[i], icon_sizes[i]);
156
157#ifndef WIN32
158 struct stat st;
159 if (stat(icon_path, &st) != 0)
160#else
161 struct _stat st;
162 if (_stat(icon_path, &st) != 0)
163 return(false);
164#endif
165 exists = false;
166 else
167 exists = true;
168
169 if (exists) {
170 icons[count] = new Fl_PNG_Image(icon_path);
171 if (icons[count]->w() == 0 ||
172 icons[count]->h() == 0 ||
173 icons[count]->d() != 4) {
174 delete icons[count];
175 continue;
176 }
177
178 count++;
179 }
180 }
181
182 Fl_Window::default_icons((const Fl_RGB_Image**)icons, count);
183
184 for (int i = 0;i < count;i++)
185 delete icons[i];
186#endif
187#endif // FLTK_HAVE_ICONS
188
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000189 // This makes the "icon" in dialogs rounded, which fits better
190 // with the above schemes.
191 fl_message_icon()->box(FL_UP_BOX);
192
193 // Turn off the annoying behaviour where popups track the mouse.
194 fl_message_hotspot(false);
195
196 // Avoid empty titles for popups
197 fl_message_title_default(_("TigerVNC Viewer"));
198
199#ifdef WIN32
200 // Most "normal" Windows apps use this font for UI elements.
201 Fl::set_font(FL_HELVETICA, "Tahoma");
202#endif
203
204 // FLTK exposes these so that we can translate them.
205 fl_no = _("No");
206 fl_yes = _("Yes");
207 fl_ok = _("OK");
208 fl_cancel = _("Cancel");
209 fl_close = _("Close");
Pierre Ossmanb8858222011-04-29 11:51:38 +0000210
211#ifdef __APPLE__
Peter Åstrandb7c55242011-08-29 13:14:51 +0000212 /* Needs trailing space */
213 static char fltk_about[16];
214 snprintf(fltk_about, sizeof(fltk_about), "%s ", _("About"));
215 Fl_Mac_App_Menu::about = fltk_about;
216 static char fltk_hide[16];
217 snprintf(fltk_hide, sizeof(fltk_hide), "%s ", _("Hide"));
218 Fl_Mac_App_Menu::hide = fltk_hide;
219 static char fltk_quit[16];
220 snprintf(fltk_quit, sizeof(fltk_quit), "%s ", _("Quit"));
221 Fl_Mac_App_Menu::quit = fltk_quit;
222
Pierre Ossman41ba6032011-06-16 11:09:31 +0000223 Fl_Mac_App_Menu::print = ""; // Don't want the print item
224 Fl_Mac_App_Menu::services = _("Services");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000225 Fl_Mac_App_Menu::hide_others = _("Hide Others");
226 Fl_Mac_App_Menu::show = _("Show All");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000227
Pierre Ossmanb8858222011-04-29 11:51:38 +0000228 fl_mac_set_about(about_callback, NULL);
229#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000230}
231
232static void mkvnchomedir()
233{
234 // Create .vnc in the user's home directory if it doesn't already exist
235 char* homeDir = NULL;
236
237 if (getvnchomedir(&homeDir) == -1) {
238 vlog.error(_("Could not create VNC home directory: can't obtain home "
239 "directory path."));
240 } else {
241 int result = mkdir(homeDir, 0755);
242 if (result == -1 && errno != EEXIST)
243 vlog.error(_("Could not create VNC home directory: %s."), strerror(errno));
244 delete [] homeDir;
245 }
246}
247
248static void usage(const char *programName)
249{
250 fprintf(stderr,
251 "\nusage: %s [parameters] [host:displayNum] [parameters]\n"
252 " %s [parameters] -listen [port] [parameters]\n",
253 programName, programName);
254 fprintf(stderr,"\n"
255 "Parameters can be turned on with -<param> or off with -<param>=0\n"
256 "Parameters which take a value can be specified as "
257 "-<param> <value>\n"
258 "Other valid forms are <param>=<value> -<param>=<value> "
259 "--<param>=<value>\n"
260 "Parameter names are case-insensitive. The parameters are:\n\n");
261 Configuration::listParams(79, 14);
262 exit(1);
263}
264
265int main(int argc, char** argv)
266{
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000267 const char* vncServerName = NULL;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000268 UserDialog dlg;
269
DRCd8e93dc2011-07-28 22:13:40 +0000270 const char englishAbout[] = N_("TigerVNC Viewer %d-bit v%s (%s)\n"
271 "%s\n"
DRC07baad72011-06-28 05:42:30 +0000272 "Copyright (C) 1999-2011 TigerVNC Team and many others (see README.txt)\n"
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000273 "See http://www.tigervnc.org for information on TigerVNC.");
274
275 setlocale(LC_ALL, "");
276 bindtextdomain(PACKAGE_NAME, LOCALEDIR);
277 textdomain(PACKAGE_NAME);
278
279 rfb::SecurityClient::setDefaults();
280
281 // Write about text to console, still using normal locale codeset
282 snprintf(aboutText, sizeof(aboutText),
DRCd8e93dc2011-07-28 22:13:40 +0000283 gettext(englishAbout), (int)sizeof(size_t)*8, PACKAGE_VERSION,
284 __BUILD__, buildTime);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000285 fprintf(stderr,"\n%s\n", aboutText);
286
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
293 // Re-create the aboutText for the GUI, now using GUI codeset
294 snprintf(aboutText, sizeof(aboutText),
DRCd8e93dc2011-07-28 22:13:40 +0000295 gettext(englishAbout), (int)sizeof(size_t)*8, PACKAGE_VERSION,
296 __BUILD__, buildTime);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000297
298 rfb::initStdIOLoggers();
299 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
DRC5aa06502011-06-23 22:04:46 +0000311 int i = 1;
312 if (!Fl::args(argc, argv, i) || i < argc)
313 for (; i < argc; i++) {
314 if (Configuration::setParam(argv[i]))
315 continue;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000316
DRC5aa06502011-06-23 22:04:46 +0000317 if (argv[i][0] == '-') {
318 if (i+1 < argc) {
319 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
320 i++;
321 continue;
322 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000323 }
DRC5aa06502011-06-23 22:04:46 +0000324 usage(argv[0]);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000325 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000326
DRC5aa06502011-06-23 22:04:46 +0000327 vncServerName = argv[i];
328 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000329
330 if (!::autoSelect.hasBeenSet()) {
331 // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used
332 ::autoSelect.setParam(!::preferredEncoding.hasBeenSet() &&
333 !::fullColour.hasBeenSet() &&
334 !::fullColourAlias.hasBeenSet());
335 }
336 if (!::fullColour.hasBeenSet() && !::fullColourAlias.hasBeenSet()) {
337 // Default to FullColor=0 if AutoSelect=0 && LowColorLevel is set
338 if (!::autoSelect && (::lowColourLevel.hasBeenSet() ||
339 ::lowColourLevelAlias.hasBeenSet())) {
340 ::fullColour.setParam(false);
341 }
342 }
343 if (!::customCompressLevel.hasBeenSet()) {
344 // Default to CustomCompressLevel=1 if CompressLevel is used.
345 ::customCompressLevel.setParam(::compressLevel.hasBeenSet());
346 }
347
348 mkvnchomedir();
349
350 CSecurity::upg = &dlg;
351#ifdef HAVE_GNUTLS
352 CSecurityTLS::msg = &dlg;
353#endif
354
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000355 if (vncServerName == NULL) {
356 vncServerName = ServerDialog::run();
357 if ((vncServerName == NULL) || (vncServerName[0] == '\0'))
358 return 1;
359 }
360
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000361 CConn *cc = new CConn(vncServerName);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000362
363 while (!exitMainloop) {
364 int next_timer;
365
366 next_timer = Timer::checkTimeouts();
367 if (next_timer == 0)
368 next_timer = INT_MAX;
369
370 if (Fl::wait((double)next_timer / 1000.0) < 0.0) {
371 vlog.error(_("Internal FLTK error. Exiting."));
372 break;
373 }
374 }
375
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000376 delete cc;
377
378 if (exitError != NULL)
379 fl_alert(exitError);
380
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000381 return 0;
382}