From 44b085adee9a0ecf697b48e38d2434d920385928 Mon Sep 17 00:00:00 2001 From: Johannes Date: Sun, 31 Jan 2021 00:12:50 +0100 Subject: [PATCH] Retry connection in case of an error --- vncviewer/CConn.cxx | 12 ++-- vncviewer/DesktopWindow.cxx | 2 +- vncviewer/Viewport.cxx | 50 +++++++++------- vncviewer/parameters.cxx | 4 ++ vncviewer/parameters.h | 1 + vncviewer/touch.cxx | 6 +- vncviewer/vncviewer.cxx | 113 +++++++++++++++++++++++++++--------- vncviewer/vncviewer.h | 8 ++- vncviewer/vncviewer.man | 6 ++ 9 files changed, 141 insertions(+), 61 deletions(-) diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx index e1f5f70a..ebdfbe87 100644 --- a/vncviewer/CConn.cxx +++ b/vncviewer/CConn.cxx @@ -111,8 +111,8 @@ CConn::CConn(const char* vncServerName, network::Socket* socket=NULL) } } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("Failed to connect to \"%s\":\n\n%s"), - vncServerName, e.str()); + abort_connection(_("Failed to connect to \"%s\":\n\n%s"), + vncServerName, e.str()); return; } } @@ -259,7 +259,7 @@ void CConn::socketEvent(FL_SOCKET fd, void *data) Timer::checkTimeouts(); // Also check if we need to stop reading and terminate - if (should_exit()) + if (should_disconnect()) break; } @@ -270,14 +270,14 @@ void CConn::socketEvent(FL_SOCKET fd, void *data) if (!cc->desktop) { vlog.error(_("The connection was dropped by the server before " "the session could be established.")); - exit_vncviewer(_("The connection was dropped by the server " + abort_connection(_("The connection was dropped by the server " "before the session could be established.")); } else { - exit_vncviewer(); + disconnect(); } } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("An unexpected error occurred when communicating " + abort_connection(_("An unexpected error occurred when communicating " "with the server:\n\n%s"), e.str()); } diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 3b4589c9..7936deb7 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -1393,7 +1393,7 @@ void DesktopWindow::repositionWidgets() void DesktopWindow::handleClose(Fl_Widget *wnd, void *data) { - exit_vncviewer(); + disconnect(); } diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index 56bcc314..2f8a92c6 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -103,7 +103,7 @@ static rfb::LogWriter vlog("Viewport"); // Menu constants -enum { ID_EXIT, ID_FULLSCREEN, ID_MINIMIZE, ID_RESIZE, +enum { ID_DISCONNECT, ID_FULLSCREEN, ID_MINIMIZE, ID_RESIZE, ID_CTRL, ID_ALT, ID_MENUKEY, ID_CTRLALTDEL, ID_REFRESH, ID_OPTIONS, ID_INFO, ID_ABOUT }; @@ -572,8 +572,9 @@ int Viewport::handle(int event) cc->sendClipboardData(filtered); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("An unexpected error occurred when communicating " - "with the server:\n\n%s"), e.str()); + abort_connection(_("An unexpected error occurred when " + "communicating with the server:\n\n%s"), + e.str()); } strFree(filtered); @@ -669,8 +670,9 @@ void Viewport::sendPointerEvent(const rfb::Point& pos, int buttonMask) cc->writer()->writePointerEvent(pos, buttonMask); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("An unexpected error occurred when communicating " - "with the server:\n\n%s"), e.str()); + abort_connection(_("An unexpected error occurred when " + "communicating with the server:\n\n%s"), + e.str()); } } else { if (!Fl::has_timeout(handlePointerTimeout, this)) @@ -771,8 +773,9 @@ void Viewport::handleClipboardChange(int source, void *data) self->cc->announceClipboard(true); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("An unexpected error occurred when communicating " - "with the server:\n\n%s"), e.str()); + abort_connection(_("An unexpected error occurred when " + "communicating with the server:\n\n%s"), + e.str()); } } @@ -785,8 +788,9 @@ void Viewport::flushPendingClipboard() cc->requestClipboard(); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("An unexpected error occurred when communicating " - "with the server:\n\n%s"), e.str()); + abort_connection(_("An unexpected error occurred when " + "communicating with the server:\n\n%s"), + e.str()); } } if (pendingClientClipboard) { @@ -795,8 +799,9 @@ void Viewport::flushPendingClipboard() cc->announceClipboard(true); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("An unexpected error occurred when communicating " - "with the server:\n\n%s"), e.str()); + abort_connection(_("An unexpected error occurred when " + "communicating with the server:\n\n%s"), + e.str()); } } @@ -822,8 +827,9 @@ void Viewport::handlePointerTimeout(void *data) self->lastButtonMask); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("An unexpected error occurred when communicating " - "with the server:\n\n%s"), e.str()); + abort_connection(_("An unexpected error occurred when " + "communicating with the server:\n\n%s"), + e.str()); } } @@ -892,8 +898,9 @@ void Viewport::handleKeyPress(int keyCode, rdr::U32 keySym) cc->writer()->writeKeyEvent(keySym, keyCode, true); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("An unexpected error occurred when communicating " - "with the server:\n\n%s"), e.str()); + abort_connection(_("An unexpected error occurred when " + "communicating with the server:\n\n%s"), + e.str()); } } @@ -927,8 +934,9 @@ void Viewport::handleKeyRelease(int keyCode) cc->writer()->writeKeyEvent(iter->second, keyCode, false); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("An unexpected error occurred when communicating " - "with the server:\n\n%s"), e.str()); + abort_connection(_("An unexpected error occurred when " + "communicating with the server:\n\n%s"), + e.str()); } downKeySym.erase(iter); @@ -1239,8 +1247,8 @@ void Viewport::initContextMenu() { contextMenu->clear(); - fltk_menu_add(contextMenu, p_("ContextMenu|", "E&xit viewer"), - 0, NULL, (void*)ID_EXIT, FL_MENU_DIVIDER); + fltk_menu_add(contextMenu, p_("ContextMenu|", "Dis&connect"), + 0, NULL, (void*)ID_DISCONNECT, FL_MENU_DIVIDER); fltk_menu_add(contextMenu, p_("ContextMenu|", "&Full screen"), 0, NULL, (void*)ID_FULLSCREEN, @@ -1314,8 +1322,8 @@ void Viewport::popupContextMenu() return; switch (m->argument()) { - case ID_EXIT: - exit_vncviewer(); + case ID_DISCONNECT: + disconnect(); break; case ID_FULLSCREEN: if (window()->fullscreen_active()) diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx index 46ad132f..c589a2f0 100644 --- a/vncviewer/parameters.cxx +++ b/vncviewer/parameters.cxx @@ -69,6 +69,10 @@ BoolParameter alertOnFatalError("AlertOnFatalError", "Give a dialog on connection problems rather " "than exiting immediately", true); +BoolParameter reconnectOnError("ReconnectOnError", + "Give a dialog on connection problems rather " + "than exiting immediately and ask for a reconnect.", true); + StringParameter passwordFile("PasswordFile", "Password file for VNC authentication", ""); AliasParameter passwd("passwd", "Alias for PasswordFile", &passwordFile); diff --git a/vncviewer/parameters.h b/vncviewer/parameters.h index 614e577f..1dd9aca2 100644 --- a/vncviewer/parameters.h +++ b/vncviewer/parameters.h @@ -74,6 +74,7 @@ extern rfb::StringParameter menuKey; extern rfb::BoolParameter fullscreenSystemKeys; extern rfb::BoolParameter alertOnFatalError; +extern rfb::BoolParameter reconnectOnError; #ifndef WIN32 extern rfb::StringParameter via; diff --git a/vncviewer/touch.cxx b/vncviewer/touch.cxx index 3d23aa58..5ba9d108 100644 --- a/vncviewer/touch.cxx +++ b/vncviewer/touch.cxx @@ -181,7 +181,7 @@ static int handleTouchEvent(void *event, void *data) handlers[msg->hwnd] = new Win32TouchHandler(msg->hwnd); } catch (rfb::Exception& e) { vlog.error(_("Failed to create touch handler: %s"), e.str()); - exit_vncviewer(_("Failed to create touch handler: %s"), e.str()); + abort_vncviewer(_("Failed to create touch handler: %s"), e.str()); } // Add a special hook-in for handling events sent directly to WndProc if (!SetWindowSubclass(msg->hwnd, &win32WindowProc, 1, 0)) { @@ -248,14 +248,14 @@ void enable_touch() fl_open_display(); if (!XQueryExtension(fl_display, "XInputExtension", &xi_major, &ev, &err)) { - exit_vncviewer(_("X Input extension not available.")); + abort_vncviewer(_("X Input extension not available.")); return; // Not reached } major_ver = 2; minor_ver = 2; if (XIQueryVersion(fl_display, &major_ver, &minor_ver) != Success) { - exit_vncviewer(_("X Input 2 (or newer) is not available.")); + abort_vncviewer(_("X Input 2 (or newer) is not available.")); return; // Not reached } diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx index c93a6f75..1092f1f4 100644 --- a/vncviewer/vncviewer.cxx +++ b/vncviewer/vncviewer.cxx @@ -22,6 +22,7 @@ #include #endif +#include #include #include #include @@ -87,7 +88,8 @@ static const char *argv0 = NULL; static bool inMainloop = false; static bool exitMainloop = false; -static const char *exitError = NULL; +static char *exitError = NULL; +static bool fatalError = false; static const char *about_text() { @@ -107,17 +109,19 @@ static const char *about_text() return buffer; } -void exit_vncviewer(const char *error, ...) + +void abort_vncviewer(const char *error, ...) { + fatalError = true; + // Prioritise the first error we get as that is probably the most // relevant one. - if ((error != NULL) && (exitError == NULL)) { + if (exitError == NULL) { va_list ap; va_start(ap, error); exitError = (char*)malloc(1024); - if (exitError) - (void) vsnprintf((char*)exitError, 1024, error, ap); + vsnprintf(exitError, 1024, error, ap); va_end(ap); } @@ -131,7 +135,30 @@ void exit_vncviewer(const char *error, ...) } } -bool should_exit() +void abort_connection(const char *error, ...) +{ + assert(inMainloop); + + // Prioritise the first error we get as that is probably the most + // relevant one. + if (exitError == NULL) { + va_list ap; + + va_start(ap, error); + exitError = (char*)malloc(1024); + vsnprintf(exitError, 1024, error, ap); + va_end(ap); + } + + exitMainloop = true; +} + +void disconnect() +{ + exitMainloop = true; +} + +bool should_disconnect() { return exitMainloop; } @@ -142,17 +169,57 @@ void about_vncviewer() fl_message("%s", about_text()); } -void run_mainloop() +static void mainloop(const char* vncserver, network::Socket* sock) { - int next_timer; + while (true) { + CConn *cc; - next_timer = Timer::checkTimeouts(); - if (next_timer == 0) - next_timer = INT_MAX; + exitMainloop = false; - if (Fl::wait((double)next_timer / 1000.0) < 0.0) { - vlog.error(_("Internal FLTK error. Exiting.")); - exit(-1); + cc = new CConn(vncServerName, sock); + + while (!exitMainloop) { + int next_timer; + + next_timer = Timer::checkTimeouts(); + if (next_timer == 0) + next_timer = INT_MAX; + + if (Fl::wait((double)next_timer / 1000.0) < 0.0) { + vlog.error(_("Internal FLTK error. Exiting.")); + exit(-1); + } + } + + delete cc; + + if (fatalError) { + assert(exitError != NULL); + if (alertOnFatalError) + fl_alert("%s", exitError); + break; + } + + if (exitError == NULL) + break; + + if(reconnectOnError && (sock == NULL)) { + int ret; + ret = fl_choice(_("%s\n\n" + "Attempt to reconnect?"), + fl_yes, fl_no, 0, exitError); + free(exitError); + exitError = NULL; + if (ret == 0) + continue; + else + break; + } + + if (alertOnFatalError) + fl_alert("%s", exitError); + + break; } } @@ -429,8 +496,8 @@ potentiallyLoadConfigurationFile(char *vncServerName) vncServerName[VNCSERVERNAMELEN-1] = '\0'; } catch (rfb::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("Error reading configuration file \"%s\":\n\n%s"), - vncServerName, e.str()); + abort_vncviewer(_("Error reading configuration file \"%s\":\n\n%s"), + vncServerName, e.str()); } } } @@ -640,7 +707,7 @@ int main(int argc, char** argv) // TRANSLATORS: "Parameters" are command line arguments, or settings // from a file or the Windows registry. vlog.error(_("Parameters -listen and -via are incompatible")); - exit_vncviewer(_("Parameters -listen and -via are incompatible")); + abort_vncviewer(_("Parameters -listen and -via are incompatible")); return 1; /* Not reached */ } #endif @@ -687,7 +754,7 @@ int main(int argc, char** argv) } } catch (rdr::Exception& e) { vlog.error("%s", e.str()); - exit_vncviewer(_("Failure waiting for incoming VNC connection:\n\n%s"), e.str()); + abort_vncviewer(_("Failure waiting for incoming VNC connection:\n\n%s"), e.str()); return 1; /* Not reached */ } @@ -708,17 +775,9 @@ int main(int argc, char** argv) #endif } - CConn *cc = new CConn(vncServerName, sock); - inMainloop = true; - while (!exitMainloop) - run_mainloop(); + mainloop(vncServerName, sock); inMainloop = false; - delete cc; - - if (exitError != NULL && alertOnFatalError) - fl_alert("%s", exitError); - return 0; } diff --git a/vncviewer/vncviewer.h b/vncviewer/vncviewer.h index 494611b7..57eb91fb 100644 --- a/vncviewer/vncviewer.h +++ b/vncviewer/vncviewer.h @@ -27,9 +27,11 @@ # define __printf_attr(a, b) #endif // __GNUC__ -void exit_vncviewer(const char *error = NULL, ...) __printf_attr(1, 2); -bool should_exit(); +void abort_vncviewer(const char *error, ...) __printf_attr(1, 2); +void abort_connection(const char *error, ...) __printf_attr(1, 2); +void disconnect(); +bool should_disconnect(); + void about_vncviewer(); -void run_mainloop(); #endif diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man index 062bba9b..894787c0 100644 --- a/vncviewer/vncviewer.man +++ b/vncviewer/vncviewer.man @@ -328,6 +328,12 @@ respectively. .TP .B \-AlertOnFatalError Display a dialog with any fatal error before exiting. Default is on. +. +.TP +.B \-ReconnectOnError +Display a dialog with any error and offer the possibility to retry +establishing the connection. In case this is off no dialog to +re-connect will be offered. Default is on. .SH FILES .TP -- 2.39.5