Merge branch 'feature/KASM-2335_IME_support' into 'master'

Resolve KASM-2335 "Feature/ ime support"

Closes KASM-2335

See merge request kasm-technologies/internal/KasmVNC!30
This commit is contained in:
Matthew McClaskey 2022-03-25 16:18:17 +00:00
commit 08f5000b9f
9 changed files with 107 additions and 15 deletions

1
.gitignore vendored
View File

@ -21,3 +21,4 @@ debian/files
debian/kasmvncserver.substvars debian/kasmvncserver.substvars
debian/kasmvncserver/ debian/kasmvncserver/
.pc .pc
.vscode/

2
.gitmodules vendored
View File

@ -1,4 +1,4 @@
[submodule "kasmweb"] [submodule "kasmweb"]
path = kasmweb path = kasmweb
url = https://github.com/kasmtech/noVNC.git url = https://github.com/kasmtech/noVNC.git
branch = master branch = feature/KASM-2335_IME_support_2

View File

@ -226,4 +226,4 @@ static void bandwidthPreset() {
rfb::PresetParameter rfb::Server::preferBandwidth rfb::PresetParameter rfb::Server::preferBandwidth
("PreferBandwidth", ("PreferBandwidth",
"Set various options for lower bandwidth use. The default is off, aka to prefer quality.", "Set various options for lower bandwidth use. The default is off, aka to prefer quality.",
false, bandwidthPreset); false, bandwidthPreset);

View File

@ -77,7 +77,6 @@ namespace rfb {
static BoolParameter ignoreClientSettingsKasm; static BoolParameter ignoreClientSettingsKasm;
static BoolParameter selfBench; static BoolParameter selfBench;
static PresetParameter preferBandwidth; static PresetParameter preferBandwidth;
}; };
}; };

@ -1 +1 @@
Subproject commit df9c9d0d96acef17423c237fea9497850a6c1c17 Subproject commit e2cc682243c55786e719436f214da0f0948f605a

View File

