blob: 5fe7fe085cbcf9cbd8f1a72cd4822c53ed22895c [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>
51#include <FL/fl_ask.H>
Pierre Ossmanb8858222011-04-29 11:51:38 +000052#include <FL/x.H>
Pierre Ossman5156d5e2011-03-09 09:42:34 +000053
54#include "i18n.h"
55#include "parameters.h"
56#include "CConn.h"
Pierre Ossman561ff0c2011-05-13 14:04:59 +000057#include "ServerDialog.h"
Pierre Ossman5156d5e2011-03-09 09:42:34 +000058#include "UserDialog.h"
59
DRCb65bb932011-06-24 03:17:00 +000060#ifdef WIN32
61#include "win32.h"
62#endif
63
Pierre Ossman5156d5e2011-03-09 09:42:34 +000064rfb::LogWriter vlog("main");
65
66using namespace network;
67using namespace rfb;
68using namespace std;
69
Pierre Ossmanb8858222011-04-29 11:51:38 +000070static char aboutText[1024];
DRCd8e93dc2011-07-28 22:13:40 +000071extern const char* buildTime;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000072
73static bool exitMainloop = false;
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000074static const char *exitError = NULL;
Pierre Ossman5156d5e2011-03-09 09:42:34 +000075
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000076void exit_vncviewer(const char *error)
Pierre Ossman5156d5e2011-03-09 09:42:34 +000077{
Pierre Ossmane2ef5c12011-07-12 16:56:34 +000078 // Prioritise the first error we get as that is probably the most
79 // relevant one.
80 if ((error != NULL) && (exitError == NULL))
81 exitError = strdup(error);
82
Pierre Ossman5156d5e2011-03-09 09:42:34 +000083 exitMainloop = true;
84}
85
Pierre Ossmanb8858222011-04-29 11:51:38 +000086void about_vncviewer()
87{
88 fl_message_title(_("About TigerVNC Viewer"));
89 fl_message(aboutText);
90}
91
92static void about_callback(Fl_Widget *widget, void *data)
93{
94 about_vncviewer();
95}
96
Pierre Ossman5156d5e2011-03-09 09:42:34 +000097static void CleanupSignalHandler(int sig)
98{
99 // CleanupSignalHandler allows C++ object cleanup to happen because it calls
100 // exit() rather than the default which is to abort.
101 vlog.info("CleanupSignalHandler called");
102 exit(1);
103}
104
105static void init_fltk()
106{
107 // Basic text size (10pt @ 96 dpi => 13px)
108 FL_NORMAL_SIZE = 13;
109
110#ifndef __APPLE__
111 // Select a FLTK scheme and background color that looks somewhat
112 // close to modern Linux and Windows.
113 Fl::scheme("gtk+");
114 Fl::background(220, 220, 220);
115#else
116 // On Mac OS X there is another scheme that fits better though.
117 Fl::scheme("plastic");
118#endif
119
Henrik Andersson3b837032011-09-19 13:46:55 +0000120 // Proper Gnome Shell integration requires that we set a sensible
121 // WM_CLASS for the window.
122 Fl_Window::default_xclass("vncviewer");
123
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000124 // This makes the "icon" in dialogs rounded, which fits better
125 // with the above schemes.
126 fl_message_icon()->box(FL_UP_BOX);
127
128 // Turn off the annoying behaviour where popups track the mouse.
129 fl_message_hotspot(false);
130
131 // Avoid empty titles for popups
132 fl_message_title_default(_("TigerVNC Viewer"));
133
134#ifdef WIN32
135 // Most "normal" Windows apps use this font for UI elements.
136 Fl::set_font(FL_HELVETICA, "Tahoma");
137#endif
138
139 // FLTK exposes these so that we can translate them.
140 fl_no = _("No");
141 fl_yes = _("Yes");
142 fl_ok = _("OK");
143 fl_cancel = _("Cancel");
144 fl_close = _("Close");
Pierre Ossmanb8858222011-04-29 11:51:38 +0000145
146#ifdef __APPLE__
Peter Åstrandb7c55242011-08-29 13:14:51 +0000147 /* Needs trailing space */
148 static char fltk_about[16];
149 snprintf(fltk_about, sizeof(fltk_about), "%s ", _("About"));
150 Fl_Mac_App_Menu::about = fltk_about;
151 static char fltk_hide[16];
152 snprintf(fltk_hide, sizeof(fltk_hide), "%s ", _("Hide"));
153 Fl_Mac_App_Menu::hide = fltk_hide;
154 static char fltk_quit[16];
155 snprintf(fltk_quit, sizeof(fltk_quit), "%s ", _("Quit"));
156 Fl_Mac_App_Menu::quit = fltk_quit;
157
Pierre Ossman41ba6032011-06-16 11:09:31 +0000158 Fl_Mac_App_Menu::print = ""; // Don't want the print item
159 Fl_Mac_App_Menu::services = _("Services");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000160 Fl_Mac_App_Menu::hide_others = _("Hide Others");
161 Fl_Mac_App_Menu::show = _("Show All");
Pierre Ossman41ba6032011-06-16 11:09:31 +0000162
Pierre Ossmanb8858222011-04-29 11:51:38 +0000163 fl_mac_set_about(about_callback, NULL);
164#endif
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000165}
166
167static void mkvnchomedir()
168{
169 // Create .vnc in the user's home directory if it doesn't already exist
170 char* homeDir = NULL;
171
172 if (getvnchomedir(&homeDir) == -1) {
173 vlog.error(_("Could not create VNC home directory: can't obtain home "
174 "directory path."));
175 } else {
176 int result = mkdir(homeDir, 0755);
177 if (result == -1 && errno != EEXIST)
178 vlog.error(_("Could not create VNC home directory: %s."), strerror(errno));
179 delete [] homeDir;
180 }
181}
182
183static void usage(const char *programName)
184{
185 fprintf(stderr,
186 "\nusage: %s [parameters] [host:displayNum] [parameters]\n"
187 " %s [parameters] -listen [port] [parameters]\n",
188 programName, programName);
189 fprintf(stderr,"\n"
190 "Parameters can be turned on with -<param> or off with -<param>=0\n"
191 "Parameters which take a value can be specified as "
192 "-<param> <value>\n"
193 "Other valid forms are <param>=<value> -<param>=<value> "
194 "--<param>=<value>\n"
195 "Parameter names are case-insensitive. The parameters are:\n\n");
196 Configuration::listParams(79, 14);
197 exit(1);
198}
199
200int main(int argc, char** argv)
201{
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000202 const char* vncServerName = NULL;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000203 UserDialog dlg;
204
DRCd8e93dc2011-07-28 22:13:40 +0000205 const char englishAbout[] = N_("TigerVNC Viewer %d-bit v%s (%s)\n"
206 "%s\n"
DRC07baad72011-06-28 05:42:30 +0000207 "Copyright (C) 1999-2011 TigerVNC Team and many others (see README.txt)\n"
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000208 "See http://www.tigervnc.org for information on TigerVNC.");
209
210 setlocale(LC_ALL, "");
211 bindtextdomain(PACKAGE_NAME, LOCALEDIR);
212 textdomain(PACKAGE_NAME);
213
214 rfb::SecurityClient::setDefaults();
215
216 // Write about text to console, still using normal locale codeset
217 snprintf(aboutText, sizeof(aboutText),
DRCd8e93dc2011-07-28 22:13:40 +0000218 gettext(englishAbout), (int)sizeof(size_t)*8, PACKAGE_VERSION,
219 __BUILD__, buildTime);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000220 fprintf(stderr,"\n%s\n", aboutText);
221
222 // Set gettext codeset to what our GUI toolkit uses. Since we are
223 // passing strings from strerror/gai_strerror to the GUI, these must
224 // be in GUI codeset as well.
225 bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
226 bind_textdomain_codeset("libc", "UTF-8");
227
228 // Re-create the aboutText for the GUI, now using GUI codeset
229 snprintf(aboutText, sizeof(aboutText),
DRCd8e93dc2011-07-28 22:13:40 +0000230 gettext(englishAbout), (int)sizeof(size_t)*8, PACKAGE_VERSION,
231 __BUILD__, buildTime);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000232
233 rfb::initStdIOLoggers();
234 rfb::LogWriter::setLogParams("*:stderr:30");
235
236#ifdef SIGHUP
237 signal(SIGHUP, CleanupSignalHandler);
238#endif
239 signal(SIGINT, CleanupSignalHandler);
240 signal(SIGTERM, CleanupSignalHandler);
241
242 init_fltk();
243
244 Configuration::enableViewerParams();
245
DRC5aa06502011-06-23 22:04:46 +0000246 int i = 1;
247 if (!Fl::args(argc, argv, i) || i < argc)
248 for (; i < argc; i++) {
249 if (Configuration::setParam(argv[i]))
250 continue;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000251
DRC5aa06502011-06-23 22:04:46 +0000252 if (argv[i][0] == '-') {
253 if (i+1 < argc) {
254 if (Configuration::setParam(&argv[i][1], argv[i+1])) {
255 i++;
256 continue;
257 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000258 }
DRC5aa06502011-06-23 22:04:46 +0000259 usage(argv[0]);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000260 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000261
DRC5aa06502011-06-23 22:04:46 +0000262 vncServerName = argv[i];
263 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000264
265 if (!::autoSelect.hasBeenSet()) {
266 // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used
267 ::autoSelect.setParam(!::preferredEncoding.hasBeenSet() &&
268 !::fullColour.hasBeenSet() &&
269 !::fullColourAlias.hasBeenSet());
270 }
271 if (!::fullColour.hasBeenSet() && !::fullColourAlias.hasBeenSet()) {
272 // Default to FullColor=0 if AutoSelect=0 && LowColorLevel is set
273 if (!::autoSelect && (::lowColourLevel.hasBeenSet() ||
274 ::lowColourLevelAlias.hasBeenSet())) {
275 ::fullColour.setParam(false);
276 }
277 }
278 if (!::customCompressLevel.hasBeenSet()) {
279 // Default to CustomCompressLevel=1 if CompressLevel is used.
280 ::customCompressLevel.setParam(::compressLevel.hasBeenSet());
281 }
282
283 mkvnchomedir();
284
285 CSecurity::upg = &dlg;
286#ifdef HAVE_GNUTLS
287 CSecurityTLS::msg = &dlg;
288#endif
289
Pierre Ossman561ff0c2011-05-13 14:04:59 +0000290 if (vncServerName == NULL) {
291 vncServerName = ServerDialog::run();
292 if ((vncServerName == NULL) || (vncServerName[0] == '\0'))
293 return 1;
294 }
295
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000296 CConn *cc = new CConn(vncServerName);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000297
298 while (!exitMainloop) {
299 int next_timer;
300
301 next_timer = Timer::checkTimeouts();
302 if (next_timer == 0)
303 next_timer = INT_MAX;
304
305 if (Fl::wait((double)next_timer / 1000.0) < 0.0) {
306 vlog.error(_("Internal FLTK error. Exiting."));
307 break;
308 }
309 }
310
Pierre Ossmane2ef5c12011-07-12 16:56:34 +0000311 delete cc;
312
313 if (exitError != NULL)
314 fl_alert(exitError);
315
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000316 return 0;
317}