diff options
Diffstat (limited to 'vncviewer/OptionsDialog.cxx')
-rw-r--r-- | vncviewer/OptionsDialog.cxx | 382 |
1 files changed, 282 insertions, 100 deletions
diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx index c5f21b24..3ba6fba1 100644 --- a/vncviewer/OptionsDialog.cxx +++ b/vncviewer/OptionsDialog.cxx @@ -1,4 +1,4 @@ -/* Copyright 2011-2021 Pierre Ossman <ossman@cendio.se> for Cendio AB +/* Copyright 2011-2025 Pierre Ossman <ossman@cendio.se> for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #include <stdlib.h> #include <list> +#include <core/string.h> + #include <rfb/encodings.h> #if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) @@ -35,8 +37,8 @@ #endif #include "OptionsDialog.h" +#include "ShortcutHandler.h" #include "i18n.h" -#include "menukey.h" #include "parameters.h" #include "fltk/layout.h" @@ -44,18 +46,21 @@ #include "fltk/Fl_Monitor_Arrangement.h" #include "fltk/Fl_Navigation.h" +#ifdef __APPLE__ +#include "cocoa.h" +#endif + #include <FL/Fl.H> +#include <FL/Fl_Box.H> #include <FL/Fl_Tabs.H> #include <FL/Fl_Button.H> #include <FL/Fl_Check_Button.H> #include <FL/Fl_Return_Button.H> #include <FL/Fl_Round_Button.H> +#include <FL/Fl_Toggle_Button.H> #include <FL/Fl_Int_Input.H> #include <FL/Fl_Choice.H> -using namespace std; -using namespace rfb; - std::map<OptionsCallback*, void*> OptionsDialog::callbacks; static std::set<OptionsDialog *> instances; @@ -85,6 +90,7 @@ OptionsDialog::OptionsDialog() createCompressionPage(tx, ty, tw, th); createSecurityPage(tx, ty, tw, th); createInputPage(tx, ty, tw, th); + createShortcutsPage(tx, ty, tw, th); createDisplayPage(tx, ty, tw, th); createMiscPage(tx, ty, tw, th); } @@ -162,27 +168,18 @@ void OptionsDialog::loadOptions(void) /* Compression */ autoselectCheckbox->value(autoSelect); - int encNum = encodingNum(preferredEncoding); - - switch (encNum) { - case encodingTight: + if (preferredEncoding == "Tight") tightButton->setonly(); - break; - case encodingZRLE: + else if (preferredEncoding == "ZRLE") zrleButton->setonly(); - break; - case encodingHextile: + else if (preferredEncoding == "Hextile") hextileButton->setonly(); - break; #ifdef HAVE_H264 - case encodingH264: + else if (preferredEncoding == "H.264") h264Button->setonly(); - break; #endif - case encodingRaw: + else if (preferredEncoding == "Raw") rawButton->setonly(); - break; - } if (fullColour) fullcolorCheckbox->setonly(); @@ -215,11 +212,11 @@ void OptionsDialog::loadOptions(void) #if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) /* Security */ - Security security(SecurityClient::secTypes); + rfb::Security security(rfb::SecurityClient::secTypes); - list<uint8_t> secTypes; + std::list<uint8_t> secTypes; - list<uint32_t> secTypesExt; + std::list<uint32_t> secTypesExt; encNoneCheckbox->value(false); #ifdef HAVE_GNUTLS @@ -237,11 +234,11 @@ void OptionsDialog::loadOptions(void) secTypes = security.GetEnabledSecTypes(); for (uint8_t type : secTypes) { switch (type) { - case secTypeNone: + case rfb::secTypeNone: encNoneCheckbox->value(true); authNoneCheckbox->value(true); break; - case secTypeVncAuth: + case rfb::secTypeVncAuth: encNoneCheckbox->value(true); authVncCheckbox->value(true); break; @@ -251,49 +248,49 @@ void OptionsDialog::loadOptions(void) secTypesExt = security.GetEnabledExtSecTypes(); for (uint32_t type : secTypesExt) { switch (type) { - case secTypePlain: + case rfb::secTypePlain: encNoneCheckbox->value(true); authPlainCheckbox->value(true); break; #ifdef HAVE_GNUTLS - case secTypeTLSNone: + case rfb::secTypeTLSNone: encTLSCheckbox->value(true); authNoneCheckbox->value(true); break; - case secTypeTLSVnc: + case rfb::secTypeTLSVnc: encTLSCheckbox->value(true); authVncCheckbox->value(true); break; - case secTypeTLSPlain: + case rfb::secTypeTLSPlain: encTLSCheckbox->value(true); authPlainCheckbox->value(true); break; - case secTypeX509None: + case rfb::secTypeX509None: encX509Checkbox->value(true); authNoneCheckbox->value(true); break; - case secTypeX509Vnc: + case rfb::secTypeX509Vnc: encX509Checkbox->value(true); authVncCheckbox->value(true); break; - case secTypeX509Plain: + case rfb::secTypeX509Plain: encX509Checkbox->value(true); authPlainCheckbox->value(true); break; #endif #ifdef HAVE_NETTLE - case secTypeRA2: - case secTypeRA256: + case rfb::secTypeRA2: + case rfb::secTypeRA256: encRSAAESCheckbox->value(true); authVncCheckbox->value(true); authPlainCheckbox->value(true); break; - case secTypeRA2ne: - case secTypeRAne256: + case rfb::secTypeRA2ne: + case rfb::secTypeRAne256: authVncCheckbox->value(true); /* fall through */ - case secTypeDH: - case secTypeMSLogonII: + case rfb::secTypeDH: + case rfb::secTypeMSLogonII: encNoneCheckbox->value(true); authPlainCheckbox->value(true); break; @@ -303,16 +300,14 @@ void OptionsDialog::loadOptions(void) } #ifdef HAVE_GNUTLS - caInput->value(CSecurityTLS::X509CA); - crlInput->value(CSecurityTLS::X509CRL); + caInput->value(rfb::CSecurityTLS::X509CA); + crlInput->value(rfb::CSecurityTLS::X509CRL); handleX509(encX509Checkbox, this); #endif #endif /* Input */ - const char *menuKeyBuf; - viewOnlyCheckbox->value(viewOnly); emulateMBCheckbox->value(emulateMiddleButton); acceptClipboardCheckbox->value(acceptClipboard); @@ -325,34 +320,48 @@ void OptionsDialog::loadOptions(void) #endif systemKeysCheckbox->value(fullscreenSystemKeys); - menuKeyChoice->value(0); + /* Keyboard shortcuts */ + unsigned modifierMask; + + modifierMask = 0; + for (core::EnumListEntry key : shortcutModifiers) + modifierMask |= ShortcutHandler::parseModifier(key.getValueStr().c_str()); + + ctrlButton->value(modifierMask & ShortcutHandler::Control); + shiftButton->value(modifierMask & ShortcutHandler::Shift); + altButton->value(modifierMask & ShortcutHandler::Alt); + superButton->value(modifierMask & ShortcutHandler::Super); - menuKeyBuf = menuKey; - for (int idx = 0; idx < getMenuKeySymbolCount(); idx++) - if (!strcmp(getMenuKeySymbols()[idx].name, menuKeyBuf)) - menuKeyChoice->value(idx + 1); + handleModifier(nullptr, this); /* Display */ if (!fullScreen) { windowedButton->setonly(); } else { - if (!strcasecmp(fullScreenMode, "all")) { + if (fullScreenMode == "all") { allMonitorsButton->setonly(); - } else if (!strcasecmp(fullScreenMode, "selected")) { + } else if (fullScreenMode == "selected") { selectedMonitorsButton->setonly(); } else { currentMonitorButton->setonly(); } } - monitorArrangement->value(fullScreenSelectedMonitors.getParam()); + monitorArrangement->value(fullScreenSelectedMonitors.getMonitors()); handleFullScreenMode(selectedMonitorsButton, this); /* Misc. */ sharedCheckbox->value(shared); reconnectCheckbox->value(reconnectOnError); - dotCursorCheckbox->value(dotWhenNoCursor); + alwaysCursorCheckbox->value(alwaysCursor); + if (cursorType == "system") { + cursorTypeChoice->value(1); + } else { + // Default + cursorTypeChoice->value(0); + } + handleAlwaysCursor(alwaysCursorCheckbox, this); } @@ -362,17 +371,17 @@ void OptionsDialog::storeOptions(void) autoSelect.setParam(autoselectCheckbox->value()); if (tightButton->value()) - preferredEncoding.setParam(encodingName(encodingTight)); + preferredEncoding.setParam(rfb::encodingName(rfb::encodingTight)); else if (zrleButton->value()) - preferredEncoding.setParam(encodingName(encodingZRLE)); + preferredEncoding.setParam(rfb::encodingName(rfb::encodingZRLE)); else if (hextileButton->value()) - preferredEncoding.setParam(encodingName(encodingHextile)); + preferredEncoding.setParam(rfb::encodingName(rfb::encodingHextile)); #ifdef HAVE_H264 else if (h264Button->value()) - preferredEncoding.setParam(encodingName(encodingH264)); + preferredEncoding.setParam(rfb::encodingName(rfb::encodingH264)); #endif else if (rawButton->value()) - preferredEncoding.setParam(encodingName(encodingRaw)); + preferredEncoding.setParam(rfb::encodingName(rfb::encodingRaw)); fullColour.setParam(fullcolorCheckbox->value()); if (verylowcolorCheckbox->value()) @@ -389,26 +398,26 @@ void OptionsDialog::storeOptions(void) #if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE) /* Security */ - Security security; + rfb::Security security; /* Process security types which don't use encryption */ if (encNoneCheckbox->value()) { if (authNoneCheckbox->value()) - security.EnableSecType(secTypeNone); + security.EnableSecType(rfb::secTypeNone); if (authVncCheckbox->value()) { - security.EnableSecType(secTypeVncAuth); + security.EnableSecType(rfb::secTypeVncAuth); #ifdef HAVE_NETTLE - security.EnableSecType(secTypeRA2ne); - security.EnableSecType(secTypeRAne256); + security.EnableSecType(rfb::secTypeRA2ne); + security.EnableSecType(rfb::secTypeRAne256); #endif } if (authPlainCheckbox->value()) { - security.EnableSecType(secTypePlain); + security.EnableSecType(rfb::secTypePlain); #ifdef HAVE_NETTLE - security.EnableSecType(secTypeRA2ne); - security.EnableSecType(secTypeRAne256); - security.EnableSecType(secTypeDH); - security.EnableSecType(secTypeMSLogonII); + security.EnableSecType(rfb::secTypeRA2ne); + security.EnableSecType(rfb::secTypeRAne256); + security.EnableSecType(rfb::secTypeDH); + security.EnableSecType(rfb::secTypeMSLogonII); #endif } } @@ -417,34 +426,34 @@ void OptionsDialog::storeOptions(void) /* Process security types which use TLS encryption */ if (encTLSCheckbox->value()) { if (authNoneCheckbox->value()) - security.EnableSecType(secTypeTLSNone); + security.EnableSecType(rfb::secTypeTLSNone); if (authVncCheckbox->value()) - security.EnableSecType(secTypeTLSVnc); + security.EnableSecType(rfb::secTypeTLSVnc); if (authPlainCheckbox->value()) - security.EnableSecType(secTypeTLSPlain); + security.EnableSecType(rfb::secTypeTLSPlain); } /* Process security types which use X509 encryption */ if (encX509Checkbox->value()) { if (authNoneCheckbox->value()) - security.EnableSecType(secTypeX509None); + security.EnableSecType(rfb::secTypeX509None); if (authVncCheckbox->value()) - security.EnableSecType(secTypeX509Vnc); + security.EnableSecType(rfb::secTypeX509Vnc); if (authPlainCheckbox->value()) - security.EnableSecType(secTypeX509Plain); + security.EnableSecType(rfb::secTypeX509Plain); } - CSecurityTLS::X509CA.setParam(caInput->value()); - CSecurityTLS::X509CRL.setParam(crlInput->value()); + rfb::CSecurityTLS::X509CA.setParam(caInput->value()); + rfb::CSecurityTLS::X509CRL.setParam(crlInput->value()); #endif #ifdef HAVE_NETTLE if (encRSAAESCheckbox->value()) { - security.EnableSecType(secTypeRA2); - security.EnableSecType(secTypeRA256); + security.EnableSecType(rfb::secTypeRA2); + security.EnableSecType(rfb::secTypeRA256); } #endif - SecurityClient::secTypes.setParam(security.ToString()); + rfb::SecurityClient::secTypes.setParam(security.ToString()); #endif /* Input */ @@ -460,11 +469,23 @@ void OptionsDialog::storeOptions(void) #endif fullscreenSystemKeys.setParam(systemKeysCheckbox->value()); - if (menuKeyChoice->value() == 0) - menuKey.setParam(""); - else { - menuKey.setParam(menuKeyChoice->text()); - } + /* Keyboard shortcuts */ + std::list<std::string> modifierList; + + if (ctrlButton->value()) + modifierList.push_back( + ShortcutHandler::modifierString(ShortcutHandler::Control)); + if (shiftButton->value()) + modifierList.push_back( + ShortcutHandler::modifierString(ShortcutHandler::Shift)); + if (altButton->value()) + modifierList.push_back( + ShortcutHandler::modifierString(ShortcutHandler::Alt)); + if (superButton->value()) + modifierList.push_back( + ShortcutHandler::modifierString(ShortcutHandler::Super)); + + shortcutModifiers.setParam(modifierList); /* Display */ if (windowedButton->value()) { @@ -481,12 +502,19 @@ void OptionsDialog::storeOptions(void) } } - fullScreenSelectedMonitors.setParam(monitorArrangement->value()); + fullScreenSelectedMonitors.setMonitors(monitorArrangement->value()); /* Misc. */ shared.setParam(sharedCheckbox->value()); reconnectOnError.setParam(reconnectCheckbox->value()); - dotWhenNoCursor.setParam(dotCursorCheckbox->value()); + alwaysCursor.setParam(alwaysCursorCheckbox->value()); + + if (cursorTypeChoice->value() == 1) { + cursorType.setParam("System"); + } else { + // Default + cursorType.setParam("Dot"); + } std::map<OptionsCallback*, void*>::const_iterator iter; @@ -840,11 +868,23 @@ void OptionsDialog::createInputPage(int tx, int ty, int tw, int th) _("Emulate middle mouse button"))); ty += CHECK_HEIGHT + TIGHT_MARGIN; - dotCursorCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty, - CHECK_MIN_WIDTH, - CHECK_HEIGHT, - _("Show dot when no cursor"))); + alwaysCursorCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty, + CHECK_MIN_WIDTH, + CHECK_HEIGHT, + _("Show local cursor when not provided by server"))); + alwaysCursorCheckbox->callback(handleAlwaysCursor, this); ty += CHECK_HEIGHT + TIGHT_MARGIN; + + /* Cursor type */ + cursorTypeChoice = new Fl_Choice(LBLLEFT(tx, ty, 150, CHOICE_HEIGHT, _("Cursor type"))); + + fltk_menu_add(cursorTypeChoice, _("Dot"), 0, nullptr, nullptr, 0); + fltk_menu_add(cursorTypeChoice, _("System"), 0, nullptr, nullptr, 0); + + fltk_adjust_choice(cursorTypeChoice); + + ty += CHOICE_HEIGHT + TIGHT_MARGIN; + } ty -= TIGHT_MARGIN; @@ -868,19 +908,11 @@ void OptionsDialog::createInputPage(int tx, int ty, int tw, int th) tx += INDENT; ty += TIGHT_MARGIN; - systemKeysCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty, - CHECK_MIN_WIDTH, - CHECK_HEIGHT, - _("Pass system keys directly to server (full screen)"))); + systemKeysCheckbox = new Fl_Check_Button( + LBLRIGHT(tx, ty, CHECK_MIN_WIDTH, CHECK_HEIGHT, + _("Always send all keyboard input in full screen"))); + systemKeysCheckbox->callback(handleSystemKeys, this); ty += CHECK_HEIGHT + TIGHT_MARGIN; - - menuKeyChoice = new Fl_Choice(LBLLEFT(tx, ty, 150, CHOICE_HEIGHT, _("Menu key"))); - - fltk_menu_add(menuKeyChoice, _("None"), 0, nullptr, nullptr, FL_MENU_DIVIDER); - for (int idx = 0; idx < getMenuKeySymbolCount(); idx++) - fltk_menu_add(menuKeyChoice, getMenuKeySymbols()[idx].name, 0, nullptr, nullptr, 0); - - ty += CHOICE_HEIGHT + TIGHT_MARGIN; } ty -= TIGHT_MARGIN; @@ -949,6 +981,76 @@ void OptionsDialog::createInputPage(int tx, int ty, int tw, int th) } +void OptionsDialog::createShortcutsPage(int tx, int ty, int tw, int th) +{ + Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Keyboard shortcuts")); + + tx += OUTER_MARGIN; + ty += OUTER_MARGIN; + + Fl_Box *intro = new Fl_Box(tx, ty, tw - OUTER_MARGIN * 2, INPUT_HEIGHT); + intro->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE); + intro->label(_("Modifier keys for keyboard shortcuts:")); + + ty += INPUT_HEIGHT + INNER_MARGIN; + + int width; + + width = (tw - OUTER_MARGIN * 2 - INNER_MARGIN * 3) / 4; + + ctrlButton = new Fl_Toggle_Button(tx, ty, + /* + * TRANSLATORS: This refers to the + * keyboard key + * */ + width, BUTTON_HEIGHT, _("Ctrl")); + ctrlButton->selection_color(FL_SELECTION_COLOR); + ctrlButton->callback(handleModifier, this); + shiftButton = new Fl_Toggle_Button(tx + width + INNER_MARGIN, ty, + /* + * TRANSLATORS: This refers to the + * keyboard key + * */ + width, BUTTON_HEIGHT, _("Shift")); + shiftButton->selection_color(FL_SELECTION_COLOR); + shiftButton->callback(handleModifier, this); + altButton = new Fl_Toggle_Button(tx + width * 2 + INNER_MARGIN * 2, ty, + /* + * TRANSLATORS: This refers to the + * keyboard key + * */ + width, BUTTON_HEIGHT, _("Alt")); + altButton->selection_color(FL_SELECTION_COLOR); + altButton->callback(handleModifier, this); + superButton = new Fl_Toggle_Button(tx + width * 3 + INNER_MARGIN * 3, ty, + /* + * TRANSLATORS: This refers to the + * keyboard key + * */ + width, BUTTON_HEIGHT, _("Win")); + superButton->selection_color(FL_SELECTION_COLOR); + superButton->callback(handleModifier, this); + +#ifdef __APPLE__ + /* TRANSLATORS: This refers to the keyboard key */ + ctrlButton->label(_("⌃ Ctrl")); + /* TRANSLATORS: This refers to the keyboard key */ + shiftButton->label(_("⇧ Shift")); + /* TRANSLATORS: This refers to the keyboard key */ + altButton->label(_("⌥ Option")); + /* TRANSLATORS: This refers to the keyboard key */ + superButton->label(_("⌘ Cmd")); +#endif + + ty += BUTTON_HEIGHT + INNER_MARGIN; + + shortcutsText = new Fl_Box(tx, ty, tw - OUTER_MARGIN * 2, th - ty - OUTER_MARGIN); + shortcutsText->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP); + + group->end(); +} + + void OptionsDialog::createDisplayPage(int tx, int ty, int tw, int th) { Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Display")); @@ -1117,6 +1219,20 @@ void OptionsDialog::handleRSAAES(Fl_Widget* /*widget*/, void *data) } +void OptionsDialog::handleSystemKeys(Fl_Widget* /*widget*/, void* data) +{ +#ifdef __APPLE__ + OptionsDialog* dialog = (OptionsDialog*)data; + + // Pop up the access dialog if needed + if (dialog->systemKeysCheckbox->value()) + cocoa_is_trusted(true); +#else + (void)data; +#endif +} + + void OptionsDialog::handleClipboard(Fl_Widget* /*widget*/, void *data) { (void)data; @@ -1134,6 +1250,61 @@ void OptionsDialog::handleClipboard(Fl_Widget* /*widget*/, void *data) #endif } +void OptionsDialog::handleModifier(Fl_Widget* /*widget*/, void *data) +{ + OptionsDialog *dialog = (OptionsDialog*)data; + unsigned mask; + + mask = 0; + if (dialog->ctrlButton->value()) + mask |= ShortcutHandler::Control; + if (dialog->shiftButton->value()) + mask |= ShortcutHandler::Shift; + if (dialog->altButton->value()) + mask |= ShortcutHandler::Alt; + if (dialog->superButton->value()) + mask |= ShortcutHandler::Super; + + if (mask == 0) { + dialog->shortcutsText->copy_label( + _("All keyboard shortcuts are disabled.")); + } else { + char prefix[256]; + char prefix_noplus[256]; + + std::string label; + + strcpy(prefix, ShortcutHandler::modifierPrefix(mask)); + strcpy(prefix_noplus, ShortcutHandler::modifierPrefix(mask, true)); + + label += core::format( + _("To release keyboard control from the session, press %s."), + prefix_noplus); + label += "\n\n"; + + label += core::format( + _("To pass all keyboard input to the session, press %sG."), + prefix); + label += "\n\n"; + + label += core::format( + _("To toggle full-screen mode, press %sEnter."), prefix); + label += "\n\n"; + + label += core::format( + _("To open the session context menu, press %sM."), prefix); + label += "\n\n"; + + label += core::format( + _("To send a key combination that includes %s directly to the " + "session, press %sSpace, release the space bar without " + "releasing %s, and press the desired key."), + prefix_noplus, prefix, prefix_noplus); + + dialog->shortcutsText->copy_label(label.c_str()); + } +} + void OptionsDialog::handleFullScreenMode(Fl_Widget* /*widget*/, void *data) { OptionsDialog *dialog = (OptionsDialog*)data; @@ -1184,5 +1355,16 @@ void OptionsDialog::handleScreenConfigTimeout(void *data) assert(self); - self->monitorArrangement->value(fullScreenSelectedMonitors.getParam()); + self->monitorArrangement->value(fullScreenSelectedMonitors.getMonitors()); +} + +void OptionsDialog::handleAlwaysCursor(Fl_Widget* /*widget*/, void *data) +{ + OptionsDialog *dialog = (OptionsDialog*)data; + + if (dialog->alwaysCursorCheckbox->value()) { + dialog->cursorTypeChoice->activate(); + } else { + dialog->cursorTypeChoice->deactivate(); + } } |