From 8a38990a8896eafc9a4f1293bd1302af9f67d8be Mon Sep 17 00:00:00 2001 From: Krystof Pistek Date: Fri, 14 Jun 2024 14:42:58 +0200 Subject: [PATCH] Add local cursor selection for C++ version This adds the option to select which cursor should be used in the event the server sends an invisible cursor. It also renames the DotWhenNoCursor config option to AlwaysCursor. --- vncviewer/OptionsDialog.cxx | 45 ++++++++++++++++++++++++++++++++----- vncviewer/OptionsDialog.h | 4 +++- vncviewer/Viewport.cxx | 26 ++++++++++++++++----- vncviewer/Viewport.h | 4 ++++ vncviewer/parameters.cxx | 15 ++++++++++--- vncviewer/parameters.h | 4 +++- vncviewer/vncviewer.cxx | 6 +++++ vncviewer/vncviewer.man | 15 +++++++++++-- 8 files changed, 101 insertions(+), 18 deletions(-) diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx index e04065eced..541d450814 100644 --- a/vncviewer/OptionsDialog.cxx +++ b/vncviewer/OptionsDialog.cxx @@ -352,7 +352,13 @@ void OptionsDialog::loadOptions(void) /* Misc. */ sharedCheckbox->value(shared); reconnectCheckbox->value(reconnectOnError); - dotCursorCheckbox->value(dotWhenNoCursor); + alwaysCursorCheckbox->value(alwaysCursor); + if (!strcasecmp(cursorType, "dot")) { + cursorTypeChoice->value(0); + } else { + cursorTypeChoice->value(1); + } + handleAlwaysCursor(alwaysCursorCheckbox, this); } @@ -486,7 +492,13 @@ void OptionsDialog::storeOptions(void) /* Misc. */ shared.setParam(sharedCheckbox->value()); reconnectOnError.setParam(reconnectCheckbox->value()); - dotWhenNoCursor.setParam(dotCursorCheckbox->value()); + alwaysCursor.setParam(alwaysCursorCheckbox->value()); + + if (cursorTypeChoice->value() == 0) { + cursorType.setParam("Dot"); + } else { + cursorType.setParam("System"); + } std::map::const_iterator iter; @@ -835,11 +847,21 @@ 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); + + ty += CHOICE_HEIGHT + TIGHT_MARGIN; + } ty -= TIGHT_MARGIN; @@ -1181,3 +1203,14 @@ void OptionsDialog::handleScreenConfigTimeout(void *data) self->monitorArrangement->value(fullScreenSelectedMonitors.getParam()); } + +void OptionsDialog::handleAlwaysCursor(Fl_Widget* /*widget*/, void *data) +{ + OptionsDialog *dialog = (OptionsDialog*)data; + + if (dialog->alwaysCursorCheckbox->value()) { + dialog->cursorTypeChoice->activate(); + } else { + dialog->cursorTypeChoice->deactivate(); + } +} diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h index f6ca89b1ba..86a1423aa6 100644 --- a/vncviewer/OptionsDialog.h +++ b/vncviewer/OptionsDialog.h @@ -60,6 +60,7 @@ class OptionsDialog : public Fl_Window { static void handleAutoselect(Fl_Widget *widget, void *data); static void handleCompression(Fl_Widget *widget, void *data); static void handleJpeg(Fl_Widget *widget, void *data); + static void handleAlwaysCursor(Fl_Widget *widget, void *data); static void handleX509(Fl_Widget *widget, void *data); static void handleRSAAES(Fl_Widget *widget, void *data); @@ -115,7 +116,8 @@ class OptionsDialog : public Fl_Window { Fl_Check_Button *viewOnlyCheckbox; Fl_Group *mouseGroup; Fl_Check_Button *emulateMBCheckbox; - Fl_Check_Button *dotCursorCheckbox; + Fl_Check_Button *alwaysCursorCheckbox; + Fl_Choice *cursorTypeChoice; Fl_Group *keyboardGroup; Fl_Check_Button *systemKeysCheckbox; Fl_Choice *menuKeyChoice; diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index 032fd32217..4a026d2d39 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -122,7 +122,8 @@ Viewport::Viewport(int w, int h, const rfb::PixelFormat& /*serverPF*/, CConn* cc altGrArmed(false), #endif firstLEDState(true), pendingClientClipboard(false), - menuCtrlKey(false), menuAltKey(false), cursor(nullptr) + menuCtrlKey(false), menuAltKey(false), cursor(nullptr), + cursorIsBlank(false) { #if !defined(WIN32) && !defined(__APPLE__) XkbDescPtr xkb; @@ -259,7 +260,12 @@ void Viewport::setCursor(int width, int height, const Point& hotspot, for (i = 0; i < width*height; i++) if (data[i*4 + 3] != 0) break; - if ((i == width*height) && dotWhenNoCursor) { + cursorIsBlank = i == width*height; + + if (cursorIsBlank && alwaysCursor) { + // This is the default in case the local cursor should be displayed yet cursorType is invalid. + // Since the cursor variable isn't used if the cursorType is system, we can do this without checking the current + // type which helps handle changing the type while the viewer is running. vlog.debug("cursor is empty - using dot"); Fl_Pixmap pxm(dotcursor_xpm); @@ -279,8 +285,18 @@ void Viewport::setCursor(int width, int height, const Point& hotspot, } } - if (Fl::belowmouse() == this) + if (Fl::belowmouse() == this) { + showCursor(); + } +} + +void Viewport::showCursor() +{ + if (cursorIsBlank && alwaysCursor && !strcasecmp("system", cursorType)) { + window()->cursor(FL_CURSOR_DEFAULT); + } else { window()->cursor(cursor, cursorHotspot.x, cursorHotspot.y); + } } void Viewport::handleClipboardRequest() @@ -583,7 +599,7 @@ int Viewport::handle(int event) return 1; case FL_ENTER: - window()->cursor(cursor, cursorHotspot.x, cursorHotspot.y); + showCursor(); // Yes, we would like some pointer events please! return 1; @@ -1299,7 +1315,7 @@ void Viewport::popupContextMenu() // Back to our proper mouse pointer. if (Fl::belowmouse()) - window()->cursor(cursor, cursorHotspot.x, cursorHotspot.y); + showCursor(); if (m == nullptr) return; diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h index d936c70898..934fa73273 100644 --- a/vncviewer/Viewport.h +++ b/vncviewer/Viewport.h @@ -77,6 +77,9 @@ class Viewport : public Fl_Widget, public EmulateMB { unsigned int getModifierMask(unsigned int keysym); + // Show the currently set (or system) cursor + void showCursor(); + static void handleClipboardChange(int source, void *data); void flushPendingClipboard(); @@ -136,6 +139,7 @@ class Viewport : public Fl_Widget, public EmulateMB { Fl_RGB_Image *cursor; rfb::Point cursorHotspot; + bool cursorIsBlank; }; #endif diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx index a03623db74..42f337f171 100644 --- a/vncviewer/parameters.cxx +++ b/vncviewer/parameters.cxx @@ -61,8 +61,15 @@ BoolParameter emulateMiddleButton("EmulateMiddleButton", "left and right mouse buttons simultaneously", false); BoolParameter dotWhenNoCursor("DotWhenNoCursor", - "Show the dot cursor when the server sends an " + "[DEPRECATED] Show the dot cursor when the server sends an " "invisible cursor", false); +BoolParameter alwaysCursor("AlwaysCursor", + "Show the local cursor when the server sends an " + "invisible cursor", false); +StringParameter cursorType("CursorType", + "Specify which cursor type the local cursor should be. " + "Should be either Dot or System", + "Dot"); BoolParameter alertOnFatalError("AlertOnFatalError", "Give a dialog on connection problems rather " @@ -198,7 +205,8 @@ static VoidParameter* parameterArray[] = { /* Input */ &viewOnly, &emulateMiddleButton, - &dotWhenNoCursor, + &alwaysCursor, + &cursorType, &acceptClipboard, &sendClipboard, #if !defined(WIN32) && !defined(__APPLE__) @@ -210,7 +218,8 @@ static VoidParameter* parameterArray[] = { }; static VoidParameter* readOnlyParameterArray[] = { - &fullScreenAllMonitors + &fullScreenAllMonitors, + &dotWhenNoCursor }; // Encoding Table diff --git a/vncviewer/parameters.h b/vncviewer/parameters.h index df7bc42059..b161dd9d2c 100644 --- a/vncviewer/parameters.h +++ b/vncviewer/parameters.h @@ -33,7 +33,9 @@ extern rfb::IntParameter pointerEventInterval; extern rfb::BoolParameter emulateMiddleButton; -extern rfb::BoolParameter dotWhenNoCursor; +extern rfb::BoolParameter dotWhenNoCursor; // deprecated +extern rfb::BoolParameter alwaysCursor; +extern rfb::StringParameter cursorType; extern rfb::StringParameter passwordFile; diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx index 27946f3fc5..cc3a247bde 100644 --- a/vncviewer/vncviewer.cxx +++ b/vncviewer/vncviewer.cxx @@ -535,6 +535,12 @@ migrateDeprecatedOptions() fullScreenMode.setParam("all"); } + if (dotWhenNoCursor) { + vlog.info(_("DotWhenNoCursor is deprecated, set AlwaysCursor to 1 and CursorType to 'Dot' instead")); + + alwaysCursor.setParam(true); + cursorType.setParam("Dot"); + } } #ifndef WIN32 diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man index 1a2821266e..d724ebf9a7 100644 --- a/vncviewer/vncviewer.man +++ b/vncviewer/vncviewer.man @@ -285,8 +285,19 @@ Use specified lossless compression level. 0 = Low, 9 = High. Default is 2. Use custom compression level. Default if \fBCompressLevel\fP is specified. . .TP -.B \-DotWhenNoCursor -Show the dot cursor when the server sends an invisible cursor. Default is off. +.B \-DotWhenNoCursor (DEPRECATED) +Show the dot cursor when the server sends an invisible cursor. Replaced by +\fB-AlwaysCursor\fP and \fB-CursorType=Dot\fP +. +.TP +.B \-AlwaysCursor +Show a local cursor when the server sends an invisible cursor. Default is off. +. +.TP +.B \-CursorType \fItype\fP +Specify which cursor type to use when a local cursor is shown. It should be +either "Dot", or "System". Ignored if AlwaysCursor is off. +The default is "Dot". . .TP .B \-PointerEventInterval \fItime\fP