浏览代码

Refactoring and implementation changes to keyboard and mouse handling

See issue #505
tags/v1.8.90
Brian P. Hinz 6 年前
父节点
当前提交
e5b4003bfb

java/com/tigervnc/rfb/UnicodeToKeysym.java → java/com/tigervnc/rfb/Keysym2ucs.java 查看文件

@@ -1,6 +1,4 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2013 Brian P. Hinz
* Copyright (C) 2001 Markus G. Kuhn, University of Cambridge
/* Copyright (C) 2017 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,14 +14,38 @@
* along with this software; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* This module converts keysym values into the corresponding ISO 10646
* (UCS, Unicode) values.
*
* The array keysymtab[] contains pairs of X11 keysym values for graphical
* characters and the corresponding Unicode value. The function
* keysym2ucs() maps a keysym onto a Unicode value using a binary search,
* therefore keysymtab[] must remain SORTED by keysym value.
*
* The keysym -> UTF-8 conversion will hopefully one day be provided
* by Xlib via XmbLookupString() and should ideally not have to be
* done in X applications. But we are not there yet.
*
* We allow to represent any UCS character in the range U-00000000 to
* U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff.
* This admittedly does not cover the entire 31-bit space of UCS, but
* it does cover all of the characters up to U-10FFFF, which can be
* represented by UTF-16, and more, and it is very unlikely that higher
* UCS codes will ever be assigned by ISO. So to get Unicode character
* U+ABCD you can directly use keysym 0x0100abcd.
*
* NOTE: The comments in the table below contain the actual character
* encoded in UTF-8, so for viewing and editing best use an editor in
* UTF-8 mode.
*
* Derived from keysym2ucs.c, originally authored by Markus G, Kuhn
*
*/
//
// Derived from keysym2ucs.c, originally authored by Markus G, Kuhn
//

package com.tigervnc.rfb;