@ -39,6 +39,7 @@
#include "xkbsrv.h" #include "xkbsrv.h"
#include "xkbstr.h" #include "xkbstr.h"
#include "xserver-properties.h" #include "xserver-properties.h"
#include "stdbool.h"
extern _X_EXPORT DevPrivateKey CoreDevicePrivateKey; extern _X_EXPORT DevPrivateKey CoreDevicePrivateKey;
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
@ -66,6 +67,8 @@ static const unsigned short *codeMap;
static unsigned int codeMapLen; static unsigned int codeMapLen;
static KeySym pressedKeys[256]; static KeySym pressedKeys[256];
static unsigned int needFree[256];
static bool freeKeys;
static int vncPointerProc(DeviceIntPtr pDevice, int onoff); static int vncPointerProc(DeviceIntPtr pDevice, int onoff);
static void vncKeyboardBell(int percent, DeviceIntPtr device, static void vncKeyboardBell(int percent, DeviceIntPtr device,
@ -91,7 +94,7 @@ static void vncKeysymKeyboardEvent(KeySym keysym, int down);
* Instead we call it from XserverDesktop at an appropriate * Instead we call it from XserverDesktop at an appropriate
* time. * time.
*/ */
void vncInitInputDevice(void) void vncInitInputDevice(bool freeKeyMappings)
{ {
int i, ret; int i, ret;
@ -110,6 +113,8 @@ void vncInitInputDevice(void)
codeMap = code_map_qnum_to_xorgkbd; codeMap = code_map_qnum_to_xorgkbd;
codeMapLen = code_map_qnum_to_xorgkbd_len; codeMapLen = code_map_qnum_to_xorgkbd_len;
#endif #endif
freeKeys = freeKeyMappings;
memset(needFree, 0, sizeof(needFree));
for (i = 0;i < 256;i++) for (i = 0;i < 256;i++)
pressedKeys[i] = NoSymbol; pressedKeys[i] = NoSymbol;
@ -500,6 +505,7 @@ static void vncKeysymKeyboardEvent(KeySym keysym, int down)
pressedKeys[i] = NoSymbol; pressedKeys[i] = NoSymbol;
pressKey(vncKeyboardDev, i, FALSE, "keycode"); pressKey(vncKeyboardDev, i, FALSE, "keycode");
mieqProcessInputEvents(); mieqProcessInputEvents();
return; return;
} }
} }
@ -544,7 +550,7 @@ static void vncKeysymKeyboardEvent(KeySym keysym, int down)
/* No matches. Will have to add a new entry... */ /* No matches. Will have to add a new entry... */
if (keycode == 0) { if (keycode == 0) {
keycode = vncAddKeysym(keysym, state); keycode = vncAddKeysym(keysym, state, needFree, freeKeys);
if (keycode == 0) { if (keycode == 0) {
LOG_ERROR("Failure adding new keysym 0x%x", keysym); LOG_ERROR("Failure adding new keysym 0x%x", keysym);
return; return;

View File

@ -25,12 +25,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <X11/X.h> #include <X11/X.h>
#include "stdbool.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
void vncInitInputDevice(void); void vncInitInputDevice(bool freeKeyMappings);
void vncPointerButtonAction(int buttonMask, const unsigned char skipclick, void vncPointerButtonAction(int buttonMask, const unsigned char skipclick,
const unsigned char skiprelease); const unsigned char skiprelease);
@ -57,7 +58,8 @@ KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state);
int vncIsAffectedByNumLock(KeyCode keycode); int vncIsAffectedByNumLock(KeyCode keycode);
KeyCode vncAddKeysym(KeySym keysym, unsigned state); KeyCode vncAddKeysym(KeySym keysym, unsigned state, unsigned int *needfree, bool freeKeys);
void vncRemoveKeycode(unsigned keycode);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -25,16 +25,19 @@
#include "xorg-version.h" #include "xorg-version.h"
#include <stdio.h> #include <stdio.h>
#include <limits.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include "RFBGlue.h"
#include "xkbsrv.h" #include "xkbsrv.h"
#include "xkbstr.h" #include "xkbstr.h"
#include "eventstr.h" #include "eventstr.h"
#include "scrnintstr.h" #include "scrnintstr.h"
#include "mi.h" #include "mi.h"
#include "stdbool.h"
#include "Input.h" #include "Input.h"
@ -50,7 +53,15 @@
#endif #endif
#endif #endif
#define LOG_NAME "Input"
#define LOG_ERROR(...) vncLogError(LOG_NAME, __VA_ARGS__)
#define LOG_STATUS(...) vncLogStatus(LOG_NAME, __VA_ARGS__)
#define LOG_INFO(...) vncLogInfo(LOG_NAME, __VA_ARGS__)
#define LOG_DEBUG(...) vncLogDebug(LOG_NAME, __VA_ARGS__)
extern DeviceIntPtr vncKeyboardDev; extern DeviceIntPtr vncKeyboardDev;
static unsigned int MAX_MAPPINGS = UINT_MAX - 1;
static void vncXkbProcessDeviceEvent(int screenNum, static void vncXkbProcessDeviceEvent(int screenNum,
InternalEvent *event, InternalEvent *event,
@ -536,12 +547,16 @@ int vncIsAffectedByNumLock(KeyCode keycode)
return 1; return 1;
} }
KeyCode vncAddKeysym(KeySym keysym, unsigned state) KeyCode vncAddKeysym(KeySym keysym, unsigned state, unsigned int *needFree, bool freeKeys)
{ {
DeviceIntPtr master; DeviceIntPtr master;
XkbDescPtr xkb; XkbDescPtr xkb;
unsigned int key; unsigned int key = 0;
unsigned int i;
unsigned int newest_k = 0;
unsigned int oldest_k = 0;
unsigned int freeCnt = 0;
XkbEventCauseRec cause; XkbEventCauseRec cause;
XkbChangesRec changes; XkbChangesRec changes;
@ -551,14 +566,45 @@ KeyCode vncAddKeysym(KeySym keysym, unsigned state)
master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT); master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
xkb = master->key->xkbInfo->desc; xkb = master->key->xkbInfo->desc;
for (key = xkb->max_key_code; key >= xkb->min_key_code; key--) {
if (XkbKeyNumGroups(xkb, key) == 0) //find the first free key to map
break; for (i = xkb->max_key_code; i >= xkb->min_key_code; i--) {
if (XkbKeyNumGroups(xkb, i) == 0 && *(needFree + i) != UINT_MAX) {
if (++freeCnt == 1)
key = i;
}
} }
if (key < xkb->min_key_code) if (key < xkb->min_key_code)
return 0; return 0;
//find the oldest and newest keys that have been remapped
for (i = 1;i < 256;i++) {
//protect from uint rollover
if (*(needFree + i) == MAX_MAPPINGS)
*(needFree + i) = 1;
if (*(needFree + i) > *(needFree + newest_k) && *(needFree + i) != UINT_MAX)
newest_k = i;
if (*(needFree + i) < *(needFree + oldest_k) && *(needFree +i) > 0)
oldest_k = i;
//mark as free to use
if (*(needFree + i) == UINT_MAX)
*(needFree + i) = 0;
}
*(needFree + key) = ++(*(needFree + newest_k));
//if running low on free keys, free the oldest key that was used
if (freeCnt < 3 && *(needFree + oldest_k) > 0 && freeKeys) {
vncRemoveKeycode(oldest_k);
LOG_DEBUG("Removed mapping for keycode %d", oldest_k);
//mark as free to use after one more cycle
*(needFree + oldest_k) = UINT_MAX;
} else if (freeCnt < 3 && freeKeys) {
//this should only happen if the system had fewer than 3 free keys to begin with
LOG_INFO("The system has fewer than 3 unmapped keys with no available keys to free.");
return 0;
}
memset(&changes, 0, sizeof(changes)); memset(&changes, 0, sizeof(changes));
memset(&cause, 0, sizeof(cause)); memset(&cause, 0, sizeof(cause));
@ -612,6 +658,40 @@ KeyCode vncAddKeysym(KeySym keysym, unsigned state)
return key; return key;
} }
void vncRemoveKeycode(unsigned keycode)
{
DeviceIntPtr master;
XkbDescPtr xkb;
XkbEventCauseRec cause;
XkbChangesRec changes;
master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
xkb = master->key->xkbInfo->desc;
memset(&changes, 0, sizeof(changes));
memset(&cause, 0, sizeof(cause));
XkbSetCauseUnknown(&cause);
KeySym ks = NoSymbol;
KeySymsRec keysyms;
keysyms.minKeyCode = keycode;
keysyms.maxKeyCode = keycode;
keysyms.mapWidth = 1;
keysyms.map = &ks;
unsigned check = 0;
XkbUpdateKeyTypesFromCore(master, &keysyms, keycode, 1, &changes);
XkbUpdateActions(master, keycode, 1, &changes, &check, &cause);
if (check)
XkbCheckSecondaryEffects(master->key->xkbInfo, 1, &changes, &cause);
XkbSendNotification(master, &changes, &cause);
}
static void vncXkbProcessDeviceEvent(int screenNum, static void vncXkbProcessDeviceEvent(int screenNum,
InternalEvent *event, InternalEvent *event,
DeviceIntPtr dev) DeviceIntPtr dev)

View File

@ -60,6 +60,10 @@ BoolParameter rawKeyboard("RawKeyboard",
"Send keyboard events straight through and " "Send keyboard events straight through and "
"avoid mapping them to the current keyboard " "avoid mapping them to the current keyboard "
"layout", false); "layout", false);
BoolParameter freeKeyMappings("FreeKeyMappings",
"Automatically free added keyboard mappings "
"when there are not enough unused keys to "
"map symbols to.", false);
IntParameter queryConnectTimeout("QueryConnectTimeout", IntParameter queryConnectTimeout("QueryConnectTimeout",
"Number of seconds to show the " "Number of seconds to show the "
"Accept Connection dialog before " "Accept Connection dialog before "
@ -370,7 +374,7 @@ void XserverDesktop::blockHandler(int* timeout)
// so we abuse the fact that this routine will be called first thing // so we abuse the fact that this routine will be called first thing
// once the dix is done initialising. // once the dix is done initialising.
// [1] Technically Xvnc has InitInput(), but libvnc.so has nothing. // [1] Technically Xvnc has InitInput(), but libvnc.so has nothing.
vncInitInputDevice(); vncInitInputDevice(freeKeyMappings);
try { try {
std::list<Socket*> sockets; std::list<Socket*> sockets;