From c084e586927ff040014d6a8ea8c519b3a9a368d2 Mon Sep 17 00:00:00 2001 From: Hugo Lundin Date: Thu, 15 Jul 2021 12:52:56 +0200 Subject: [PATCH] Add fullscreen mode parameter Before this commit, `FullScreen` and `FullScreenAllMonitors` could be used to configure whether to use the current monitor in fullscreen, or all monitors in fullscreen. This commit deprecates `FullScreenAllMonitors` in favour of `FullScreenMode` (which can either be `current` or `all`). This allows for additional modes to be added, without the risk of having invalid states (for example two activate two different fullscreen modes at the same time). A new concept has been added; read-only parameters. They are parameters that will be read, but never written back. This allows for migration paths to be constructed, where a parameter can be taken to consideration but then for example be discarded, logged or changed into something else. This has been used for `FullScreenAllMonitors` to provide a migration path. On startup of vncviewer, if `FullScreenAllMonitors` is enabled, `FullScreenMode=all` will be automatically enabled instead. The next time the configuration file is written to disk, `FullScreenAllMonitors` will then be removed. --- vncviewer/DesktopWindow.cxx | 7 +- vncviewer/OptionsDialog.cxx | 49 +++++++++-- vncviewer/OptionsDialog.h | 4 +- vncviewer/parameters.cxx | 166 ++++++++++++++++++++++++------------ vncviewer/parameters.h | 3 +- vncviewer/vncviewer.cxx | 12 +++ vncviewer/vncviewer.man | 10 ++- 7 files changed, 182 insertions(+), 69 deletions(-) diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 8c85c4d9..1fdce182 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -880,10 +880,11 @@ int DesktopWindow::fltkHandle(int event, Fl_Window *win) return ret; } - void DesktopWindow::fullscreen_on() { - if (not fullScreenAllMonitors) + bool all_monitors = !strcasecmp(fullScreenMode, "all"); + + if (not all_monitors) fullscreen_screens(-1, -1, -1, -1); else { int top, bottom, left, right; @@ -1377,7 +1378,7 @@ void DesktopWindow::handleOptions(void *data) self->ungrabKeyboard(); // Call fullscreen_on even if active since it handles - // fullScreenAllMonitors + // fullScreenMode if (fullScreen) self->fullscreen_on(); else if (!fullScreen && self->fullscreen_active()) diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx index 872a49cf..cbf4571d 100644 --- a/vncviewer/OptionsDialog.cxx +++ b/vncviewer/OptionsDialog.cxx @@ -297,7 +297,12 @@ void OptionsDialog::loadOptions(void) } remoteResizeCheckbox->value(remoteResize); fullScreenCheckbox->value(fullScreen); - fullScreenAllMonitorsCheckbox->value(fullScreenAllMonitors); + + if (!strcasecmp(fullScreenMode, "all")) { + allMonitorsButton->setonly(); + } else { + currentMonitorButton->setonly(); + } handleDesktopSize(desktopSizeCheckbox, this); @@ -407,7 +412,12 @@ void OptionsDialog::storeOptions(void) } remoteResize.setParam(remoteResizeCheckbox->value()); fullScreen.setParam(fullScreenCheckbox->value()); - fullScreenAllMonitors.setParam(fullScreenAllMonitorsCheckbox->value()); + + if (allMonitorsButton->value()) { + fullScreenMode.setParam("All"); + } else { + fullScreenMode.setParam("Current"); + } /* Misc. */ shared.setParam(sharedCheckbox->value()); @@ -755,6 +765,7 @@ void OptionsDialog::createInputPage(int tx, int ty, int tw, int th) void OptionsDialog::createScreenPage(int tx, int ty, int tw, int th) { int x; + int width, height; Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Screen")); @@ -783,15 +794,35 @@ void OptionsDialog::createScreenPage(int tx, int ty, int tw, int th) fullScreenCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty, CHECK_MIN_WIDTH, CHECK_HEIGHT, - _("Full-screen mode"))); - ty += CHECK_HEIGHT + TIGHT_MARGIN; + _("Enable full-screen"))); + ty += CHECK_HEIGHT + INNER_MARGIN; - fullScreenAllMonitorsCheckbox = new Fl_Check_Button(LBLRIGHT(tx + INDENT, ty, - CHECK_MIN_WIDTH, - CHECK_HEIGHT, - _("Enable full-screen mode over all monitors"))); - ty += CHECK_HEIGHT + TIGHT_MARGIN; + width = tw - OUTER_MARGIN * 2; + height = th - ty + OUTER_MARGIN * 3; + Fl_Group *fullScreenModeGroup = new Fl_Group(tx, + ty, + width, + height); + + { + tx += INDENT; + + currentMonitorButton = new Fl_Round_Button(LBLRIGHT(tx, ty, + RADIO_MIN_WIDTH, + RADIO_HEIGHT, + _("Use current monitor"))); + currentMonitorButton->type(FL_RADIO_BUTTON); + ty += RADIO_HEIGHT + TIGHT_MARGIN; + + allMonitorsButton = new Fl_Round_Button(LBLRIGHT(tx, ty, + RADIO_MIN_WIDTH, + RADIO_HEIGHT, + _("Use all monitors"))); + allMonitorsButton->type(FL_RADIO_BUTTON); + ty += RADIO_HEIGHT + TIGHT_MARGIN; + } + fullScreenModeGroup->end(); group->end(); } diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h index fd3b9500..1c78ca16 100644 --- a/vncviewer/OptionsDialog.h +++ b/vncviewer/OptionsDialog.h @@ -125,7 +125,9 @@ protected: Fl_Int_Input *desktopHeightInput; Fl_Check_Button *remoteResizeCheckbox; Fl_Check_Button *fullScreenCheckbox; - Fl_Check_Button *fullScreenAllMonitorsCheckbox; + + Fl_Round_Button *currentMonitorButton; + Fl_Round_Button *allMonitorsButton; /* Misc. */ Fl_Check_Button *sharedCheckbox; diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx index b28a1112..1525f13f 100644 --- a/vncviewer/parameters.cxx +++ b/vncviewer/parameters.cxx @@ -101,10 +101,13 @@ IntParameter qualityLevel("QualityLevel", 8); BoolParameter maximize("Maximize", "Maximize viewer window", false); -BoolParameter fullScreen("FullScreen", "Full screen mode", false); +BoolParameter fullScreen("FullScreen", "Enable full screen", false); +StringParameter fullScreenMode("FullScreenMode", "Specify which monitors to use when in full screen. " + "Should be either Current or All", + "Current"); BoolParameter fullScreenAllMonitors("FullScreenAllMonitors", - "Enable full screen over all monitors", - true); + "[DEPRECATED] Enable full screen over all monitors", + false); StringParameter desktopSize("DesktopSize", "Reconfigure desktop size on the server on " "connect (if possible)", ""); @@ -175,7 +178,7 @@ static VoidParameter* parameterArray[] = { &noJpeg, &qualityLevel, &fullScreen, - &fullScreenAllMonitors, + &fullScreenMode, &desktopSize, &remoteResize, &viewOnly, @@ -190,6 +193,10 @@ static VoidParameter* parameterArray[] = { &fullscreenSystemKeys }; +static VoidParameter* readOnlyParameterArray[] = { + &fullScreenAllMonitors +}; + // Encoding Table static struct { const char first; @@ -399,6 +406,30 @@ static bool getKeyInt(const char* _name, int* dest, HKEY* hKey) { return true; } +static bool removeValue(const char* _name, HKEY* hKey) { + const DWORD buffersize = 256; + wchar_t name[buffersize]; + + unsigned size = fl_utf8towc(_name, strlen(_name)+1, name, buffersize); + if (size >= buffersize) { + vlog.error(_("The name of the parameter %s was too large to remove from the registry"), _name); + return false; + } + + LONG res = RegDeleteValueW(*hKey, name); + if (res != ERROR_SUCCESS) { + if (res == ERROR_FILE_NOT_FOUND) { + // The value does not exist, no need to remove it. + return true; + } else { + vlog.error(_("Failed to remove parameter %s from the registry: %ld"), + _name, res); + return false; + } + } + + return true; +} void saveHistoryToRegKey(const vector& serverHistory) { HKEY hKey; @@ -456,6 +487,12 @@ static void saveToReg(const char* servername) { } } + // Remove read-only parameters to replicate the behaviour of Linux/macOS when they + // store a config to disk. If the parameter hasn't been migrated at this point it + // will be lost. + for (size_t i = 0; i < sizeof(readOnlyParameterArray)/sizeof(VoidParameter*); i++) + removeValue(readOnlyParameterArray[i]->getName(), &hKey); + res = RegCloseKey(hKey); if (res != ERROR_SUCCESS) { vlog.error(_("Failed to close registry key: %ld"), res); @@ -500,6 +537,29 @@ void loadHistoryFromRegKey(vector& serverHistory) { } } +static void findAndSetViewerParametersFromReg(VoidParameter* parameters[], size_t parameters_len, HKEY* hKey) { + + const size_t buffersize = 256; + int intValue = 0; + char stringValue[buffersize]; + + for (size_t i = 0; i < parameters_len/sizeof(VoidParameter*); i++) { + if (dynamic_cast(parameters[i]) != NULL) { + if (getKeyString(parameters[i]->getName(), stringValue, buffersize, hKey)) + parameters[i]->setParam(stringValue); + } else if (dynamic_cast(parameters[i]) != NULL) { + if (getKeyInt(parameters[i]->getName(), &intValue, hKey)) + ((IntParameter*)parameters[i])->setParam(intValue); + } else if (dynamic_cast(parameters[i]) != NULL) { + if (getKeyInt(parameters[i]->getName(), &intValue, hKey)) + ((BoolParameter*)parameters[i])->setParam(intValue); + } else { + vlog.error(_("Unknown parameter type for parameter %s"), + parameters[i]->getName()); + } + } +} + static char* loadFromReg() { HKEY hKey; @@ -523,24 +583,8 @@ static char* loadFromReg() { if (getKeyString("ServerName", servernameBuffer, buffersize, &hKey)) snprintf(servername, buffersize, "%s", servernameBuffer); - int intValue = 0; - char stringValue[buffersize]; - - for (size_t i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) { - if (dynamic_cast(parameterArray[i]) != NULL) { - if (getKeyString(parameterArray[i]->getName(), stringValue, buffersize, &hKey)) - parameterArray[i]->setParam(stringValue); - } else if (dynamic_cast(parameterArray[i]) != NULL) { - if (getKeyInt(parameterArray[i]->getName(), &intValue, &hKey)) - ((IntParameter*)parameterArray[i])->setParam(intValue); - } else if (dynamic_cast(parameterArray[i]) != NULL) { - if (getKeyInt(parameterArray[i]->getName(), &intValue, &hKey)) - ((BoolParameter*)parameterArray[i])->setParam(intValue); - } else { - vlog.error(_("Unknown parameter type for parameter %s"), - parameterArray[i]->getName()); - } - } + findAndSetViewerParametersFromReg(parameterArray, sizeof(parameterArray), &hKey); + findAndSetViewerParametersFromReg(readOnlyParameterArray, sizeof(readOnlyParameterArray), &hKey); res = RegCloseKey(hKey); if (res != ERROR_SUCCESS){ @@ -607,6 +651,48 @@ void saveViewerParameters(const char *filename, const char *servername) { fclose(f); } +static bool findAndSetViewerParameterFromValue( + VoidParameter* parameters[], size_t parameters_len, + char* value, char* line, int lineNr, char* filepath) +{ + const size_t buffersize = 256; + char decodingBuffer[buffersize]; + + // Find and set the correct parameter + for (size_t i = 0; i < parameters_len/sizeof(VoidParameter*); i++) { + + if (dynamic_cast(parameters[i]) != NULL) { + if (strcasecmp(line, ((StringParameter*)parameters[i])->getName()) == 0) { + + if(!decodeValue(value, decodingBuffer, sizeof(decodingBuffer))) { + vlog.error(_("Failed to read line %d in file %s: %s"), + lineNr, filepath, _("Invalid format or too large value")); + continue; + } + ((StringParameter*)parameters[i])->setParam(decodingBuffer); + return false; + } + + } else if (dynamic_cast(parameters[i]) != NULL) { + if (strcasecmp(line, ((IntParameter*)parameters[i])->getName()) == 0) { + ((IntParameter*)parameters[i])->setParam(atoi(value)); + return false; + } + + } else if (dynamic_cast(parameters[i]) != NULL) { + if (strcasecmp(line, ((BoolParameter*)parameters[i])->getName()) == 0) { + ((BoolParameter*)parameters[i])->setParam(atoi(value)); + return false; + } + + } else { + vlog.error(_("Unknown parameter type for parameter %s"), + parameters[i]->getName()); + } + } + + return true; +} char* loadViewerParameters(const char *filename) { @@ -705,38 +791,12 @@ char* loadViewerParameters(const char *filename) { invalidParameterName = false; } else { + invalidParameterName = findAndSetViewerParameterFromValue(parameterArray, sizeof(parameterArray), + value, line, lineNr, filepath); - // Find and set the correct parameter - for (size_t i = 0; i < sizeof(parameterArray)/sizeof(VoidParameter*); i++) { - - if (dynamic_cast(parameterArray[i]) != NULL) { - if (strcasecmp(line, ((StringParameter*)parameterArray[i])->getName()) == 0) { - - if(!decodeValue(value, decodingBuffer, sizeof(decodingBuffer))) { - vlog.error(_("Failed to read line %d in file %s: %s"), - lineNr, filepath, _("Invalid format or too large value")); - continue; - } - ((StringParameter*)parameterArray[i])->setParam(decodingBuffer); - invalidParameterName = false; - } - - } else if (dynamic_cast(parameterArray[i]) != NULL) { - if (strcasecmp(line, ((IntParameter*)parameterArray[i])->getName()) == 0) { - ((IntParameter*)parameterArray[i])->setParam(atoi(value)); - invalidParameterName = false; - } - - } else if (dynamic_cast(parameterArray[i]) != NULL) { - if (strcasecmp(line, ((BoolParameter*)parameterArray[i])->getName()) == 0) { - ((BoolParameter*)parameterArray[i])->setParam(atoi(value)); - invalidParameterName = false; - } - - } else { - vlog.error(_("Unknown parameter type for parameter %s"), - parameterArray[i]->getName()); - } + if (invalidParameterName) { + invalidParameterName = findAndSetViewerParameterFromValue(readOnlyParameterArray, sizeof(readOnlyParameterArray), + value, line, lineNr, filepath); } } diff --git a/vncviewer/parameters.h b/vncviewer/parameters.h index 3d8ac24f..fee37ee8 100644 --- a/vncviewer/parameters.h +++ b/vncviewer/parameters.h @@ -49,7 +49,8 @@ extern rfb::IntParameter qualityLevel; extern rfb::BoolParameter maximize; extern rfb::BoolParameter fullScreen; -extern rfb::BoolParameter fullScreenAllMonitors; +extern rfb::StringParameter fullScreenMode; +extern rfb::BoolParameter fullScreenAllMonitors; // deprecated extern rfb::StringParameter desktopSize; extern rfb::StringParameter geometry; extern rfb::BoolParameter remoteResize; diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx index bb41a2f7..6a84c7cb 100644 --- a/vncviewer/vncviewer.cxx +++ b/vncviewer/vncviewer.cxx @@ -435,6 +435,16 @@ potentiallyLoadConfigurationFile(char *vncServerName) } } +static void +migrateDeprecatedOptions() +{ + if (fullScreenAllMonitors) { + vlog.info(_("FullScreenAllMonitors is deprecated, set FullScreenMode to 'all' instead.")); + + fullScreenMode.setParam("all"); + } +} + #ifndef WIN32 static int interpretViaParam(char *remoteHost, int *remotePort, int localPort) @@ -613,6 +623,8 @@ int main(int argc, char** argv) // Check if the server name in reality is a configuration file potentiallyLoadConfigurationFile(vncServerName); + migrateDeprecatedOptions(); + mkvnchomedir(); CSecurity::upg = &dlg; diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man index 868e6aeb..ff81b914 100644 --- a/vncviewer/vncviewer.man +++ b/vncviewer/vncviewer.man @@ -205,9 +205,15 @@ Maximize viewer window. Start in full-screen mode. . .TP -.B \-FullScreenAllMonitors +.B \-FullScreenAllMonitors (DEPRECATED) Use all local monitors and not just the current one when switching to -full-screen mode. +full-screen mode. Replaced by \fB-FullScreenMode=all\fP +. +.TP +.B \-FullScreenMode \fImode\fP +Specify which monitors to use when in full screen. It should be either "Current" or "All". +The default is "Current". +. . .TP .B \-FullscreenSystemKeys -- 2.39.5