From f4e04254926f306c8d1b0e67ee4e6a7362476652 Mon Sep 17 00:00:00 2001 From: Krystof Pistek Date: Fri, 14 Jun 2024 15:36:14 +0200 Subject: [PATCH] Add local cursor selection for Java version Replicates the C++ work done in the previous commit on the Java version. --- .../com/tigervnc/vncviewer/OptionsDialog.java | 52 ++++++++++-- java/com/tigervnc/vncviewer/Parameters.java | 83 +++++++++++++------ .../tigervnc/vncviewer/UserPreferences.java | 5 ++ java/com/tigervnc/vncviewer/Viewport.java | 17 +++- java/com/tigervnc/vncviewer/VncViewer.java | 11 +++ 5 files changed, 130 insertions(+), 38 deletions(-) diff --git a/java/com/tigervnc/vncviewer/OptionsDialog.java b/java/com/tigervnc/vncviewer/OptionsDialog.java index 2fbd9d9c20..60853763b5 100644 --- a/java/com/tigervnc/vncviewer/OptionsDialog.java +++ b/java/com/tigervnc/vncviewer/OptionsDialog.java @@ -152,9 +152,11 @@ protected void processFocusEvent(final FocusEvent e) { /* Misc. */ JCheckBox sharedCheckbox; - JCheckBox dotWhenNoCursorCheckbox; + JCheckBox alwaysCursorCheckbox; JCheckBox acceptBellCheckbox; + JComboBox cursorTypeChoice; + /* SSH */ JCheckBox tunnelCheckbox; JCheckBox viaCheckbox; @@ -311,6 +313,7 @@ private void loadOptions() handleAutoselect(); handleCompression(); handleJpeg(); + handleAlwaysCursor(); /* Security */ Security security = new Security(SecurityClient.secTypes); @@ -458,7 +461,9 @@ private void loadOptions() /* Misc. */ sharedCheckbox.setSelected(shared.getValue()); - dotWhenNoCursorCheckbox.setSelected(dotWhenNoCursor.getValue()); + alwaysCursorCheckbox.setSelected(alwaysCursor.getValue()); + String cursorTypeStr = cursorType.getValueStr(); + cursorTypeChoice.setSelectedItem(cursorTypeStr); acceptBellCheckbox.setSelected(acceptBell.getValue()); /* SSH */ @@ -613,8 +618,9 @@ else if (mediumcolorButton.isSelected()) /* Misc. */ shared.setParam(sharedCheckbox.isSelected()); - dotWhenNoCursor.setParam(dotWhenNoCursorCheckbox.isSelected()); + alwaysCursor.setParam(alwaysCursorCheckbox.isSelected()); acceptBell.setParam(acceptBellCheckbox.isSelected()); + cursorType.setParam((String)cursorTypeChoice.getSelectedItem()); /* SSH */ tunnel.setParam(tunnelCheckbox.isSelected()); @@ -1173,31 +1179,54 @@ private JPanel createMiscPanel() { MiscPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5)); sharedCheckbox = new JCheckBox("Shared (don't disconnect other viewers)"); - dotWhenNoCursorCheckbox = new JCheckBox("Show dot when no cursor"); + alwaysCursorCheckbox = new JCheckBox("Show local cursor when not provided by server"); + alwaysCursorCheckbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleAlwaysCursor(); + } + }); + JLabel cursorTypeLabel = new JLabel("Cursor type:"); + String[] cursorTypes = {"Dot", "System"}; + cursorTypeChoice = new MyJComboBox(cursorTypes); + cursorTypeChoice.setEditable(true); acceptBellCheckbox = new JCheckBox("Beep when requested by the server"); MiscPanel.add(sharedCheckbox, new GridBagConstraints(0, 0, - 1, 1, + REMAINDER, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - MiscPanel.add(dotWhenNoCursorCheckbox, + MiscPanel.add(alwaysCursorCheckbox, new GridBagConstraints(0, 1, - 1, 1, + REMAINDER, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - MiscPanel.add(acceptBellCheckbox, + MiscPanel.add(cursorTypeLabel, new GridBagConstraints(0, 2, 1, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - MiscPanel.add(Box.createRigidArea(new Dimension(5, 0)), + MiscPanel.add(cursorTypeChoice, + new GridBagConstraints(1, 2, + 1, 1, + LIGHT, LIGHT, + LINE_START, NONE, + new Insets(0, 5, 4, 0), + NONE, NONE)); + MiscPanel.add(acceptBellCheckbox, new GridBagConstraints(0, 3, + REMAINDER, 1, + LIGHT, LIGHT, + LINE_START, NONE, + new Insets(0, 0, 4, 0), + NONE, NONE)); + MiscPanel.add(Box.createRigidArea(new Dimension(5, 0)), + new GridBagConstraints(0, 4, REMAINDER, REMAINDER, HEAVY, HEAVY, LINE_START, BOTH, @@ -1633,5 +1662,10 @@ private void handleRfbState() } } + private void handleAlwaysCursor() + { + cursorTypeChoice.setEnabled(alwaysCursorCheckbox.isSelected()); + } + static LogWriter vlog = new LogWriter("OptionsDialog"); } diff --git a/java/com/tigervnc/vncviewer/Parameters.java b/java/com/tigervnc/vncviewer/Parameters.java index 8c8465cba1..e763837010 100644 --- a/java/com/tigervnc/vncviewer/Parameters.java +++ b/java/com/tigervnc/vncviewer/Parameters.java @@ -40,9 +40,19 @@ public class Parameters { public static BoolParameter dotWhenNoCursor = new BoolParameter("DotWhenNoCursor", - "Show the dot cursor when the server sends an invisible cursor", + "[DEPRECATED] Show the dot cursor when the server sends an invisible cursor", false); + public static BoolParameter alwaysCursor + = new BoolParameter("AlwaysCursor", + "Show the local cursor when the server sends an invisible cursor", + false); + + public static StringParameter cursorType + = new StringParameter("CursorType", + "Specify which cursor type the local cursor should be. Should be either Dot or System", + "Dot"); + public static BoolParameter sendLocalUsername = new BoolParameter("SendLocalUsername", "Send the local username for SecurityTypes "+ @@ -282,7 +292,8 @@ public class Parameters { CSecurityTLS.X509CA, CSecurityTLS.X509CRL, SecurityClient.secTypes, - dotWhenNoCursor, + alwaysCursor, + cursorType, autoSelect, fullColor, lowColorLevel, @@ -315,6 +326,10 @@ public class Parameters { sshKeyFile, }; + static VoidParameter[] readOnlyParameterArray = { + dotWhenNoCursor + }; + static LogWriter vlog = new LogWriter("Parameters"); @@ -447,29 +462,35 @@ public static String loadViewerParameters(String filename) throws Exception { invalidParameterName = false; } else { for (int i = 0; i < parameterArray.length; i++) { - if (parameterArray[i] instanceof StringParameter) { - if (line.substring(0,idx).trim().equalsIgnoreCase(parameterArray[i].getName())) { + VoidParameter parameter; + if (i < parameterArray.length) { + parameter = parameterArray[i]; + } else { + parameter = readOnlyParameterArray[i - parameterArray.length]; + } + if (parameter instanceof StringParameter) { + if (line.substring(0,idx).trim().equalsIgnoreCase(parameter.getName())) { if (value.length() > 256) { vlog.error(String.format("Failed to read line %d in file %s: %s", lineNr, filepath, "Invalid format or too large value")); continue; } - ((StringParameter)parameterArray[i]).setParam(value); + ((StringParameter)parameter).setParam(value); invalidParameterName = false; } - } else if (parameterArray[i] instanceof IntParameter) { - if (line.substring(0,idx).trim().equalsIgnoreCase(parameterArray[i].getName())) { - ((IntParameter)parameterArray[i]).setParam(value); + } else if (parameter instanceof IntParameter) { + if (line.substring(0,idx).trim().equalsIgnoreCase(parameter.getName())) { + ((IntParameter)parameter).setParam(value); invalidParameterName = false; } - } else if (parameterArray[i] instanceof BoolParameter) { - if (line.substring(0,idx).trim().equalsIgnoreCase(parameterArray[i].getName())) { - ((BoolParameter)parameterArray[i]).setParam(value); + } else if (parameter instanceof BoolParameter) { + if (line.substring(0,idx).trim().equalsIgnoreCase(parameter.getName())) { + ((BoolParameter)parameter).setParam(value); invalidParameterName = false; } } else { vlog.error(String.format("Unknown parameter type for parameter %s", - parameterArray[i].getName())); + parameter.getName())); } } @@ -517,6 +538,10 @@ public static void saveToReg(String servername) { } } + for (int i = 0; i < readOnlyParameterArray.length; i++) { + UserPreferences.delete(hKey, readOnlyParameterArray[i].getName()); + } + UserPreferences.save(hKey); } @@ -528,28 +553,34 @@ public static String loadFromReg() { if (servername == null) servername = ""; - for (int i = 0; i < parameterArray.length; i++) { - if (parameterArray[i] instanceof StringParameter) { - if (UserPreferences.get(hKey, parameterArray[i].getName()) != null) { + for (int i = 0; i < parameterArray.length + readOnlyParameterArray.length; i++) { + VoidParameter parameter; + if (i < parameterArray.length) { + parameter = parameterArray[i]; + } else { + parameter = readOnlyParameterArray[i - parameterArray.length]; + } + if (parameter instanceof StringParameter) { + if (UserPreferences.get(hKey, parameter.getName()) != null) { String stringValue = - UserPreferences.get(hKey, parameterArray[i].getName()); - ((StringParameter)parameterArray[i]).setParam(stringValue); + UserPreferences.get(hKey, parameter.getName()); + ((StringParameter)parameter).setParam(stringValue); } - } else if (parameterArray[i] instanceof IntParameter) { - if (UserPreferences.get(hKey, parameterArray[i].getName()) != null) { + } else if (parameter instanceof IntParameter) { + if (UserPreferences.get(hKey, parameter.getName()) != null) { int intValue = - UserPreferences.getInt(hKey, parameterArray[i].getName()); - ((IntParameter)parameterArray[i]).setParam(intValue); + UserPreferences.getInt(hKey, parameter.getName()); + ((IntParameter)parameter).setParam(intValue); } - } else if (parameterArray[i] instanceof BoolParameter) { - if (UserPreferences.get(hKey, parameterArray[i].getName()) != null) { + } else if (parameter instanceof BoolParameter) { + if (UserPreferences.get(hKey, parameter.getName()) != null) { boolean booleanValue = - UserPreferences.getBool(hKey, parameterArray[i].getName()); - ((BoolParameter)parameterArray[i]).setParam(booleanValue); + UserPreferences.getBool(hKey, parameter.getName()); + ((BoolParameter)parameter).setParam(booleanValue); } } else { vlog.error(String.format("Unknown parameter type for parameter %s", - parameterArray[i].getName())); + parameter.getName())); } } diff --git a/java/com/tigervnc/vncviewer/UserPreferences.java b/java/com/tigervnc/vncviewer/UserPreferences.java index 544774dbd5..44c62343bc 100644 --- a/java/com/tigervnc/vncviewer/UserPreferences.java +++ b/java/com/tigervnc/vncviewer/UserPreferences.java @@ -144,6 +144,11 @@ public static void load(String nName) { } } + public static void delete(String nName, String key) { + Preferences node = root.node(nName); + node.remove(key); + } + static LogWriter vlog = new LogWriter("UserPreferences"); } diff --git a/java/com/tigervnc/vncviewer/Viewport.java b/java/com/tigervnc/vncviewer/Viewport.java index e12ef2a685..c16c5922ab 100644 --- a/java/com/tigervnc/vncviewer/Viewport.java +++ b/java/com/tigervnc/vncviewer/Viewport.java @@ -167,8 +167,15 @@ public void setCursor(int width, int height, Point hotspot, for (i = 0; i < width*height; i++) if (data[i*4 + 3] != 0) break; - if ((i == width*height) && dotWhenNoCursor.getValue()) { - vlog.debug("cursor is empty - using dot"); + useSystemCursor = false; + if ((i == width*height) && alwaysCursor.getValue()) { + String cursorTypeStr = cursorType.getValueStr(); + if (cursorTypeStr.matches("^Dot$")) { + vlog.debug("cursor is empty - using dot"); + } else if (cursorTypeStr.matches("^System$")) { + useSystemCursor = true; + } + // Do this anyway to prevent cursor being null cursor = new BufferedImage(5, 5, BufferedImage.TYPE_INT_ARGB_PRE); cursor.setRGB(0, 0, 5, 5, dotcursor_xpm, 0, 5); cursorHotspot.x = cursorHotspot.y = 3; @@ -216,7 +223,10 @@ private void setCursor(Image img, int x, int y) hotspot = new java.awt.Point(x, y); softCursor = tk.createCustomCursor(img, hotspot, name); - setCursor(softCursor); + if (useSystemCursor) + setCursor(java.awt.Cursor.getDefaultCursor()); + else + setCursor(softCursor); } public void resize(int x, int y, int w, int h) { @@ -830,6 +840,7 @@ public DownMap(int capacity) { float scaleRatioX, scaleRatioY; static BufferedImage cursor; + static boolean useSystemCursor = false; Point cursorHotspot = new Point(); } diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java index 067f4eff6f..e4f12fd9d9 100644 --- a/java/com/tigervnc/vncviewer/VncViewer.java +++ b/java/com/tigervnc/vncviewer/VncViewer.java @@ -212,6 +212,8 @@ public VncViewer(String[] argv) { // Check if the server name in reality is a configuration file potentiallyLoadConfigurationFile(vncServerName); + + migrateDeprecatedOptions(); } public static void usage() { @@ -449,6 +451,15 @@ public void run() { } } + static void migrateDeprecatedOptions() { + if (dotWhenNoCursor.getValue()) { + vlog.info("DotWhenNoCursor is deprecated, set AlwaysCursor to 1 and CursorType to 'Dot' instead"); + + alwaysCursor.setParam(true); + cursorType.setParam("Dot"); + } + } + public static CConn cc; public static StringParameter config = new StringParameter("Config",