public class UnicodeToKeysym {
public class Keysym2ucs {

private static class codepair {
public codepair(int keysym_, int ucs_) {
@@ -34,6 +56,15 @@ public class UnicodeToKeysym {
int ucs;
}

private static class combiningpair {
public combiningpair(int spacing_, int combining_) {
spacing = spacing_;
combining = combining_;
}
int spacing;
int combining;
}

public static codepair[] keysymtab = {
new codepair(0x01a1, 0x0104), /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */
new codepair(0x01a2, 0x02d8), /* breve ˘ BREVE */
@@ -826,6 +857,63 @@ public class UnicodeToKeysym {
new codepair(0xfe60, 0x0323), /* COMBINING DOT BELOW */
};

public static combiningpair[] combinetab = {
new combiningpair(0x0060, 0x0300), /* GRAVE ACCENT ` COMBINING GRAVE ACCENT */
new combiningpair(0x00b4, 0x0301), /* ACUTE ACCENT ´ COMBINING ACUTE ACCENT */
new combiningpair(0x0027, 0x0301), /* APOSTROPHE ' COMBINING ACUTE ACCENT */
new combiningpair(0x0384, 0x0301), /* GREEK TONOS ΄ COMBINING ACUTE ACCENT */
new combiningpair(0x005e, 0x0302), /* CIRCUMFLEX ACCENT ^ COMBINING CIRCUMFLEX ACCENT */
new combiningpair(0x007e, 0x0303), /* TILDE ~ COMBINING TILDE */
new combiningpair(0x00af, 0x0304), /* MACRON ¯ COMBINING MACRON */
new combiningpair(0x02d8, 0x0306), /* BREVE ˘ COMBINING BREVE */
new combiningpair(0x02d9, 0x0307), /* DOT ABOVE ˙ COMBINING DOT ABOVE */
new combiningpair(0x00a8, 0x0308), /* DIAERESIS ¨ COMBINING DIAERESIS */
new combiningpair(0x0022, 0x0308), /* QUOTATION MARK " COMBINING DIAERESIS */
new combiningpair(0x02da, 0x030a), /* RING ABOVE ˚ COMBINING RING ABOVE */
new combiningpair(0x00b0, 0x030a), /* DEGREE SIGN ° COMBINING RING ABOVE */
new combiningpair(0x02dd, 0x030b), /* DOUBLE ACUTE ACCENT ˝ COMBINING DOUBLE ACUTE ACCENT */
new combiningpair(0x02c7, 0x030c), /* CARON ˇ COMBINING CARON */
new combiningpair(0x00b8, 0x0327), /* CEDILLA ¸ COMBINING CEDILLA */
new combiningpair(0x02db, 0x0328), /* OGONEK ¸ COMBINING OGONEK */
new combiningpair(0x037a, 0x0345), /* GREEK YPOGEGRAMMENI ͺ COMBINING GREEK YPOGEGRAMMENI */
new combiningpair(0x309b, 0x3099), /* KATAKANA-HIRAGANA VOICED SOUND MARK ゛COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK */
new combiningpair(0x309c, 0x309a), /* KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK ゜COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
new combiningpair(0x002e, 0x0323), /* FULL STOP . COMBINING DOT BELOW */
new combiningpair(0x0385, 0x0344), /* GREEK DIALYTIKA TONOS ΅ COMBINING GREEK DIALYTIKA TONOS */
};

public static int keysym2ucs(int keysym)
{
int min = 0;
int max = keysymtab.length - 1;
int mid;

/* first check for Latin-1 characters (1:1 mapping) */
if ((keysym >= 0x0020 && keysym <= 0x007e) ||
(keysym >= 0x00a0 && keysym <= 0x00ff))
return keysym;

/* also check for directly encoded 24-bit UCS characters */
if ((keysym & 0xff000000) == 0x01000000)
return keysym & 0x00ffffff;

/* binary search in table */
while (max >= min) {
mid = (min + max) / 2;
if (keysymtab[mid].keysym < keysym)
min = mid + 1;
else if (keysymtab[mid].keysym > keysym)
max = mid - 1;
else {
/* found it */
return keysymtab[mid].ucs;
}
}

/* no matching Unicode value found */
return -1;
}

public static int ucs2keysym(int ucs) {
int cur = 0;
int max = keysymtab.length - 1;
@@ -847,6 +935,24 @@ public class UnicodeToKeysym {
return ucs | 0x01000000;

/* no matching Unicode value found */
return 0xffffff;
return NoSymbol;
}

public static int ucs2combining(int spacing)
{
int cur = 0;
int max = combinetab.length - 1;

/* linear search in table */
while (cur <= max) {
if (combinetab[cur].spacing == spacing)
return combinetab[cur].combining;
cur++;
}

/* no matching Unicode value found */
return -1;
}

private static final int NoSymbol = 0;
}

+ 1526
- 0
java/com/tigervnc/rfb/Keysymdef.java
文件差异内容过多而无法显示
查看文件


+ 0
- 391
java/com/tigervnc/rfb/Keysyms.java 查看文件

@@ -1,391 +0,0 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2012-2013 D. R. Commander. All Rights Reserved.
* Copyright (C) 2013 Brian P. Hinz
* Copyright (C) 2013 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/

//
// Keysyms - defines X keysyms for non-character keys. All keysyms
// corresponding to characters should be generated by calling
// UnicodeToKeysym.ucs2keysym().
//

package com.tigervnc.rfb;

import java.awt.event.KeyEvent;
import com.tigervnc.vncviewer.VncViewer;

public class Keysyms {

public static final int VoidSymbol = 0xffffff;
/*
* TTY Functions, cleverly chosen to map to ascii, for convenience of
* programming, but could have been arbitrary (at the cost of lookup
* tables in client code.
*/
public static final int BackSpace = 0xFF08;
public static final int Tab = 0xFF09;
public static final int Linefeed = 0xFF0A;
public static final int Clear = 0xFF0B;
public static final int Return = 0xFF0D;
public static final int Pause = 0xFF13;
public static final int Scroll_Lock = 0xFF14;
public static final int Sys_Req = 0xFF15;
public static final int Escape = 0xFF1B;
public static final int Delete = 0xFFFF;

/* International & multi-key character composition */
public static final int Multi_key = 0xFF20; /* Multi-key character compose */
public static final int Codeinput = 0xFF37;
public static final int SingleCandidate = 0xFF3C;
public static final int MultipleCandidate = 0xFF3D;
public static final int PreviousCandidate = 0xFF3E;

/* Japanese keyboard support */
public static final int Kanji = 0xFF21; /* Kanji, Kanji convert */
public static final int Muhenkan = 0xFF22; /* Cancel Conversion */
public static final int Henkan_Mode = 0xFF23; /* Start/Stop Conversion */
public static final int Henkan = 0xFF23; /* Alias for Henkan_Mode */
public static final int Romaji = 0xFF24; /* to Romaji */
public static final int Hiragana = 0xFF25; /* to Hiragana */
public static final int Katakana = 0xFF26; /* to Katakana */
public static final int Hiragana_Katakana = 0xFF27; /* Hiragana/Katakana toggle */
public static final int Zenkaku = 0xFF28; /* to Zenkaku */
public static final int Hankaku = 0xFF29; /* to Hankaku */
public static final int Zenkaku_Hankaku = 0xFF2A; /* Zenkaku/Hankaku toggle */
public static final int Touroku = 0xFF2B; /* Add to Dictionary */
public static final int Massyo = 0xFF2C; /* Delete from Dictionary */
public static final int Kana_Lock = 0xFF2D; /* Kana Lock */
public static final int Kana_Shift = 0xFF2E; /* Kana Shift */
public static final int Eisu_Shift = 0xFF2F; /* Alphanumeric Shift */
public static final int Eisu_toggle = 0xFF30; /* Alphanumeric toggle */
public static final int Kanji_Bangou = 0xFF37; /* Codeinput */
public static final int Zen_Koho = 0xFF3D; /* Multiple/All Candidate(s) */
public static final int Mae_Koho = 0xFF3E; /* Previous Candidate */

/* Cursor control & motion */
public static final int Home = 0xFF50;
public static final int Left = 0xFF51;
public static final int Up = 0xFF52;
public static final int Right = 0xFF53;
public static final int Down = 0xFF54;
public static final int Prior = 0xFF55;
public static final int Page_Up = 0xFF55;
public static final int Next = 0xFF56;
public static final int Page_Down = 0xFF56;
public static final int End = 0xFF57;
public static final int Begin = 0xFF58;

/* Misc Functions */
public static final int Select = 0xFF60;
public static final int Print = 0xFF61;
public static final int Execute = 0xFF62;
public static final int Insert = 0xFF63;
public static final int Undo = 0xFF65;
public static final int Redo = 0xFF66;
public static final int Menu = 0xFF67;
public static final int Find = 0xFF68;
public static final int Cancel = 0xFF69;
public static final int Help = 0xFF6A;
public static final int Break = 0xFF6B;
public static final int Mode_switch = 0xFF7E;
public static final int script_switch = 0xFF7E;
public static final int Num_Lock = 0xFF7F;

/* Keypad Functions, keypad numbers cleverly chosen to map to ascii */
public static final int KP_Enter = 0xFF8D;
public static final int KP_Home = 0xFF95;
public static final int KP_Left = 0xFF96;
public static final int KP_Up = 0xFF97;
public static final int KP_Right = 0xFF98;
public static final int KP_Down = 0xFF99;
public static final int KP_Page_Up = 0xFF9A;
public static final int KP_Page_Down = 0xFF9B;
public static final int KP_End = 0xFF9C;
public static final int KP_Begin = 0xFF9D;
public static final int KP_Insert = 0xFF9E;
public static final int KP_Delete = 0xFF9F;
public static final int KP_Equal = 0xFFBD;
public static final int KP_0 = 0xFFB0;
public static final int KP_1 = 0xFFB1;
public static final int KP_2 = 0xFFB2;
public static final int KP_3 = 0xFFB3;
public static final int KP_4 = 0xFFB4;
public static final int KP_5 = 0xFFB5;
public static final int KP_6 = 0xFFB6;
public static final int KP_7 = 0xFFB7;
public static final int KP_8 = 0xFFB8;
public static final int KP_9 = 0xFFB9;
public static final int KP_Decimal = 0xFFAE;
public static final int KP_Add = 0xFFAB;
public static final int KP_Subtract = 0xFFAD;
public static final int KP_Multiply = 0xFFAA;
public static final int KP_Divide = 0xFFAF;

/*
* Auxilliary Functions; note the duplicate definitions for left and right
* function keys; Sun keyboards and a few other manufactures have such
* function key groups on the left and/or right sides of the keyboard.
* We've not found a keyboard with more than 35 function keys total.
*/
public static final int F1 = 0xFFBE;
public static final int F2 = 0xFFBF;
public static final int F3 = 0xFFC0;
public static final int F4 = 0xFFC1;
public static final int F5 = 0xFFC2;
public static final int F6 = 0xFFC3;
public static final int F7 = 0xFFC4;
public static final int F8 = 0xFFC5;
public static final int F9 = 0xFFC6;
public static final int F10 = 0xFFC7;
public static final int F11 = 0xFFC8;
public static final int F12 = 0xFFC9;
public static final int F13 = 0xFFCA;
public static final int F14 = 0xFFCB;
public static final int F15 = 0xFFCC;
public static final int F16 = 0xFFCD;
public static final int F17 = 0xFFCE;
public static final int F18 = 0xFFCF;
public static final int F19 = 0xFFD0;
public static final int F20 = 0xFFD1;
public static final int F21 = 0xFFD2;
public static final int F22 = 0xFFD3;
public static final int F23 = 0xFFD4;
public static final int F24 = 0xFFD5;

/* Modifiers */
public static final int Shift_L = 0xFFE1;
public static final int Shift_R = 0xFFE2;
public static final int Control_L = 0xFFE3;
public static final int Control_R = 0xFFE4;
public static final int Caps_Lock = 0xFFE5;
public static final int Shift_Lock = 0xFFE6;

public static final int Meta_L = 0xFFE7;
public static final int Meta_R = 0xFFE8;
public static final int Alt_L = 0xFFE9;
public static final int Alt_R = 0xFFEA;
public static final int Super_L = 0xFFEB;
public static final int Super_R = 0xFFEC;
public static final int Hyper_L = 0xFFED;
public static final int Hyper_R = 0xFFEE;

/*
* ISO 9995 Function and Modifier Keys
* Byte 3 = 0xFE
*/
public static final int ISO_Level3_Shift = 0xFE03;

/*
* Dead Modifier Keys
*/
public static final int Dead_Grave = 0xfe50; /* COMBINING GRAVE ACCENT */
public static final int Dead_Acute = 0xfe51; /* COMBINING ACUTE ACCENT */
public static final int Dead_Circumflex = 0xfe52; /* COMBINING CIRCUMFLEX ACCENT */
public static final int Dead_Tilde = 0xfe53; /* COMBINING TILDE */
public static final int Dead_Macron = 0xfe54; /* COMBINING MACRON */
public static final int Dead_Breve = 0xfe55; /* COMBINING BREVE */
public static final int Dead_AboveDot = 0xfe56; /* COMBINING DOT ABOVE */
public static final int Dead_Diaeresis = 0xfe57; /* COMBINING DIAERESIS */
public static final int Dead_AboveRing = 0xfe58; /* COMBINING RING ABOVE */
public static final int Dead_DoubleAcute = 0xfe59; /* COMBINING DOUBLE ACUTE ACCENT */
public static final int Dead_Caron = 0xfe5a; /* COMBINING CARON */
public static final int Dead_Cedilla = 0xfe5b; /* COMBINING CEDILLA */
public static final int Dead_Ogonek = 0xfe5c; /* COMBINING OGONEK */
public static final int Dead_Iota = 0xfe5d; /* MODIFIER LETTER SMALL IOTA */
public static final int Dead_Voiced_Sound = 0xfe5e; /* COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK */
public static final int Dead_SemiVoiced_Sound = 0xfe5f; /* COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
public static final int Dead_BelowDot = 0xfe60; /* COMBINING DOT BELOW */

private static class KeySymbol {
public KeySymbol(int keycode_, int[] keysym_) {
keycode = keycode_;
keysym = new int[5];
System.arraycopy(keysym_, 0, keysym, 0, 5);
}
int keycode;
int[] keysym;
}

private static KeySymbol[] keySymbols = {
/* KEYCODE LOCATION */
/* UNKNOWN STANDARD LEFT RIGHT NUMPAD */
new KeySymbol(KeyEvent.VK_BACK_SPACE, new int[]{VoidSymbol, BackSpace, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_TAB, new int[]{VoidSymbol, Tab, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_ENTER, new int[]{VoidSymbol, Return, VoidSymbol, VoidSymbol, KP_Enter}),
new KeySymbol(KeyEvent.VK_ESCAPE, new int[]{VoidSymbol, Escape, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_CONTROL, new int[]{VoidSymbol, Control_L, Control_L, Control_R, VoidSymbol}),
new KeySymbol(KeyEvent.VK_ALT_GRAPH, new int[]{VoidSymbol, ISO_Level3_Shift, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_ALT, new int[]{VoidSymbol, ISO_Level3_Shift, Alt_L, Alt_R, VoidSymbol}),
new KeySymbol(KeyEvent.VK_SHIFT, new int[]{VoidSymbol, Shift_L, Shift_L, Shift_R, VoidSymbol}),
new KeySymbol(KeyEvent.VK_META, new int[]{VoidSymbol, Meta_L, Meta_L, Meta_R, VoidSymbol}),
new KeySymbol(KeyEvent.VK_NUMPAD0, new int[]{VoidSymbol, VoidSymbol, VoidSymbol, VoidSymbol, KP_0}),
new KeySymbol(KeyEvent.VK_NUMPAD1, new int[]{VoidSymbol, VoidSymbol, VoidSymbol, VoidSymbol, KP_1}),
new KeySymbol(KeyEvent.VK_NUMPAD2, new int[]{VoidSymbol, VoidSymbol, VoidSymbol, VoidSymbol, KP_2}),
new KeySymbol(KeyEvent.VK_NUMPAD3, new int[]{VoidSymbol, VoidSymbol, VoidSymbol, VoidSymbol, KP_3}),
new KeySymbol(KeyEvent.VK_NUMPAD4, new int[]{VoidSymbol, VoidSymbol, VoidSymbol, VoidSymbol, KP_4}),
new KeySymbol(KeyEvent.VK_NUMPAD5, new int[]{VoidSymbol, VoidSymbol, VoidSymbol, VoidSymbol, KP_5}),
new KeySymbol(KeyEvent.VK_NUMPAD6, new int[]{VoidSymbol, VoidSymbol, VoidSymbol, VoidSymbol, KP_6}),
new KeySymbol(KeyEvent.VK_NUMPAD7, new int[]{VoidSymbol, VoidSymbol, VoidSymbol, VoidSymbol, KP_7}),
new KeySymbol(KeyEvent.VK_NUMPAD8, new int[]{VoidSymbol, VoidSymbol, VoidSymbol, VoidSymbol, KP_8}),
new KeySymbol(KeyEvent.VK_NUMPAD9, new int[]{VoidSymbol, VoidSymbol, VoidSymbol, VoidSymbol, KP_9}),
new KeySymbol(KeyEvent.VK_DECIMAL, new int[]{VoidSymbol, KP_Decimal, VoidSymbol, VoidSymbol, KP_Decimal}),
new KeySymbol(KeyEvent.VK_ADD, new int[]{VoidSymbol, KP_Add, VoidSymbol, VoidSymbol, KP_Add}),
new KeySymbol(KeyEvent.VK_SUBTRACT, new int[]{VoidSymbol, KP_Subtract, VoidSymbol, VoidSymbol, KP_Subtract}),
new KeySymbol(KeyEvent.VK_MULTIPLY, new int[]{VoidSymbol, KP_Multiply, VoidSymbol, VoidSymbol, KP_Multiply}),
new KeySymbol(KeyEvent.VK_DIVIDE, new int[]{VoidSymbol, KP_Divide, VoidSymbol, VoidSymbol, KP_Divide}),
new KeySymbol(KeyEvent.VK_DELETE, new int[]{VoidSymbol, Delete, VoidSymbol, VoidSymbol, KP_Delete}),
new KeySymbol(KeyEvent.VK_CLEAR, new int[]{VoidSymbol, Clear, VoidSymbol, VoidSymbol, KP_Begin}),
new KeySymbol(KeyEvent.VK_HOME, new int[]{VoidSymbol, Home, VoidSymbol, VoidSymbol, KP_Home}),
new KeySymbol(KeyEvent.VK_END, new int[]{VoidSymbol, End, VoidSymbol, VoidSymbol, KP_End}),
new KeySymbol(KeyEvent.VK_PAGE_UP, new int[]{VoidSymbol, Page_Up, VoidSymbol, VoidSymbol, KP_Page_Up}),
new KeySymbol(KeyEvent.VK_PAGE_DOWN, new int[]{VoidSymbol, Page_Down, VoidSymbol, VoidSymbol, KP_Page_Down}),
new KeySymbol(KeyEvent.VK_UP, new int[]{VoidSymbol, Up, VoidSymbol, VoidSymbol, KP_Up}),
new KeySymbol(KeyEvent.VK_DOWN, new int[]{VoidSymbol, Down, VoidSymbol, VoidSymbol, KP_Down}),
new KeySymbol(KeyEvent.VK_LEFT, new int[]{VoidSymbol, Left, VoidSymbol, VoidSymbol, KP_Left}),
new KeySymbol(KeyEvent.VK_RIGHT, new int[]{VoidSymbol, Right, VoidSymbol, VoidSymbol, KP_Right}),
new KeySymbol(KeyEvent.VK_BEGIN, new int[]{VoidSymbol, Begin, VoidSymbol, VoidSymbol, KP_Begin}),
new KeySymbol(KeyEvent.VK_KP_LEFT, new int[]{VoidSymbol, KP_Left, VoidSymbol, VoidSymbol, KP_Left}),
new KeySymbol(KeyEvent.VK_KP_UP, new int[]{VoidSymbol, KP_Up, VoidSymbol, VoidSymbol, KP_Up}),
new KeySymbol(KeyEvent.VK_KP_RIGHT, new int[]{VoidSymbol, KP_Right, VoidSymbol, VoidSymbol, KP_Right}),
new KeySymbol(KeyEvent.VK_KP_DOWN, new int[]{VoidSymbol, KP_Down, VoidSymbol, VoidSymbol, KP_Down}),
new KeySymbol(KeyEvent.VK_PRINTSCREEN, new int[]{VoidSymbol, Print, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_SCROLL_LOCK, new int[]{VoidSymbol, Scroll_Lock, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_CAPS_LOCK, new int[]{VoidSymbol, Caps_Lock, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_NUM_LOCK, new int[]{VoidSymbol, Num_Lock, VoidSymbol, VoidSymbol, Num_Lock}),
new KeySymbol(KeyEvent.VK_INSERT, new int[]{VoidSymbol, Insert, VoidSymbol, VoidSymbol, KP_Insert}),
new KeySymbol(KeyEvent.VK_AGAIN, new int[]{VoidSymbol, Redo, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_UNDO, new int[]{VoidSymbol, Undo, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_FIND, new int[]{VoidSymbol, Find, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_STOP, new int[]{VoidSymbol, Cancel, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_HELP, new int[]{VoidSymbol, Help, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_WINDOWS, new int[]{VoidSymbol, Super_L, Super_L, Super_R, VoidSymbol}),
new KeySymbol(KeyEvent.VK_CONTEXT_MENU, new int[]{VoidSymbol, Menu, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_KANJI, new int[]{VoidSymbol, Kanji, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_KATAKANA, new int[]{VoidSymbol, Katakana, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_HIRAGANA, new int[]{VoidSymbol, Hiragana, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_PREVIOUS_CANDIDATE,
new int[]{VoidSymbol, PreviousCandidate,VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_CODE_INPUT, new int[]{VoidSymbol, Codeinput, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_JAPANESE_ROMAN,
new int[]{VoidSymbol, Romaji, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_KANA_LOCK, new int[]{VoidSymbol, Kana_Lock, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_ABOVEDOT,new int[]{VoidSymbol, Dead_AboveDot, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_ABOVERING,
new int[]{VoidSymbol, Dead_AboveRing, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_ACUTE, new int[]{VoidSymbol, Dead_Acute, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_BREVE, new int[]{VoidSymbol, Dead_Breve, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_CARON, new int[]{VoidSymbol, Dead_Caron, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_CIRCUMFLEX,
new int[]{VoidSymbol, Dead_Circumflex, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_DIAERESIS,
new int[]{VoidSymbol, Dead_Diaeresis, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_DOUBLEACUTE,
new int[]{VoidSymbol, Dead_DoubleAcute, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_GRAVE, new int[]{VoidSymbol, Dead_Grave, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_IOTA, new int[]{VoidSymbol, Dead_Iota, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_MACRON, new int[]{VoidSymbol, Dead_Macron, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_OGONEK, new int[]{VoidSymbol, Dead_Ogonek, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_SEMIVOICED_SOUND,
new int[]{VoidSymbol, Dead_SemiVoiced_Sound,VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_TILDE, new int[]{VoidSymbol, Dead_Tilde, VoidSymbol, VoidSymbol, VoidSymbol}),
new KeySymbol(KeyEvent.VK_DEAD_VOICED_SOUND,
new int[]{VoidSymbol, Dead_Voiced_Sound, VoidSymbol, VoidSymbol, VoidSymbol}),
};

public static int translateKeyEvent(KeyEvent ev) {
int location = ev.getKeyLocation();
int keyCode = ev.getKeyCode();

// First check for function keys
if (keyCode >= KeyEvent.VK_F1 && keyCode <= KeyEvent.VK_F12)
return Keysyms.F1 + keyCode - KeyEvent.VK_F1;
if (keyCode >= KeyEvent.VK_F13 && keyCode <= KeyEvent.VK_F24)
return Keysyms.F13 + keyCode - KeyEvent.VK_F13;

// Numpad numbers
if (keyCode >= KeyEvent.VK_0 && keyCode <= KeyEvent.VK_9 &&
location == KeyEvent.KEY_LOCATION_NUMPAD)
return Keysyms.KP_0 + keyCode - KeyEvent.VK_0;

if (VncViewer.os.startsWith("mac os x")) {
// Alt on OS X behaves more like AltGr on other systems, and to get
// sane behaviour we should translate things in that manner for the
// remote VNC server. However that means we lose the ability to use
// Alt as a shortcut modifier. Do what RealVNC does and hijack the
// left command key as an Alt replacement.
switch (keyCode) {
case KeyEvent.VK_META:
if (location == KeyEvent.KEY_LOCATION_LEFT)
return Alt_L;
else
return Super_L;
case KeyEvent.VK_ALT:
if (location == KeyEvent.KEY_LOCATION_LEFT)
return Alt_L;
else
return ISO_Level3_Shift;
}
}

// Then other special keys
if (keyCode == KeyEvent.VK_PAUSE)
return (ev.isControlDown() ? Break : Pause);
else if (keyCode == KeyEvent.VK_PRINTSCREEN)
return (ev.isControlDown() ? Sys_Req : Print);
else
for(int i = 0; i < keySymbols.length; i++)
if (keySymbols[i].keycode == keyCode)
return (keySymbols[i].keysym)[location];

// Unknown special key?
if (KeyEvent.getKeyText(keyCode).isEmpty()) {
vlog.error("Unknown key code:");
String fmt = ev.paramString().replaceAll("%","%%");
vlog.error(String.format(fmt.replaceAll(",","%n ")));
return VoidSymbol;
}

char keyChar = ev.getKeyChar();
if (!ev.isControlDown() && keyChar != KeyEvent.CHAR_UNDEFINED)
return UnicodeToKeysym.ucs2keysym(Character.toString(keyChar).codePointAt(0));

int key = keyChar;
if (ev.isControlDown()) {
// For CTRL-<letter>, CTRL is sent separately, so just send <letter>.
if ((key >= 1 && key <= 26 && !ev.isShiftDown()) ||
// CTRL-{, CTRL-|, CTRL-} also map to ASCII 96-127
(key >= 27 && key <= 29 && ev.isShiftDown()))
key += 96;
// For CTRL-SHIFT-<letter>, send capital <letter> to emulate behavior
// of Linux. For CTRL-@, send @. For CTRL-_, send _. For CTRL-^,
// send ^.
else if (key < 32)
key += 64;
// Windows and Mac sometimes return CHAR_UNDEFINED with CTRL-SHIFT
// combinations, so best we can do is send the key code if it is
// a valid ASCII symbol.
else if (ev.getKeyChar() == KeyEvent.CHAR_UNDEFINED && keyCode >= 0 &&
keyCode <= 127)
key = keyCode;
}
return UnicodeToKeysym.ucs2keysym(key);
}

static LogWriter vlog = new LogWriter("Keysyms");
}

+ 1
- 191
java/com/tigervnc/vncviewer/CConn.java 查看文件

@@ -1,7 +1,7 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2013 Pierre Ossman <ossman@cendio.se> for Cendio AB
* Copyright (C) 2011-2013 D. R. Commander. All Rights Reserved.
* Copyright (C) 2011-2015 Brian P. Hinz
* Copyright (C) 2011-2017 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -77,10 +77,6 @@ public class CConn extends CConnection implements
static final PixelFormat mediumColorPF =
new PixelFormat(8, 8, false, true, 7, 7, 3, 5, 2, 0);

static final int KEY_LOC_SHIFT_R = 0;
static final int KEY_LOC_SHIFT_L = 16;
static final int SUPER_MASK = 1<<15;

////////////////////////////////////////////////////////////////////
// The following methods are all called from the RFB thread

@@ -95,7 +91,6 @@ public class CConn extends CConnection implements

setShared(shared.getValue());
sock = socket;
downKeySym = new HashMap<Integer, Integer>();

upg = this;

@@ -706,47 +701,6 @@ public class CConn extends CConnection implements
}
}

void showInfo() {
Window fullScreenWindow = DesktopWindow.getFullScreenWindow();
if (fullScreenWindow != null)
DesktopWindow.setFullScreenWindow(null);
String info = new String("Desktop name: %s%n"+
"Host: %s:%d%n"+
"Size: %dx%d%n"+
"Pixel format: %s%n"+
" (server default: %s)%n"+
"Requested encoding: %s%n"+
"Last used encoding: %s%n"+
"Line speed estimate: %d kbit/s%n"+
"Protocol version: %d.%d%n"+
"Security method: %s [%s]%n");
String msg =
String.format(info, cp.name(),
sock.getPeerName(), sock.getPeerPort(),
cp.width, cp.height,
cp.pf().print(),
serverPF.print(),
Encodings.encodingName(currentEncoding),
Encodings.encodingName(lastServerEncoding),
sock.inStream().kbitsPerSecond(),
cp.majorVersion, cp.minorVersion,
Security.secTypeName(csecurity.getType()),
csecurity.description());
JOptionPane op = new JOptionPane(msg, JOptionPane.PLAIN_MESSAGE,
JOptionPane.DEFAULT_OPTION);
JDialog dlg = op.createDialog(desktop, "VNC connection info");
dlg.setIconImage(VncViewer.frameIcon);
dlg.setAlwaysOnTop(true);
dlg.setVisible(true);
if (fullScreenWindow != null)
DesktopWindow.setFullScreenWindow(fullScreenWindow);
}

public void refresh() {
writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height), false);
pendingUpdate = true;
}

// writeClientCutText() is called from the clipboard dialog
public void writeClientCutText(String str, int len) {
if (state() != RFBSTATE_NORMAL || shuttingDown)
@@ -754,147 +708,6 @@ public class CConn extends CConnection implements
writer().writeClientCutText(str, len);
}

public void writeKeyEvent(int keysym, boolean down) {
if (state() != RFBSTATE_NORMAL || shuttingDown)
return;
writer().keyEvent(keysym, down);
}

public void writeKeyEvent(KeyEvent ev) {
if (viewOnly.getValue() || shuttingDown)
return;

boolean down = (ev.getID() == KeyEvent.KEY_PRESSED);

int keySym, keyCode = ev.getKeyCode();

// If neither the keyCode or keyChar are defined, then there's
// really nothing that we can do with this. The fn key on OS-X
// fires events like this when pressed but does not fire a
// corresponding release event.
if (keyCode == 0 && ev.getKeyChar() == KeyEvent.CHAR_UNDEFINED)
return;

if (!down) {
Integer iter = downKeySym.get(keyCode);
if (iter == null) {
// Note that dead keys will raise this sort of error falsely
// See https://bugs.openjdk.java.net/browse/JDK-6534883
vlog.debug("Unexpected key release of keyCode "+keyCode);
String fmt = ev.paramString().replaceAll("%","%%");
vlog.debug(String.format(fmt.replaceAll(",","%n ")));

return;
}

vlog.debug(String.format("Key released: 0x%04x => 0x%04x",
keyCode, iter));

writeKeyEvent(iter, false);
downKeySym.remove(keyCode);

return;
}

keySym = Keysyms.translateKeyEvent(ev);
if (keySym == Keysyms.VoidSymbol)
return;

boolean need_cheat = true;
if (VncViewer.os.startsWith("windows")) {
// Windows doesn't have a proper AltGr, but handles it using fake
// Ctrl+Alt. Unfortunately X11 doesn't generally like the combination
// Ctrl+Alt+AltGr, which we usually end up with when Xvnc tries to
// get everything in the correct state. Cheat and temporarily release
// Ctrl and Alt whenever we get a key with a symbol.
if (KeyEvent.getKeyText(keyCode).isEmpty())
need_cheat = false;
else if (!downKeySym.containsValue(Keysyms.Control_L) &&
!downKeySym.containsValue(Keysyms.Control_R))
need_cheat = false;
else if (!downKeySym.containsValue(Keysyms.Alt_L) &&
!downKeySym.containsValue(Keysyms.Alt_R))
need_cheat = false;

if (need_cheat) {
vlog.info("Faking release of AltGr (Ctrl+Alt)");
if (downKeySym.containsValue(Keysyms.Control_L))
writeKeyEvent(Keysyms.Control_L, false);
if (downKeySym.containsValue(Keysyms.Control_R))
writeKeyEvent(Keysyms.Control_R, false);
if (downKeySym.containsValue(Keysyms.Alt_L))
writeKeyEvent(Keysyms.Alt_L, false);
if (downKeySym.containsValue(Keysyms.Alt_R))
writeKeyEvent(Keysyms.Alt_R, false);
}
}

vlog.debug(String.format("Key pressed: 0x%04x '%s' => 0x%04x",
keyCode, Character.toString(ev.getKeyChar()), keySym));

downKeySym.put(keyCode, keySym);

writeKeyEvent(keySym, down);

if (VncViewer.os.startsWith("windows")) {
if (need_cheat) {
vlog.debug("Restoring AltGr state");
if (downKeySym.containsValue(Keysyms.Control_L))
writeKeyEvent(Keysyms.Control_L, true);
if (downKeySym.containsValue(Keysyms.Control_R))
writeKeyEvent(Keysyms.Control_R, true);
if (downKeySym.containsValue(Keysyms.Alt_L))
writeKeyEvent(Keysyms.Alt_L, true);
if (downKeySym.containsValue(Keysyms.Alt_R))
writeKeyEvent(Keysyms.Alt_R, true);
}
}
}

public void writePointerEvent(MouseEvent ev) {
if (state() != RFBSTATE_NORMAL || shuttingDown)
return;

switch (ev.getID()) {
case MouseEvent.MOUSE_PRESSED:
buttonMask = 1;
if ((ev.getModifiers() & KeyEvent.ALT_MASK) != 0) buttonMask = 2;
if ((ev.getModifiers() & KeyEvent.META_MASK) != 0) buttonMask = 4;
break;
case MouseEvent.MOUSE_RELEASED:
buttonMask = 0;
break;
}

writer().pointerEvent(new Point(ev.getX(), ev.getY()), buttonMask);
}

public void writeWheelEvent(MouseWheelEvent ev) {
if (state() != RFBSTATE_NORMAL || shuttingDown)
return;
int x, y;
int clicks = ev.getWheelRotation();
if (clicks < 0) {
buttonMask = 8;
} else {
buttonMask = 16;
}
for (int i = 0; i < Math.abs(clicks); i++) {
x = ev.getX();
y = ev.getY();
writer().pointerEvent(new Point(x, y), buttonMask);
buttonMask = 0;
writer().pointerEvent(new Point(x, y), buttonMask);
}

}

synchronized void releaseDownKeys() {
for (Map.Entry<Integer, Integer> entry : downKeySym.entrySet())
writeKeyEvent(entry.getValue(), false);
downKeySym.clear();
}

// this is a special ActionListener passed in by the
// Java Plug-in software to control applet's close behavior
public void setCloseListener(ActionListener cl) {
@@ -930,8 +743,6 @@ public class CConn extends CConnection implements
// from when constructed).

// the following are only ever accessed by the GUI thread:
int buttonMask;

private String serverHost;
private int serverPort;
private Socket sock;
@@ -957,7 +768,6 @@ public class CConn extends CConnection implements

private boolean supportsSyncFence;

private HashMap<Integer, Integer> downKeySym;
public ActionListener closeListener = null;

static LogWriter vlog = new LogWriter("CConn");

+ 1
- 1
java/com/tigervnc/vncviewer/DesktopWindow.java 查看文件

@@ -81,7 +81,7 @@ public class DesktopWindow extends JFrame
scroll.getViewport().getView().requestFocusInWindow();
}
public void windowLostFocus(WindowEvent e) {
cc.releaseDownKeys();
viewport.releaseDownKeys();
}
});


+ 0
- 203
java/com/tigervnc/vncviewer/F8Menu.java 查看文件

@@ -1,203 +0,0 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011-2014 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/

package com.tigervnc.vncviewer;

import java.awt.*;
import java.awt.Cursor;
import java.awt.event.*;
import java.io.File;
import javax.swing.filechooser.*;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;

import com.tigervnc.rfb.*;

import static com.tigervnc.vncviewer.Parameters.*;

public class F8Menu extends JPopupMenu implements ActionListener {
public F8Menu(CConn cc) {
super("VNC Menu");
setLightWeightPopupEnabled(false);
String os = System.getProperty("os.name");
if (os.startsWith("Windows"))
com.sun.java.swing.plaf.windows.WindowsLookAndFeel.setMnemonicHidden(false);
this.cc = cc;
restore = addMenuItem("Restore",KeyEvent.VK_R);
restore.setEnabled(!embed.getValue());
move = addMenuItem("Move");
move.setEnabled(false);
size = addMenuItem("Size");
size.setEnabled(false);
minimize = addMenuItem("Minimize", KeyEvent.VK_N);
minimize.setEnabled(!embed.getValue());
maximize = addMenuItem("Maximize", KeyEvent.VK_X);
maximize.setEnabled(!embed.getValue());
addSeparator();
exit = addMenuItem("Close Viewer", KeyEvent.VK_C);
addSeparator();
fullScreenCheckbox = new JCheckBoxMenuItem("Full Screen");
fullScreenCheckbox.setMnemonic(KeyEvent.VK_F);
fullScreenCheckbox.setSelected(fullScreen.getValue());
fullScreenCheckbox.addActionListener(this);
fullScreenCheckbox.setEnabled(!embed.getValue());
add(fullScreenCheckbox);
addSeparator();
clipboard = addMenuItem("Clipboard...");
addSeparator();
int keyCode = MenuKey.getMenuKeyCode();
String keyText = KeyEvent.getKeyText(keyCode);
f8 = addMenuItem("Send "+keyText, keyCode);
ctrlAltDel = addMenuItem("Send Ctrl-Alt-Del");
addSeparator();
refresh = addMenuItem("Refresh Screen", KeyEvent.VK_H);
addSeparator();
newConn = addMenuItem("New connection...", KeyEvent.VK_W);
newConn.setEnabled(!embed.getValue());
options = addMenuItem("Options...", KeyEvent.VK_O);
save = addMenuItem("Save connection info as...", KeyEvent.VK_S);
info = addMenuItem("Connection info...", KeyEvent.VK_I);
about = addMenuItem("About VncViewer...", KeyEvent.VK_A);
addSeparator();
dismiss = addMenuItem("Dismiss menu");
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}

JMenuItem addMenuItem(String str, int mnemonic) {
JMenuItem item = new JMenuItem(str, mnemonic);
item.addActionListener(this);
add(item);
return item;
}

JMenuItem addMenuItem(String str) {
JMenuItem item = new JMenuItem(str);
item.addActionListener(this);
add(item);
return item;
}

boolean actionMatch(ActionEvent ev, JMenuItem item) {
return ev.getActionCommand().equals(item.getActionCommand());
}

public void actionPerformed(ActionEvent ev) {
if (actionMatch(ev, exit)) {
cc.close();
} else if (actionMatch(ev, fullScreenCheckbox)) {
if (fullScreenCheckbox.isSelected())
cc.desktop.fullscreen_on();
else
cc.desktop.fullscreen_off();
} else if (actionMatch(ev, restore)) {
if (cc.desktop.fullscreen_active())
cc.desktop.fullscreen_off();
cc.desktop.setExtendedState(JFrame.NORMAL);
} else if (actionMatch(ev, minimize)) {
if (cc.desktop.fullscreen_active())
cc.desktop.fullscreen_off();
cc.desktop.setExtendedState(JFrame.ICONIFIED);
} else if (actionMatch(ev, maximize)) {
if (cc.desktop.fullscreen_active())
cc.desktop.fullscreen_off();
cc.desktop.setExtendedState(JFrame.MAXIMIZED_BOTH);
} else if (actionMatch(ev, clipboard)) {
ClipboardDialog.showDialog(cc.desktop);
} else if (actionMatch(ev, f8)) {
cc.writeKeyEvent(MenuKey.getMenuKeySym(), true);
cc.writeKeyEvent(MenuKey.getMenuKeySym(), false);
} else if (actionMatch(ev, ctrlAltDel)) {
cc.writeKeyEvent(Keysyms.Control_L, true);
cc.writeKeyEvent(Keysyms.Alt_L, true);
cc.writeKeyEvent(Keysyms.Delete, true);
cc.writeKeyEvent(Keysyms.Delete, false);
cc.writeKeyEvent(Keysyms.Alt_L, false);
cc.writeKeyEvent(Keysyms.Control_L, false);
} else if (actionMatch(ev, refresh)) {
cc.refresh();
} else if (actionMatch(ev, newConn)) {
VncViewer.newViewer();
} else if (actionMatch(ev, options)) {
OptionsDialog.showDialog(cc.desktop);
} else if (actionMatch(ev, save)) {
String title = "Save the TigerVNC configuration to file";
File dflt = new File(FileUtils.getVncHomeDir().concat("default.tigervnc"));
if (!dflt.exists() || !dflt.isFile())
dflt = new File(FileUtils.getVncHomeDir());
FileNameExtensionFilter filter =
new FileNameExtensionFilter("TigerVNC configuration (*.tigervnc)", "tigervnc");
File f = Dialog.showChooser(title, dflt, this, filter);
while (f != null && f.exists() && f.isFile()) {
String msg = f.getAbsolutePath();
msg = msg.concat(" already exists. Do you want to overwrite?");
Object[] options = {"Overwrite", "No \u21B5"};
JOptionPane op =
new JOptionPane(msg, JOptionPane.QUESTION_MESSAGE,
JOptionPane.OK_CANCEL_OPTION, null, options, options[1]);
JDialog dlg = op.createDialog(this, "TigerVNC Viewer");
dlg.setIconImage(VncViewer.frameIcon);
dlg.setAlwaysOnTop(true);
dlg.setVisible(true);
if (op.getValue() == options[0])
break;
else
f = Dialog.showChooser(title, f, this, filter);
}
if (f != null && (!f.exists() || f.canWrite()))
saveViewerParameters(f.getAbsolutePath(), vncServerName.getValue());
} else if (actionMatch(ev, info)) {
cc.showInfo();
} else if (actionMatch(ev, about)) {
VncViewer.showAbout(cc.desktop);
} else if (actionMatch(ev, dismiss)) {
firePopupMenuCanceled();
}
}

public void show(Component invoker, int x, int y) {
// lightweight components can't show in FullScreen Exclusive mode
/*
Window fsw = DesktopWindow.getFullScreenWindow();
GraphicsDevice gd = null;
if (fsw != null) {
gd = fsw.getGraphicsConfiguration().getDevice();
if (gd.isFullScreenSupported())
DesktopWindow.setFullScreenWindow(null);
}
*/
super.show(invoker, x, y);
/*
if (fsw != null && gd.isFullScreenSupported())
DesktopWindow.setFullScreenWindow(fsw);
*/
}

CConn cc;
JMenuItem restore, move, size, minimize, maximize;
JMenuItem exit, clipboard, ctrlAltDel, refresh;
JMenuItem newConn, options, save, info, about, dismiss;
static JMenuItem f8;
JCheckBoxMenuItem fullScreenCheckbox;
static LogWriter vlog = new LogWriter("F8Menu");
}

+ 235
- 0
java/com/tigervnc/vncviewer/KeyMap.java 查看文件

@@ -0,0 +1,235 @@
/* Copyright (C) 2017 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/

package com.tigervnc.vncviewer;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

import com.tigervnc.rfb.*;

import static java.awt.event.KeyEvent.*;
import static com.tigervnc.rfb.Keysymdef.*;

public class KeyMap {

public final static int NoSymbol = 0;

private static final HashMap<Integer, Character> code_map_java_to_char;
static {
// Certain KeyStrokes fail to produce a valid character (CTRL+ALT+...
// on Windows and Mac almost never does). For these cases, the best
// we can try to do is to map to the ASCII symbol from the keyCode.
code_map_java_to_char = new HashMap<Integer, Character>();
for (int c=32; c<0x7f; c++) {
int keyCode = KeyEvent.getExtendedKeyCodeForChar(c);
if (keyCode != KeyEvent.VK_UNDEFINED)
// Not all ASCII characters have VK_ constants, see vk_to_ascii()
code_map_java_to_char.put(keyCode, (char)c);
}
}

private static int[][] vkey_map = {
/* KEYCODE LOCATION */
/* UNKNOWN STANDARD LEFT RIGHT NUMPAD */
{ VK_BACK_SPACE, NoSymbol, XK_BackSpace, NoSymbol, NoSymbol, NoSymbol },
{ VK_TAB, NoSymbol, XK_Tab, NoSymbol, NoSymbol, NoSymbol },
{ VK_CANCEL, NoSymbol, XK_Cancel, NoSymbol, NoSymbol, NoSymbol },
{ VK_ENTER, NoSymbol, XK_Return, NoSymbol, NoSymbol, XK_KP_Enter },
{ VK_SHIFT, NoSymbol, XK_Shift_L, XK_Shift_L, XK_Shift_R, NoSymbol },
{ VK_CONTROL, NoSymbol, XK_Control_L, XK_Control_L, XK_Control_R, NoSymbol },
{ VK_ALT, NoSymbol, XK_Alt_L, XK_Alt_L, XK_Alt_R, NoSymbol },
/* VK_PAUSE left out on purpose because interpretation depends on state of CTRL. See further down. */
{ VK_CAPS_LOCK, NoSymbol, XK_Caps_Lock, NoSymbol, NoSymbol, NoSymbol },
{ VK_ESCAPE, NoSymbol, XK_Escape, NoSymbol, NoSymbol, NoSymbol },
{ VK_END, NoSymbol, XK_End, NoSymbol, NoSymbol, XK_KP_End },
{ VK_HOME, NoSymbol, XK_Home, NoSymbol, NoSymbol, XK_KP_Home },
{ VK_LEFT, NoSymbol, XK_Left, NoSymbol, NoSymbol, XK_KP_Left },
{ VK_UP, NoSymbol, XK_Up, NoSymbol, NoSymbol, XK_KP_Up },
{ VK_RIGHT, NoSymbol, XK_Right, NoSymbol, NoSymbol, XK_KP_Right },
{ VK_DOWN, NoSymbol, XK_Down, NoSymbol, NoSymbol, XK_KP_Down },
/* VK_PRINTSCREEN left out on purpose because interpretation depends on state of CTRL. See further down. */
{ VK_PAGE_UP, NoSymbol, XK_Page_Up, NoSymbol, NoSymbol, XK_KP_Page_Up },
{ VK_PAGE_DOWN, NoSymbol, XK_Page_Down, NoSymbol, NoSymbol, XK_KP_Page_Down },
{ VK_BEGIN, NoSymbol, XK_Begin, NoSymbol, NoSymbol, XK_KP_Begin },
{ VK_KP_LEFT, NoSymbol, XK_KP_Left, NoSymbol, NoSymbol, XK_KP_Left },
{ VK_KP_UP, NoSymbol, XK_KP_Up, NoSymbol, NoSymbol, XK_KP_Up },
{ VK_KP_RIGHT, NoSymbol, XK_KP_Right, NoSymbol, NoSymbol, XK_KP_Right },
{ VK_KP_DOWN, NoSymbol, XK_KP_Down, NoSymbol, NoSymbol, XK_KP_Down },
{ VK_INSERT, NoSymbol, XK_Insert, NoSymbol, NoSymbol, XK_KP_Insert },
{ VK_DELETE, NoSymbol, XK_Delete, NoSymbol, NoSymbol, XK_KP_Delete },
{ VK_WINDOWS, NoSymbol, NoSymbol, XK_Super_L, XK_Super_R, NoSymbol },
{ VK_CONTEXT_MENU, NoSymbol, XK_Menu, NoSymbol, NoSymbol, NoSymbol },
{ VK_NUMPAD0, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_0 },
{ VK_NUMPAD1, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_1 },
{ VK_NUMPAD2, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_2 },
{ VK_NUMPAD3, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_3 },
{ VK_NUMPAD4, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_4 },
{ VK_NUMPAD5, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_5 },
{ VK_NUMPAD6, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_6 },
{ VK_NUMPAD7, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_7 },
{ VK_NUMPAD8, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_8 },
{ VK_NUMPAD9, NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_KP_9 },
{ VK_MULTIPLY, NoSymbol, XK_KP_Multiply, NoSymbol, NoSymbol, XK_KP_Multiply },
{ VK_ADD, NoSymbol, XK_KP_Add, NoSymbol, NoSymbol, XK_KP_Add },
{ VK_SUBTRACT, NoSymbol, XK_KP_Subtract, NoSymbol, NoSymbol, XK_KP_Subtract },
{ VK_DIVIDE, NoSymbol, XK_KP_Divide, NoSymbol, NoSymbol, XK_KP_Divide },
{ VK_SEPARATER, NoSymbol, XK_KP_Separator, NoSymbol, NoSymbol, XK_KP_Separator },
{ VK_DECIMAL, NoSymbol, XK_KP_Decimal, NoSymbol, NoSymbol, XK_KP_Decimal },
{ VK_F1, NoSymbol, XK_F1, XK_L1, XK_R1, NoSymbol },
{ VK_F2, NoSymbol, XK_F2, XK_L2, XK_R2, NoSymbol },
{ VK_F3, NoSymbol, XK_F3, XK_L3, XK_R3, NoSymbol },
{ VK_F4, NoSymbol, XK_F4, XK_L4, XK_R4, NoSymbol },
{ VK_F5, NoSymbol, XK_F5, XK_L5, XK_R5, NoSymbol },
{ VK_F6, NoSymbol, XK_F6, XK_L6, XK_R6, NoSymbol },
{ VK_F7, NoSymbol, XK_F7, XK_L7, XK_R7, NoSymbol },
{ VK_F8, NoSymbol, XK_F8, XK_L8, XK_R8, NoSymbol },
{ VK_F9, NoSymbol, XK_F9, XK_L9, XK_R9, NoSymbol },
{ VK_F10, NoSymbol, XK_F10, XK_L10, XK_R10, NoSymbol },
{ VK_F11, NoSymbol, XK_F11, NoSymbol, XK_R11, NoSymbol },
{ VK_F12, NoSymbol, XK_F12, NoSymbol, XK_R12, NoSymbol },
{ VK_F13, NoSymbol, XK_F13, NoSymbol, XK_R13, NoSymbol },
{ VK_F14, NoSymbol, XK_F14, NoSymbol, XK_R14, NoSymbol },
{ VK_F15, NoSymbol, XK_F15, NoSymbol, XK_R15, NoSymbol },
{ VK_F16, NoSymbol, XK_F16, NoSymbol, NoSymbol, NoSymbol },
{ VK_F17, NoSymbol, XK_F17, NoSymbol, NoSymbol, NoSymbol },
{ VK_F18, NoSymbol, XK_F18, NoSymbol, NoSymbol, NoSymbol },
{ VK_F19, NoSymbol, XK_F19, NoSymbol, NoSymbol, NoSymbol },
{ VK_F20, NoSymbol, XK_F20, NoSymbol, NoSymbol, NoSymbol },
{ VK_F21, NoSymbol, XK_F21, NoSymbol, NoSymbol, NoSymbol },
{ VK_F22, NoSymbol, XK_F22, NoSymbol, NoSymbol, NoSymbol },
{ VK_F23, NoSymbol, XK_F23, NoSymbol, NoSymbol, NoSymbol },
{ VK_F24, NoSymbol, XK_F24, NoSymbol, NoSymbol, NoSymbol },
{ VK_NUM_LOCK, NoSymbol, XK_Num_Lock, NoSymbol, NoSymbol, XK_Num_Lock },
{ VK_SCROLL_LOCK, NoSymbol, XK_Scroll_Lock, NoSymbol, NoSymbol, NoSymbol },
{ VK_ALT_GRAPH, NoSymbol, XK_ISO_Level3_Shift, NoSymbol, NoSymbol, NoSymbol },
{ VK_META, NoSymbol, NoSymbol, XK_Meta_L, XK_Meta_R, NoSymbol },
{ VK_MODECHANGE, NoSymbol, XK_Mode_switch, NoSymbol, NoSymbol, NoSymbol },
{ VK_CLEAR, NoSymbol, XK_Clear, NoSymbol, NoSymbol, XK_KP_Begin },
{ VK_AGAIN, NoSymbol, XK_Redo, NoSymbol, NoSymbol, NoSymbol },
{ VK_UNDO, NoSymbol, XK_Undo, NoSymbol, NoSymbol, NoSymbol },
{ VK_FIND, NoSymbol, XK_Find, NoSymbol, NoSymbol, NoSymbol },
{ VK_STOP, NoSymbol, XK_Cancel, NoSymbol, NoSymbol, NoSymbol },
{ VK_HELP, NoSymbol, XK_Help, NoSymbol, NoSymbol, NoSymbol },
{ VK_KANJI, NoSymbol, XK_Kanji, NoSymbol, NoSymbol, NoSymbol },
{ VK_KATAKANA, NoSymbol, XK_Katakana, NoSymbol, NoSymbol, NoSymbol },
{ VK_HIRAGANA, NoSymbol, XK_Hiragana, NoSymbol, NoSymbol, NoSymbol },
{ VK_PREVIOUS_CANDIDATE, NoSymbol, XK_PreviousCandidate, NoSymbol, NoSymbol, NoSymbol },
{ VK_CODE_INPUT, NoSymbol, XK_Codeinput, NoSymbol, NoSymbol, NoSymbol },
{ VK_JAPANESE_ROMAN, NoSymbol, XK_Romaji, NoSymbol, NoSymbol, NoSymbol },
{ VK_KANA_LOCK, NoSymbol, XK_Kana_Lock, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_ABOVEDOT, NoSymbol, XK_dead_abovedot, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_ABOVERING, NoSymbol, XK_dead_abovering, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_ACUTE, NoSymbol, XK_dead_acute, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_BREVE, NoSymbol, XK_dead_breve, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_CARON, NoSymbol, XK_dead_caron, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_CEDILLA, NoSymbol, XK_dead_cedilla, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_CIRCUMFLEX, NoSymbol, XK_dead_circumflex, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_DIAERESIS, NoSymbol, XK_dead_diaeresis, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_DOUBLEACUTE, NoSymbol, XK_dead_doubleacute, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_GRAVE, NoSymbol, XK_dead_grave, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_IOTA, NoSymbol, XK_dead_iota, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_MACRON, NoSymbol, XK_dead_macron, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_OGONEK, NoSymbol, XK_dead_ogonek, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_SEMIVOICED_SOUND, NoSymbol, XK_dead_semivoiced_sound, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_TILDE, NoSymbol, XK_dead_tilde, NoSymbol, NoSymbol, NoSymbol },
{ VK_DEAD_VOICED_SOUND, NoSymbol, XK_dead_voiced_sound, NoSymbol, NoSymbol, NoSymbol },
{ VK_ALPHANUMERIC, NoSymbol, XK_Eisu_Shift, NoSymbol, NoSymbol, NoSymbol },
{ VK_ALL_CANDIDATES, NoSymbol, XK_MultipleCandidate, NoSymbol, NoSymbol, NoSymbol },
{ VK_KANA, NoSymbol, XK_Kana_Shift, NoSymbol, NoSymbol, NoSymbol },
{ VK_JAPANESE_KATAKANA, NoSymbol, XK_Katakana, NoSymbol, NoSymbol, NoSymbol },
{ VK_JAPANESE_HIRAGANA, NoSymbol, XK_Hiragana, NoSymbol, NoSymbol, NoSymbol },
{ VK_COMPOSE, NoSymbol, XK_Multi_key, NoSymbol, NoSymbol, NoSymbol },
};

public static int vkey_to_keysym(KeyEvent ev)
{
int keyCode = ev.getKeyCode();

// Start with keys that either don't generate a symbol, or
// generate the same symbol as some other key.
if (keyCode == KeyEvent.VK_PAUSE)
return (ev.isControlDown() ? XK_Break : XK_Pause);
else if (keyCode == KeyEvent.VK_PRINTSCREEN)
return (ev.isControlDown() ? XK_Sys_Req : XK_Print);
else
for(int i = 0; i < vkey_map.length; i++)
if (keyCode == vkey_map[i][0])
return vkey_map[i][ev.getKeyLocation()+1];

// Unknown special key?
if (KeyEvent.getKeyText(keyCode).isEmpty()) {
vlog.error("Unknown key code: 0x%04x", keyCode);
return NoSymbol;
}

// Pressing Ctrl wreaks havoc with the symbol lookup...
int ucs = (int)ev.getKeyChar();
if (ev.isControlDown()) {
// For CTRL-<letter>, CTRL is sent separately, so just send <letter>.
if ((ucs >= 1 && ucs <= 26 && !ev.isShiftDown()) ||
// CTRL-{, CTRL-|, CTRL-} also map to ASCII 96-127
(ucs >= 27 && ucs <= 29 && ev.isShiftDown()))
ucs += 96;
// For CTRL-SHIFT-<letter>, send capital <letter> to emulate behavior
// of Linux. For CTRL-@, send @. For CTRL-_, send _. For CTRL-^,
// send ^.
else if (ucs < 32)
ucs += 64;
// If it's still undefined, map the keyCode to ASCII symbol
else if (keyCode >= 0 && keyCode <= 127)
if (ucs == CHAR_UNDEFINED || ev.isAltDown())
ucs = vk_to_ascii(keyCode, ev.isShiftDown());
else if (VncViewer.os.startsWith("mac os x") && ev.isMetaDown())
// Alt on OS X behaves more like AltGr on other systems, and to get
// sane behaviour we should translate things in that manner for the
// remote VNC server. However that means we lose the ability to use
// Alt as a shortcut modifier. Do what RealVNC does and hijack the
// left command key as an Alt replacement.
ucs = vk_to_ascii(keyCode, ev.isShiftDown());
}

// Dead keys are represented by their spacing equivalent
// (or something similar depending on the layout)
if (Character.getType(ucs) == Character.COMBINING_SPACING_MARK)
return Keysym2ucs.ucs2keysym(Keysym2ucs.ucs2combining(ucs));

if (Character.isDefined(ucs))
return Keysym2ucs.ucs2keysym(ucs);

return NoSymbol;
}

private static int vk_to_ascii(int vk, boolean shift) {
char c = 0;
if (code_map_java_to_char.containsKey(vk))
c = code_map_java_to_char.get(vk);
// 0x25 (%) and 0x3F (?) do not have VK_ constants
if (vk == VK_5)
c = shift ? '%' : c;
else if (vk == VK_SLASH)
c = shift ? '?' : c;
if (Character.isLetter(c))
c = shift ? Character.toUpperCase(c) : Character.toLowerCase(c);
return (int)c;
}

static LogWriter vlog = new LogWriter("KeyMap");
}

+ 44
- 26
java/com/tigervnc/vncviewer/MenuKey.java 查看文件

@@ -1,6 +1,6 @@
/* Copyright 2011 Martin Koegler <mkoegler@auto.tuwien.ac.at>
* Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
* Copyright 2012 Brian P. Hinz
* Copyright 2012-2017 Brian P. Hinz
*
* 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,42 +24,47 @@ import java.awt.event.KeyEvent;

import com.tigervnc.rfb.*;

import static java.awt.event.KeyEvent.*;
import static com.tigervnc.rfb.Keysymdef.*;

public class MenuKey
{
static class MenuKeySymbol {
public MenuKeySymbol(String name_, int keycode_, int keysym_) {
public MenuKeySymbol(String name_, int javacode_,
int keycode_, int keysym_) {
name = name_;
javacode = javacode_;
keycode = keycode_;
keysym = keysym_;
}
String name;
int javacode;
int keycode;
int keysym;
}

private static final MenuKeySymbol[] menuSymbols = {
new MenuKeySymbol("F1", KeyEvent.VK_F1, Keysyms.F1),
new MenuKeySymbol("F2", KeyEvent.VK_F2, Keysyms.F2),
new MenuKeySymbol("F3", KeyEvent.VK_F3, Keysyms.F3),
new MenuKeySymbol("F4", KeyEvent.VK_F4, Keysyms.F4),
new MenuKeySymbol("F5", KeyEvent.VK_F5, Keysyms.F5),
new MenuKeySymbol("F6", KeyEvent.VK_F6, Keysyms.F6),
new MenuKeySymbol("F7", KeyEvent.VK_F7, Keysyms.F7),
new MenuKeySymbol("F8", KeyEvent.VK_F8, Keysyms.F8),
new MenuKeySymbol("F9", KeyEvent.VK_F9, Keysyms.F9),
new MenuKeySymbol("F10", KeyEvent.VK_F10, Keysyms.F10),
new MenuKeySymbol("F11", KeyEvent.VK_F11, Keysyms.F11),
new MenuKeySymbol("F12", KeyEvent.VK_F12, Keysyms.F12),
new MenuKeySymbol("Pause", KeyEvent.VK_PAUSE, Keysyms.Pause),
new MenuKeySymbol("Print", KeyEvent.VK_PRINTSCREEN, Keysyms.Print),
new MenuKeySymbol("Scroll_Lock", KeyEvent.VK_SCROLL_LOCK,
Keysyms.Scroll_Lock),
new MenuKeySymbol("Escape", KeyEvent.VK_ESCAPE, Keysyms.Escape),
new MenuKeySymbol("Insert", KeyEvent.VK_INSERT, Keysyms.Insert),
new MenuKeySymbol("Delete", KeyEvent.VK_DELETE, Keysyms.Delete),
new MenuKeySymbol("Home", KeyEvent.VK_HOME, Keysyms.Home),
new MenuKeySymbol("Page_Up", KeyEvent.VK_PAGE_UP, Keysyms.Page_Up),
new MenuKeySymbol("Page_Down", KeyEvent.VK_PAGE_DOWN, Keysyms.Page_Down)
new MenuKeySymbol("F1", VK_F1, 0x3b, XK_F1),
new MenuKeySymbol("F2", VK_F2, 0x3c, XK_F2),
new MenuKeySymbol("F3", VK_F3, 0x3d, XK_F3),
new MenuKeySymbol("F4", VK_F4, 0x3e, XK_F4),
new MenuKeySymbol("F5", VK_F5, 0x3f, XK_F5),
new MenuKeySymbol("F6", VK_F6, 0x40, XK_F6),
new MenuKeySymbol("F7", VK_F7, 0x41, XK_F7),
new MenuKeySymbol("F8", VK_F8, 0x42, XK_F8),
new MenuKeySymbol("F9", VK_F9, 0x43, XK_F9),
new MenuKeySymbol("F10", VK_F10, 0x44, XK_F10),
new MenuKeySymbol("F11", VK_F11, 0x57, XK_F11),
new MenuKeySymbol("F12", VK_F12, 0x58, XK_F12),
new MenuKeySymbol("Pause", VK_PAUSE, 0xc6, XK_Pause),
new MenuKeySymbol("Scroll_Lock", VK_SCROLL_LOCK,
0x46, XK_Scroll_Lock),
new MenuKeySymbol("Escape", VK_ESCAPE, 0x01, XK_Escape),
new MenuKeySymbol("Insert", VK_INSERT, 0xd2, XK_Insert),
new MenuKeySymbol("Delete", VK_DELETE, 0xd3, XK_Delete),
new MenuKeySymbol("Home", VK_HOME, 0xc7, XK_Home),
new MenuKeySymbol("Page_Up", VK_PAGE_UP, 0xc9, XK_Page_Up),
new MenuKeySymbol("Page_Down", VK_PAGE_DOWN, 0xd1, XK_Page_Down)
};

static int getMenuKeySymbolCount() {
@@ -87,8 +92,21 @@ public class MenuKey
return s;
}

static int getMenuKeyJavaCode() {
int menuKeyCode = VK_F8;

@SuppressWarnings({"static"})
String menuKeyStr =
Configuration.global().getParam("menuKey").getValueStr();
for(int i = 0; i < getMenuKeySymbolCount(); i++)
if (menuSymbols[i].name.equals(menuKeyStr))
menuKeyCode = menuSymbols[i].javacode;

return menuKeyCode;
}

static int getMenuKeyCode() {
int menuKeyCode = KeyEvent.VK_F8;
int menuKeyCode = VK_F8;

@SuppressWarnings({"static"})
String menuKeyStr =
@@ -101,7 +119,7 @@ public class MenuKey
}

static int getMenuKeySym() {
int menuKeySym = Keysyms.F8;
int menuKeySym = XK_F8;

@SuppressWarnings({"static"})
String menuKeyStr =

+ 1
- 1
java/com/tigervnc/vncviewer/ServerDialog.java 查看文件

@@ -269,7 +269,7 @@ class ServerDialog extends Dialog implements Runnable {
}

private void handleAbout() {
VncViewer.showAbout(this);
VncViewer.about_vncviewer(this);
}

private void handleCancel() {

+ 543
- 155
java/com/tigervnc/vncviewer/Viewport.java 查看文件

@@ -31,16 +31,11 @@
package com.tigervnc.vncviewer;

import java.awt.*;
import java.awt.Color;
import java.awt.color.ColorSpace;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.Clipboard;
import java.io.BufferedReader;
import java.nio.*;
import java.util.*;
import javax.swing.*;

import javax.imageio.*;
@@ -48,46 +43,76 @@ import java.io.*;

import com.tigervnc.rfb.*;
import com.tigervnc.rfb.Cursor;
import com.tigervnc.rfb.Exception;
import com.tigervnc.rfb.Point;

import static java.awt.event.KeyEvent.*;
import static com.tigervnc.vncviewer.Parameters.*;
import static com.tigervnc.rfb.Keysymdef.*;

class Viewport extends JPanel implements MouseListener,
MouseMotionListener, MouseWheelListener, KeyListener {
class Viewport extends JPanel implements ActionListener {

static LogWriter vlog = new LogWriter("Viewport");

enum ID { EXIT, FULLSCREEN, MINIMIZE, RESIZE, NEWVIEWER,
CTRL, ALT, MENUKEY, CTRLALTDEL, CLIPBOARD,
REFRESH, OPTIONS, INFO, ABOUT, DISMISS }

enum MENU { INACTIVE, TOGGLE, VALUE, RADIO,
INVISIBLE, SUBMENU_POINTER, SUBMENU, DIVIDER }

public Viewport(int w, int h, PixelFormat serverPF, CConn cc_)
{
cc = cc_;
setScaledSize(cc.cp.width, cc.cp.height);
setScaledSize(w, h);
frameBuffer = createFramebuffer(serverPF, w, h);
assert(frameBuffer != null);
setBackground(Color.BLACK);

cc.setFramebuffer(frameBuffer);

contextMenu = new JPopupMenu();

OptionsDialog.addCallback("handleOptions", this);

addMouseListener(this);
addMouseWheelListener(this);
addMouseMotionListener(this);
addKeyListener(this);
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { handle(e); }
public void mouseExited(MouseEvent e) { handle(e); }
public void mouseReleased(MouseEvent e) { handle(e); }
public void mousePressed(MouseEvent e) { handle(e); }
});
addMouseWheelListener(new MouseAdapter() {
public void mouseWheelMoved(MouseWheelEvent e) { handle(e); }
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) { handle(e); }
public void mouseMoved(MouseEvent e) { handle(e); }
});
addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) { }
public void keyPressed(KeyEvent e) { handleSystemEvent(e); }
public void keyReleased(KeyEvent e) { handleSystemEvent(e); }
});
addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
ClipboardDialog.clientCutText();
}
public void focusLost(FocusEvent e) {
cc.releaseDownKeys();
releaseDownKeys();
}
});

setFocusTraversalKeysEnabled(false);
setFocusable(true);

setMenuKey();

// Send a fake pointer event so that the server will stop rendering
// a server-side cursor. Ideally we'd like to send the actual pointer
// position, but we can't really tell when the window manager is done
// placing us so we don't have a good time for that.
cc.writer().pointerEvent(new Point(w/2, h/2), 0);
handlePointerEvent(new Point(w/2, h/2), 0);
}

// Most efficient format (from Viewport's point of view)
@@ -127,6 +152,9 @@ class Viewport extends JPanel implements MouseListener,
{
int i;

if (cursor != null)
cursor.flush();

for (i = 0; i < width*height; i++)
if (data[i*4 + 3] != 0) break;

@@ -148,7 +176,6 @@ class Viewport extends JPanel implements MouseListener,
new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
cursor.setRGB(0, 0, width, height, buffer.array(), 0, width);
cursorHotspot = hotspot;

}
}

@@ -158,35 +185,32 @@ class Viewport extends JPanel implements MouseListener,
int x = (int)Math.floor((float)cursorHotspot.x * scaleRatioX);
int y = (int)Math.floor((float)cursorHotspot.y * scaleRatioY);

java.awt.Cursor softCursor;

Dimension cs = tk.getBestCursorSize(cw, ch);
if (cs.width != cw && cs.height != ch) {
cw = Math.min(cw, cs.width);
ch = Math.min(ch, cs.height);
x = (int)Math.min(x, Math.max(cs.width - 1, 0));
y = (int)Math.min(y, Math.max(cs.height - 1, 0));
BufferedImage scaledImage =
new BufferedImage(cs.width, cs.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = scaledImage.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.drawImage(cursor,
0, 0, cw, ch,
0, 0, cursor.getWidth(), cursor.getHeight(), null);
BufferedImage tmp =
new BufferedImage(cs.width, cs.height, BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D g2 = tmp.createGraphics();
g2.drawImage(cursor, 0, 0, cw, ch, 0, 0, width, height, null);
g2.dispose();
java.awt.Point hs = new java.awt.Point(x, y);
softCursor = tk.createCustomCursor(scaledImage, hs, "softCursor");
scaledImage.flush();
} else {
java.awt.Point hs = new java.awt.Point(x, y);
softCursor = tk.createCustomCursor(cursor, hs, "softCursor");
cursor = tmp;
}

cursor.flush();
setCursor(cursor, x, y);
}

setCursor(softCursor);
private void setCursor(Image img, int x, int y)
{
java.awt.Point hotspot;
java.awt.Cursor softCursor;
String name = "rfb cursor";

hotspot = new java.awt.Point(x, y);
softCursor = tk.createCustomCursor(img, hotspot, name);
setCursor(softCursor);
}

public void resize(int x, int y, int w, int h) {
@@ -200,6 +224,54 @@ class Viewport extends JPanel implements MouseListener,
setScaledSize(w, h);
}

public int handle(MouseEvent e)
{
int buttonMask, wheelMask;
switch (e.getID()) {
case MouseEvent.MOUSE_ENTERED:
if (cursor != null)
setCursor(cursor, cursorHotspot.x, cursorHotspot.y);
if (embed.getValue())
requestFocus();
return 1;
case MouseEvent.MOUSE_EXITED:
setCursor(java.awt.Cursor.getDefaultCursor());
return 1;
case MouseEvent.MOUSE_PRESSED:
case MouseEvent.MOUSE_RELEASED:
case MouseEvent.MOUSE_DRAGGED:
case MouseEvent.MOUSE_MOVED:
case MouseEvent.MOUSE_WHEEL:
buttonMask = 0;
if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0)
buttonMask |= 1;
if ((e.getModifiersEx() & MouseEvent.BUTTON2_DOWN_MASK) != 0)
buttonMask |= 2;
if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) != 0)
buttonMask |= 4;

if (e.getID() == MouseEvent.MOUSE_WHEEL) {
wheelMask = 0;
int clicks = ((MouseWheelEvent)e).getWheelRotation();
if (clicks < 0)
wheelMask |= e.isShiftDown() ? 32 : 8;
else
wheelMask |= e.isShiftDown() ? 64 : 16;
Point pt = new Point(e.getX(), e.getY());
for (int i = 0; i < Math.abs(clicks); i++) {
handlePointerEvent(pt, buttonMask|wheelMask);
handlePointerEvent(pt, buttonMask);
}
return 1;
}

handlePointerEvent(new Point(e.getX(), e.getY()), buttonMask);
return 1;
}

return -1;
}

private PlatformPixelBuffer createFramebuffer(PixelFormat pf, int w, int h)
{
PlatformPixelBuffer fb;
@@ -241,116 +313,6 @@ class Viewport extends JPanel implements MouseListener,
g2.dispose();
}

// Mouse-Motion callback function
private void mouseMotionCB(MouseEvent e) {
if (!viewOnly.getValue() &&
e.getX() >= 0 && e.getX() <= scaledWidth &&
e.getY() >= 0 && e.getY() <= scaledHeight)
cc.writePointerEvent(translateMouseEvent(e));
}
public void mouseDragged(MouseEvent e) { mouseMotionCB(e); }
public void mouseMoved(MouseEvent e) { mouseMotionCB(e); }

// Mouse callback function
private void mouseCB(MouseEvent e) {
if (!viewOnly.getValue())
if ((e.getID() == MouseEvent.MOUSE_RELEASED) ||
(e.getX() >= 0 && e.getX() <= scaledWidth &&
e.getY() >= 0 && e.getY() <= scaledHeight))
cc.writePointerEvent(translateMouseEvent(e));
}
public void mouseReleased(MouseEvent e) { mouseCB(e); }
public void mousePressed(MouseEvent e) { mouseCB(e); }
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {
if (embed.getValue())
requestFocus();
}
public void mouseExited(MouseEvent e) {}

// MouseWheel callback function
private void mouseWheelCB(MouseWheelEvent e) {
if (!viewOnly.getValue())
cc.writeWheelEvent(e);
}

public void mouseWheelMoved(MouseWheelEvent e) {
mouseWheelCB(e);
}

private static final Integer keyEventLock = 0;

// Handle the key-typed event.
public void keyTyped(KeyEvent e) { }

// Handle the key-released event.
public void keyReleased(KeyEvent e) {
synchronized(keyEventLock) {
cc.writeKeyEvent(e);
}
}

// Handle the key-pressed event.
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == MenuKey.getMenuKeyCode()) {
java.awt.Point pt = e.getComponent().getMousePosition();
if (pt != null) {
F8Menu menu = new F8Menu(cc);
menu.show(e.getComponent(), (int)pt.getX(), (int)pt.getY());
}
return;
}
int ctrlAltShiftMask = Event.SHIFT_MASK | Event.CTRL_MASK | Event.ALT_MASK;
if ((e.getModifiers() & ctrlAltShiftMask) == ctrlAltShiftMask) {
switch (e.getKeyCode()) {
case KeyEvent.VK_A:
VncViewer.showAbout(this);
return;
case KeyEvent.VK_F:
if (cc.desktop.fullscreen_active())
cc.desktop.fullscreen_on();
else
cc.desktop.fullscreen_off();
return;
case KeyEvent.VK_H:
cc.refresh();
return;
case KeyEvent.VK_I:
cc.showInfo();
return;
case KeyEvent.VK_O:
OptionsDialog.showDialog(this);
return;
case KeyEvent.VK_W:
VncViewer.newViewer();
return;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN:
return;
}
}
if ((e.getModifiers() & Event.META_MASK) == Event.META_MASK) {
switch (e.getKeyCode()) {
case KeyEvent.VK_COMMA:
case KeyEvent.VK_N:
case KeyEvent.VK_W:
case KeyEvent.VK_I:
case KeyEvent.VK_R:
case KeyEvent.VK_L:
case KeyEvent.VK_F:
case KeyEvent.VK_Z:
case KeyEvent.VK_T:
return;
}
}
synchronized(keyEventLock) {
cc.writeKeyEvent(e);
}
}

public void setScaledSize(int width, int height)
{
assert(width != 0 && height != 0);
@@ -384,21 +346,417 @@ class Viewport extends JPanel implements MouseListener,
setSize(new Dimension(scaledWidth, scaledHeight));
}

private MouseEvent translateMouseEvent(MouseEvent e)
private void handlePointerEvent(Point pos, int buttonMask)
{
if (cc.cp.width != scaledWidth ||
cc.cp.height != scaledHeight) {
int sx = (scaleRatioX == 1.00) ?
e.getX() : (int)Math.floor(e.getX() / scaleRatioX);
int sy = (scaleRatioY == 1.00) ?
e.getY() : (int)Math.floor(e.getY() / scaleRatioY);
e.translatePoint(sx - e.getX(), sy - e.getY());
if (!viewOnly.getValue()) {
if (buttonMask != lastButtonMask || !pos.equals(lastPointerPos)) {
try {
if (cc.cp.width != scaledWidth ||
cc.cp.height != scaledHeight) {
int sx = (scaleRatioX == 1.00) ?
pos.x : (int)Math.floor(pos.x / scaleRatioX);
int sy = (scaleRatioY == 1.00) ?
pos.y : (int)Math.floor(pos.y / scaleRatioY);
pos = pos.translate(new Point(sx - pos.x, sy - pos.y));
}
cc.writer().pointerEvent(pos, buttonMask);
} catch (Exception e) {
vlog.error("%s", e.getMessage());
cc.close();
}
}
lastPointerPos = pos;
lastButtonMask = buttonMask;
}
return e;
}

public void handleKeyPress(int keyCode, int keySym)
{
// Prevent recursion if the menu wants to send it's own
// activation key.
if ((menuKeySym != 0) && keySym == menuKeySym && !menuRecursion) {
popupContextMenu();
return;
}

if (viewOnly.getValue())
return;

if (keyCode == 0) {
vlog.error("No key code specified on key press");
return;
}

if (VncViewer.os.startsWith("mac os x")) {
// Alt on OS X behaves more like AltGr on other systems, and to get
// sane behaviour we should translate things in that manner for the
// remote VNC server. However that means we lose the ability to use
// Alt as a shortcut modifier. Do what RealVNC does and hijack the
// left command key as an Alt replacement.
switch (keySym) {
case XK_Meta_L:
keySym = XK_Alt_L;
break;
case XK_Meta_R:
keySym = XK_Super_L;
break;
case XK_Alt_L:
keySym = XK_Mode_switch;
break;
case XK_Alt_R:
keySym = XK_ISO_Level3_Shift;
break;
}
}

if (VncViewer.os.startsWith("windows")) {
// Ugly hack alert!
//
// Windows doesn't have a proper AltGr, but handles it using fake
// Ctrl+Alt. Unfortunately X11 doesn't generally like the combination
// Ctrl+Alt+AltGr, which we usually end up with when Xvnc tries to
// get everything in the correct state. Cheat and temporarily release
// Ctrl and Alt when we send some other symbol.
if (downKeySym.containsValue(XK_Control_L) &&
downKeySym.containsValue(XK_Alt_R)) {
vlog.debug("Faking release of AltGr (Ctrl_L+Alt_R)");
try {
cc.writer().keyEvent(XK_Control_L, false);
cc.writer().keyEvent(XK_Alt_R, false);
} catch (Exception e) {
vlog.error("%s", e.getMessage());
cc.close();
}
}
}

// Because of the way keyboards work, we cannot expect to have the same
// symbol on release as when pressed. This breaks the VNC protocol however,
// so we need to keep track of what keysym a key _code_ generated on press
// and send the same on release.
downKeySym.put(keyCode, keySym);

vlog.debug("Key pressed: 0x%04x => 0x%04x", keyCode, keySym);

try {
// Fake keycode?
if (keyCode > 0xffff)
cc.writer().keyEvent(keySym, true);
else
cc.writer().keyEvent(keySym, true);
} catch (Exception e) {
vlog.error("%s", e.getMessage());
cc.close();
}

if (VncViewer.os.startsWith("windows")) {
// Ugly hack continued...
if (downKeySym.containsValue(XK_Control_L) &&
downKeySym.containsValue(XK_Alt_R)) {
vlog.debug("Restoring AltGr state");
try {
cc.writer().keyEvent(XK_Control_L, true);
cc.writer().keyEvent(XK_Alt_R, true);
} catch (Exception e) {
vlog.error("%s", e.getMessage());
cc.close();
}
}
}
}

public void handleKeyRelease(int keyCode)
{
Integer iter;

if (viewOnly.getValue())
return;

iter = downKeySym.get(keyCode);
if (iter == null) {
// These occur somewhat frequently so let's not spam them unless
// logging is turned up.
vlog.debug("Unexpected release of key code %d", keyCode);
return;
}

vlog.debug("Key released: 0x%04x => 0x%04x", keyCode, iter);

try {
if (keyCode > 0xffff)
cc.writer().keyEvent(iter, false);
else
cc.writer().keyEvent(iter, false);
} catch (Exception e) {
vlog.error("%s", e.getMessage());
cc.close();
}

downKeySym.remove(keyCode);
}

private int handleSystemEvent(AWTEvent event)
{

if (event instanceof KeyEvent) {
KeyEvent ev = (KeyEvent)event;
if (ev.getKeyCode() == 0) {
// Not much we can do with this...
vlog.debug("Ignoring KeyEvent with unknown Java keycode");
return 0;
}

if (ev.getID() == KeyEvent.KEY_PRESSED) {
// Generate a fake keycode just for tracking if we can't figure
// out the proper one. Java virtual key codes aren't unique
// between left/right versions of keys, so we can't use them as
// indexes to the downKeySym map. There should not be any key
// codes > 0xFFFF so we can use the high nibble to make a unique
// pseudo-key code.
int keyCode = ev.getKeyCode() | ev.getKeyLocation()<<16;

// Pressing Ctrl wreaks havoc with the symbol lookup, so turn
// that off. But AltGr shows up as Ctrl_L+Alt_R in Windows, so
// construct a new KeyEvent that uses a proper AltGraph for the
// symbol lookup.
if (VncViewer.os.startsWith("windows")) {
if (downKeySym.containsValue(XK_Control_L) &&
downKeySym.containsValue(XK_Alt_R)) {
int mask = ev.getModifiers();
mask &= ~CTRL_MASK;
mask &= ~ALT_MASK;
mask |= ALT_GRAPH_MASK;
AWTKeyStroke ks =
AWTKeyStroke.getAWTKeyStroke(ev.getKeyCode(), mask);
ev = new KeyEvent((JComponent)ev.getSource(), ev.getID(),
ev.getWhen(), mask, ev.getKeyCode(),
ks.getKeyChar(), ev.getKeyLocation());
}
}

int keySym = KeyMap.vkey_to_keysym(ev);

if (keySym == KeyMap.NoSymbol)
vlog.error("No symbol for virtual key 0x%02x", keyCode);

if (VncViewer.os.startsWith("linux")) {
switch (keySym) {
// For the first few years, there wasn't a good consensus on what the
// Windows keys should be mapped to for X11. So we need to help out a
// bit and map all variants to the same key...
case XK_Hyper_L:
keySym = XK_Super_L;
break;
case XK_Hyper_R:
keySym = XK_Super_R;
break;
// There has been several variants for Shift-Tab over the years.
// RFB states that we should always send a normal tab.
case XK_ISO_Left_Tab:
keySym = XK_Tab;
break;
}
}

handleKeyPress(keyCode, keySym);

if (VncViewer.os.startsWith("mac os x")) {
// We don't get any release events for CapsLock, so we have to
// send the release right away.
if (keySym == XK_Caps_Lock)
handleKeyRelease(keyCode);
}

return 1;
} else if (ev.getID() == KeyEvent.KEY_RELEASED) {
int keyCode = keyCode = ev.getKeyCode() | ev.getKeyLocation()<<16;
handleKeyRelease(keyCode);
return 1;
}
}

return 0;
}

private void initContextMenu()
{
contextMenu.setLightWeightPopupEnabled(false);

contextMenu.removeAll();

menu_add(contextMenu, "Exit viewer", KeyEvent.VK_X,
this, ID.EXIT, EnumSet.of(MENU.DIVIDER));

menu_add(contextMenu, "Full screen", KeyEvent.VK_F, this, ID.FULLSCREEN,
window().fullscreen_active() ?
EnumSet.of(MENU.TOGGLE, MENU.VALUE) : EnumSet.of(MENU.TOGGLE));
menu_add(contextMenu, "Minimize", KeyEvent.VK_Z,
this, ID.MINIMIZE, EnumSet.noneOf(MENU.class));
menu_add(contextMenu, "Resize window to session", KeyEvent.VK_W,
this, ID.RESIZE,
window().fullscreen_active() ?
EnumSet.of(MENU.INACTIVE, MENU.DIVIDER) : EnumSet.of(MENU.DIVIDER));

menu_add(contextMenu, "Clipboard viewer...", KeyEvent.VK_UNDEFINED,
this, ID.CLIPBOARD, EnumSet.of(MENU.DIVIDER));

menu_add(contextMenu, "Ctrl", KeyEvent.VK_C,
this, ID.CTRL,
menuCtrlKey ? EnumSet.of(MENU.TOGGLE, MENU.VALUE) : EnumSet.of(MENU.TOGGLE));
menu_add(contextMenu, "Alt", KeyEvent.VK_A,
this, ID.ALT,
menuAltKey ? EnumSet.of(MENU.TOGGLE, MENU.VALUE) : EnumSet.of(MENU.TOGGLE));

menu_add(contextMenu, "Send Ctrl-Alt-Del", KeyEvent.VK_D,
this, ID.CTRLALTDEL, EnumSet.of(MENU.DIVIDER));

menu_add(contextMenu, "Refresh screen", KeyEvent.VK_R,
this, ID.REFRESH, EnumSet.of(MENU.DIVIDER));

menu_add(contextMenu, "New connection...", KeyEvent.VK_N,
this, ID.NEWVIEWER,
embed.getValue() ? EnumSet.of(MENU.INACTIVE, MENU.DIVIDER) : EnumSet.of(MENU.DIVIDER));

menu_add(contextMenu, "Options...", KeyEvent.VK_O,
this, ID.OPTIONS, EnumSet.noneOf(MENU.class));
menu_add(contextMenu, "Connection info...", KeyEvent.VK_I,
this, ID.INFO, EnumSet.noneOf(MENU.class));
menu_add(contextMenu, "About TigerVNC viewer...", KeyEvent.VK_T,
this, ID.ABOUT, EnumSet.of(MENU.DIVIDER));

menu_add(contextMenu, "Dismiss menu", KeyEvent.VK_M,
this, ID.DISMISS, EnumSet.noneOf(MENU.class));
}

static void menu_add(JPopupMenu menu, String text,
int shortcut, ActionListener cb,
ID data, EnumSet<MENU> flags)
{
JMenuItem item;
if (flags.contains(MENU.TOGGLE)) {
item = new JCheckBoxMenuItem(text, flags.contains(MENU.VALUE));
} else {
if (shortcut != 0)
item = new JMenuItem(text, shortcut);
else
item = new JMenuItem(text);
}
item.setActionCommand(data.toString());
item.addActionListener(cb);
item.setEnabled(!flags.contains(MENU.INACTIVE));
menu.add(item);
if (flags.contains(MENU.DIVIDER))
menu.addSeparator();
}

void popupContextMenu()
{
// initialize context menu before display
initContextMenu();

contextMenu.setCursor(java.awt.Cursor.getDefaultCursor());

contextMenu.show(this, lastPointerPos.x, lastPointerPos.y);
}

public void actionPerformed(ActionEvent ev)
{
switch(ID.valueOf(ev.getActionCommand())) {
case EXIT:
cc.close();
break;
case FULLSCREEN:
if (window().fullscreen_active())
window().fullscreen_off();
else
window().fullscreen_on();
break;
case MINIMIZE:
if (window().fullscreen_active())
window().fullscreen_off();
window().setExtendedState(JFrame.ICONIFIED);
break;
case RESIZE:
if (window().fullscreen_active())
break;
int dx = window().getInsets().left + window().getInsets().right;
int dy = window().getInsets().top + window().getInsets().bottom;
window().setSize(getWidth()+dx, getHeight()+dy);
break;
case CLIPBOARD:
ClipboardDialog.showDialog(window());
break;
case CTRL:
if (((JMenuItem)ev.getSource()).isSelected())
handleKeyPress(0x1d, XK_Control_L);
else
handleKeyRelease(0x1d);
menuCtrlKey = !menuCtrlKey;
break;
case ALT:
if (((JMenuItem)ev.getSource()).isSelected())
handleKeyPress(0x38, XK_Alt_L);
else
handleKeyRelease(0x38);
menuAltKey = !menuAltKey;
break;
case MENUKEY:
menuRecursion = true;
handleKeyPress(menuKeyCode, menuKeySym);
menuRecursion = false;
handleKeyRelease(menuKeyCode);
break;
case CTRLALTDEL:
handleKeyPress(0x1d, XK_Control_L);
handleKeyPress(0x38, XK_Alt_L);
handleKeyPress(0xd3, XK_Delete);

handleKeyRelease(0xd3);
handleKeyRelease(0x38);
handleKeyRelease(0x1d);
break;
case REFRESH:
cc.refreshFramebuffer();
break;
case NEWVIEWER:
VncViewer.newViewer();
break;
case OPTIONS:
OptionsDialog.showDialog(cc.desktop);
break;
case INFO:
Window fullScreenWindow =
DesktopWindow.getFullScreenWindow();
if (fullScreenWindow != null)
DesktopWindow.setFullScreenWindow(null);
JOptionPane op = new JOptionPane(cc.connectionInfo(),
JOptionPane.PLAIN_MESSAGE,
JOptionPane.DEFAULT_OPTION);
JDialog dlg = op.createDialog(window(), "VNC connection info");
dlg.setIconImage(VncViewer.frameIcon);
dlg.setAlwaysOnTop(true);
dlg.setVisible(true);
if (fullScreenWindow != null)
DesktopWindow.setFullScreenWindow(fullScreenWindow);
break;
case ABOUT:
VncViewer.about_vncviewer(cc.desktop);
break;
case DISMISS:
break;
}
}

private void setMenuKey()
{
menuKeyJava = MenuKey.getMenuKeyJavaCode();
menuKeyCode = MenuKey.getMenuKeyCode();
menuKeySym = MenuKey.getMenuKeySym();
}

public void handleOptions()
{
setMenuKey();
/*
setScaledSize(cc.cp.width, cc.cp.height);
if (!oldSize.equals(new Dimension(scaledWidth, scaledHeight))) {
@@ -414,18 +772,48 @@ class Viewport extends JPanel implements MouseListener,
*/
}

public void releaseDownKeys() {
while (!downKeySym.isEmpty())
handleKeyRelease(downKeySym.keySet().iterator().next());
}

private DesktopWindow window() {
return (DesktopWindow)getTopLevelAncestor();
}
private int x() { return getX(); }
private int y() { return getY(); }
private int w() { return getWidth(); }
private int h() { return getHeight(); }
// access to cc by different threads is specified in CConn
private CConn cc;

// access to the following must be synchronized:
public PlatformPixelBuffer frameBuffer;
private PlatformPixelBuffer frameBuffer;

Point lastPointerPos = new Point(0, 0);
int lastButtonMask = 0;

private class DownMap extends HashMap<Integer, Integer> {
public DownMap(int capacity) {
super(capacity);
}
}
DownMap downKeySym = new DownMap(256);

int menuKeySym;
int menuKeyCode, menuKeyJava;
JPopupMenu contextMenu;
boolean menuRecursion = false;

boolean menuCtrlKey = false;
boolean menuAltKey = false;

static Toolkit tk = Toolkit.getDefaultToolkit();

public int scaledWidth = 0, scaledHeight = 0;
float scaleRatioX, scaleRatioY;

BufferedImage cursor;
static BufferedImage cursor;
Point cursorHotspot = new Point();

}

+ 2
- 2
java/com/tigervnc/vncviewer/VncViewer.java 查看文件

@@ -394,7 +394,7 @@ public class VncViewer extends javax.swing.JApplet
}
public void windowDeactivated(WindowEvent e) {
if (cc != null)
cc.releaseDownKeys();
cc.desktop.viewport.releaseDownKeys();
}
});
}
@@ -412,7 +412,7 @@ public class VncViewer extends javax.swing.JApplet
}
}

public static void showAbout(Container parent) {
public static void about_vncviewer(Container parent) {
String pkgDate = "";
String pkgTime = "";
try {

正在加载...
取消
保存