Synergy 907 client tray language switcher reverts to en (#7038)
* SYNERGY-907 Client tray language switcher reverts to EN *Fix checkbox spelling * SYNERGY-907 Client tray language switcher reverts to EN *Update changelog * SYNERGY-907 Client tray language switcher reverts to EN *Temporary disable language sync logic * SYNERGY-907 client tray language switcher reverts to en *Update changelog * Synergy 907 client tray language switcher reverts to en *Fix sonar * Synergy 907 client tray language switcher reverts to en *Fix sonar * Synergy 907 client tray language switcher reverts to en *Fix chnagelog * SYNERGY-907 Client tray language switcher reverts to EN *Add prototype of keyboard layout names transfer(Windows part) * SYNERGY-907 Client tray language switcher reverts to EN *Add keyboard layout determination(Mac part) * SYNERGY-907 Client tray language switcher reverts to EN *Add language check on client side * SYNERGY-907 Client tray language switcher reverts to EN *Fix Windows build * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Add logs * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Add protocol update * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Add server side of language sync * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Remove unused variables * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *AFix mac language determination * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Clean up logs * SYNERGY-907 Client tray language switcher reverts to EN *Replace logs with messagebox on Windows * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Add mac messagebox, revert protocol changes * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Fix minor language bugs * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Update chagelog * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Minor fixes * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Fix Linux build * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Fix sonar code smells * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Fix Linux build * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Add Linux language determination * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Fix sonar codesmells * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Fix sonar codesmells * SYNERGY-907-Client-tray-language-switcher-reverts-to-EN *Replace custom xml parser with tinyxml2 * SYNERGY-907 Client tray language switcher reverts to EN *Fix PR comments * SYNERGY-907 Client tray language switcher reverts to EN *Update changelog * SYNERGY-907 Client tray language switcher reverts to EN *Fix unix build * SYNERGY-907 Client tray language switcher reverts to EN *Fix unix build * SYNERGY-907 Client tray language switcher reverts to EN *Fix sonar code smells * SYNERGY-907 Client tray language switcher reverts to EN *Update changelog * SYNERGY-907 Client tray language switcher reverts to EN *Replace tinuxml with pugixml * SYNERGY-907 Client tray language switcher reverts to EN *Add pugixml as a git submodule * SYNERGY-907 Client tray language switcher reverts to EN *Add missed files * SYNERGY-907 Client tray language switcher reverts to EN *Fix sonar code smells * SYNERGY-907 Client tray language switcher reverts to EN *Fix comments from PR * SYNERGY-907 Client tray language switcher reverts to EN *Fix sonar code smell * SYNERGY-907 Client tray language switcher reverts to EN *add tests for x11 files parser * SYNERGY-907 Client tray language switcher reverts to EN *Fix include for unix build * SYNERGY-907 Client tray language switcher reverts to EN *Add test coverage for new logic * SYNERGY-907 Client tray language switcher reverts to EN *Add test coverage for new logic * SYNERGY-907 Client tray language switcher reverts to EN *Add code coverage *Fix code smells * SYNERGY-907 Client tray language switcher reverts to EN *Return missed file * SYNERGY-907 Client tray language switcher reverts to EN *Improve code coverage of x11Layouts parser * SYNERGY-907 Client tray language switcher reverts to EN *Add client args code coverage * SYNERGY-907 Client tray language switcher reverts to EN *Return runtime language sync * SYNERGY-907 Client tray language switcher reverts to EN *Return windows current keyboard layout determination * SYNERGY-907 Client tray language switcher reverts to EN *Return Linux language determination * SYNERGY-907 Client tray language switcher reverts to EN *Remove debug code * SYNERGY-907 Client tray language switcher reverts to EN *Move back Carbon current language determination * SYNERGY-907 Client tray language switcher reverts to EN *Fix language change for existed languages on iOS and Linux * SYNERGY-907 Client tray language switcher reverts to EN *Return protocol missed parameter * SYNERGY-907 Client tray language switcher reverts to EN *Fix macOS language change * SYNERGY-907 Client tray language switcher reverts to EN *Fix language sync (when it is disabled) * SYNERGY-907 Client tray language switcher reverts to EN *Add correct language choosing *Fix code coverage tests * SYNERGY-907 Client tray language switcher reverts to EN *Fix windows build and tests * SYNERGY-907 Client tray language switcher reverts to EN *Fix macOS build and tests * SYNERGY-907 Client tray language switcher reverts to EN *Fix language change for first letter in keystroke * SYNERGY-907 Client tray language switcher reverts to EN *Fix macOS build * SYNERGY-907 Client tray language switcher reverts to EN *Partially fix Sonar codesmells * SYNERGY-907 Client tray language switcher reverts to EN *Partially fix Sonar codesmells * SYNERGY-907 Client tray language switcher reverts to EN *Fix build * SYNERGY-907 Client tray language switcher reverts to EN *Added logging for language change process *Fixed language change process for Windows * SYNERGY-907 Client tray language switcher reverts to EN *Fix double layout change in key repeat event * SYNERGY-907 Client tray language switcher reverts to EN *Fix determination of current language on windows *Removed legacy code of restoring language when leaving screen *Fix double group change on Linux * SYNERGY-907 Client tray language switcher reverts to EN *Temp fix for language change on Linux * SYNERGY-907 Client tray language switcher reverts to EN *Add XSync to normalize Linux language polling and setting *Remove setxkbmap using * SYNERGY-907 Client tray language switcher reverts to EN *Add keyboard layout changing on Linux with DBus * SYNERGY-907 Client tray language switcher reverts to EN *Add comments to new language change function *Logical fix * SYNERGY-907 Client tray language switcher reverts to EN *Remove input attaching * SYNERGY-907 Client tray language switcher reverts to EN *Fix language change on macOS * SYNERGY-907 Fix review issues * SYNERGY-907 Fix review comment for Windows * SYNERGY-907 Removed debug trace Co-authored-by: Andrii Batyiev <andrii-external@symless.com> Co-authored-by: user <user@users-Mac-mini.local> Co-authored-by: Serhii Hadzhilov <serhii-external@symless.com>
This commit is contained in:
parent
64139e092b
commit
e9e21aad8f
74 changed files with 1586 additions and 257 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -4,3 +4,6 @@
|
|||
[submodule "ext/WinToast"]
|
||||
path = ext/WinToast
|
||||
url = https://github.com/mohabouje/WinToast
|
||||
[submodule "ext/pugixml"]
|
||||
path = ext/pugixml
|
||||
url = https://github.com/zeux/pugixml
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ v1.14.2-snapshot
|
|||
===========
|
||||
Bug fixes:
|
||||
- #7070 Fix radio button shifted focus on macOS
|
||||
- #7038 Fix client and server different keyboard layout errors
|
||||
- #7078 Fix clipboard re-enables automatically
|
||||
- #7077 Fix Ubuntu, CentOS and Debian build after SYNERGY-1161
|
||||
- #7080 Add trace if the system can't open file with trusted fingerprints
|
||||
|
|
|
|||
1
ext/pugixml
Submodule
1
ext/pugixml
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 9e382f98076e57581fcc61323728443374889646
|
||||
|
|
@ -77,7 +77,8 @@ const char* AppConfig::m_SynergySettingsName[] = {
|
|||
"serverHostname",
|
||||
"tlsCertPath",
|
||||
"tlsKeyLength",
|
||||
"preventSleep"
|
||||
"preventSleep",
|
||||
"languageSync"
|
||||
};
|
||||
|
||||
static const char* logLevelNames[] =
|
||||
|
|
@ -241,6 +242,7 @@ void AppConfig::loadSettings()
|
|||
m_ClientGroupChecked = loadSetting(kGroupClientCheck, false).toBool();
|
||||
m_ServerHostname = loadSetting(kServerHostname).toString();
|
||||
m_PreventSleep = loadSetting(kPreventSleep, false).toBool();
|
||||
m_LanguageSync = loadSetting(kLanguageSync, false).toBool();
|
||||
|
||||
//only change the serial key if the settings being loaded contains a key
|
||||
bool updateSerial = ConfigWriter::make()
|
||||
|
|
@ -302,6 +304,7 @@ void AppConfig::saveSettings()
|
|||
setSetting(kGroupClientCheck, m_ClientGroupChecked);
|
||||
setSetting(kServerHostname, m_ServerHostname);
|
||||
setSetting(kPreventSleep, m_PreventSleep);
|
||||
setSetting(kLanguageSync, m_LanguageSync);
|
||||
}
|
||||
|
||||
m_unsavedChanges = false;
|
||||
|
|
|
|||
|
|
@ -204,7 +204,8 @@ protected:
|
|||
kServerHostname,
|
||||
kTLSCertPath,
|
||||
kTLSKeyLength,
|
||||
kPreventSleep
|
||||
kPreventSleep,
|
||||
kLanguageSync
|
||||
};
|
||||
|
||||
void setScreenName(const QString& s);
|
||||
|
|
|
|||
|
|
@ -807,6 +807,10 @@ bool MainWindow::clientArgs(QStringList& args, QString& app)
|
|||
args << "--log" << appConfig().logFilenameCmd();
|
||||
}
|
||||
|
||||
if (appConfig().getLanguageSync()) {
|
||||
args << "--sync-language";
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
|
||||
// check auto config first, if it is disabled or no server detected,
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
|
|||
buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
|
||||
enableControls(appConfig().isWritable());
|
||||
|
||||
//temporary disable language sync logic
|
||||
m_pCheckBoxLanguageSync->setVisible(false);
|
||||
m_pCheckBoxLanguageSync->setVisible(m_pMainWindow->synergyType() == MainWindow::synergyClient);
|
||||
|
||||
const auto& serveConfig = m_pMainWindow->serverConfig();
|
||||
m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator(m_pLineEditScreenName, m_pLabelNameError, (&serveConfig.screens())));
|
||||
|
|
@ -69,6 +68,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
|
|||
connect(m_pSpinBoxPort, SIGNAL(valueChanged(int)), this, SLOT(onChange()));
|
||||
connect(m_pLineEditScreenName, SIGNAL(textEdited(QString)), this, SLOT(onChange()));
|
||||
connect(m_pComboElevate, SIGNAL(currentIndexChanged(int)), this, SLOT(onChange()));
|
||||
connect(m_pCheckBoxLanguageSync, SIGNAL(clicked()), this, SLOT(onChange()));
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
|
|
@ -91,6 +91,7 @@ void SettingsDialog::accept()
|
|||
appConfig().setTLSCertPath(m_pLineEditCertificatePath->text());
|
||||
appConfig().setTLSKeyLength(m_pComboBoxKeyLength->currentText());
|
||||
appConfig().setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked());
|
||||
appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
|
||||
|
||||
appConfig().saveSettings();
|
||||
QDialog::accept();
|
||||
|
|
@ -148,6 +149,7 @@ void SettingsDialog::loadFromConfig() {
|
|||
m_pCheckBoxMinimizeToTray->setChecked(appConfig().getMinimizeToTray());
|
||||
m_pLineEditCertificatePath->setText(appConfig().getTLSCertPath());
|
||||
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled());
|
||||
m_pCheckBoxLanguageSync->setChecked(m_appConfig.getLanguageSync());
|
||||
|
||||
//If the tls file exists test its key length
|
||||
if (QFile(appConfig().getTLSCertPath()).exists()) {
|
||||
|
|
@ -338,7 +340,8 @@ bool SettingsDialog::isModified()
|
|||
|| appConfig().getTLSCertPath() != m_pLineEditCertificatePath->text()
|
||||
|| appConfig().getTLSKeyLength() != m_pComboBoxKeyLength->currentText()
|
||||
|| appConfig().getCryptoEnabled() != m_pCheckBoxEnableCrypto->isChecked()
|
||||
|| appConfig().isSystemScoped() != m_isSystemAtStart)
|
||||
|| appConfig().isSystemScoped() != m_isSystemAtStart
|
||||
|| appConfig().getLanguageSync() != m_pCheckBoxLanguageSync->isChecked())
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -359,6 +362,7 @@ void SettingsDialog::enableControls(bool enable) {
|
|||
m_pPushButtonBrowseCert->setEnabled(enable);
|
||||
m_pCheckBoxEnableCrypto->setEnabled(enable);
|
||||
m_labelAdminRightsMessage->setVisible(!enable);
|
||||
m_pCheckBoxLanguageSync->setEnabled(enable);
|
||||
|
||||
if (enable) {
|
||||
m_pLabelLogPath->setEnabled(m_pCheckBoxLogToFile->isChecked());
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "client/Client.h"
|
||||
|
||||
#include "client/ServerProxy.h"
|
||||
#include "synergy/AppUtil.h"
|
||||
#include "synergy/Screen.h"
|
||||
#include "synergy/FileChunk.h"
|
||||
#include "synergy/DropHelper.h"
|
||||
|
|
@ -46,6 +47,7 @@
|
|||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <iterator>
|
||||
|
||||
//
|
||||
// Client
|
||||
|
|
@ -304,16 +306,16 @@ Client::setClipboardDirty(ClipboardID, bool)
|
|||
}
|
||||
|
||||
void
|
||||
Client::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
|
||||
Client::keyDown(KeyID id, KeyModifierMask mask, KeyButton button, const String &lang)
|
||||
{
|
||||
m_screen->keyDown(id, mask, button);
|
||||
m_screen->keyDown(id, mask, button, lang);
|
||||
}
|
||||
|
||||
void
|
||||
Client::keyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button)
|
||||
SInt32 count, KeyButton button, const String& lang)
|
||||
{
|
||||
m_screen->keyRepeat(id, mask, count, button);
|
||||
m_screen->keyRepeat(id, mask, count, button, lang);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -721,7 +723,8 @@ void
|
|||
Client::handleHello(const Event&, void*)
|
||||
{
|
||||
SInt16 major, minor;
|
||||
if (!ProtocolUtil::readf(m_stream, kMsgHello, &major, &minor)) {
|
||||
String keyboardLayoutList("<unknown>");
|
||||
if (!ProtocolUtil::readf(m_stream, kMsgHello, &major, &minor, &keyboardLayoutList)) {
|
||||
sendConnectionFailedEvent("Protocol error from server, check encryption settings");
|
||||
cleanupTimer();
|
||||
cleanupConnection();
|
||||
|
|
@ -746,10 +749,37 @@ Client::handleHello(const Event&, void*)
|
|||
}
|
||||
|
||||
// say hello back
|
||||
LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion));
|
||||
String allKeyboardLayoutsStr;
|
||||
for (const auto& layout : AppUtil::instance().getKeyboardLayoutList()) {
|
||||
allKeyboardLayoutsStr += layout;
|
||||
}
|
||||
|
||||
LOG((CLOG_DEBUG1 "say hello version %d.%d", helloBackMajor, helloBackMinor));
|
||||
ProtocolUtil::writef(m_stream, kMsgHelloBack,
|
||||
helloBackMajor,
|
||||
helloBackMinor, & m_name);
|
||||
helloBackMinor, & m_name, &allKeyboardLayoutsStr);
|
||||
|
||||
if(m_args.m_enableLangSync) {
|
||||
std::vector<String> missed;
|
||||
std::vector<String> supported;
|
||||
AppUtil::getKeyboardLayoutsDiff(keyboardLayoutList,
|
||||
AppUtil::instance().getKeyboardLayoutList(),
|
||||
missed, supported);
|
||||
|
||||
if(!supported.empty()) {
|
||||
LOG((CLOG_DEBUG "Supported server languages: %s", AppUtil::joinStrVector(supported, ", ").c_str()));
|
||||
}
|
||||
|
||||
if(!missed.empty()) {
|
||||
auto result = AppUtil::joinStrVector(missed, ", ");
|
||||
AppUtil::instance().showNotification("Language synchronization error",
|
||||
"These languages are required for client proper work: " + result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_DEBUG "Language sync logic is disabled."));
|
||||
}
|
||||
|
||||
// now connected but waiting to complete handshake
|
||||
setupScreen();
|
||||
|
|
|
|||
|
|
@ -162,9 +162,9 @@ public:
|
|||
virtual void setClipboard(ClipboardID, const IClipboard*);
|
||||
virtual void grabClipboard(ClipboardID);
|
||||
virtual void setClipboardDirty(ClipboardID, bool);
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton, const String&);
|
||||
virtual void keyRepeat(KeyID, KeyModifierMask,
|
||||
SInt32 count, KeyButton);
|
||||
SInt32 count, KeyButton, const String& lang);
|
||||
virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
|
||||
virtual void mouseDown(ButtonID);
|
||||
virtual void mouseUp(ButtonID);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "base/XBase.h"
|
||||
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
//
|
||||
// ServerProxy
|
||||
|
|
@ -528,11 +529,13 @@ ServerProxy::enter()
|
|||
LOG((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask));
|
||||
|
||||
// discard old compressed mouse motion, if any
|
||||
m_compressMouse = false;
|
||||
m_compressMouseRelative = false;
|
||||
m_dxMouse = 0;
|
||||
m_dyMouse = 0;
|
||||
m_seqNum = seqNum;
|
||||
m_compressMouse = false;
|
||||
m_compressMouseRelative = false;
|
||||
m_dxMouse = 0;
|
||||
m_dyMouse = 0;
|
||||
m_seqNum = seqNum;
|
||||
m_serverLanguage = "";
|
||||
m_isUserNotifiedAboutLanguageSyncError = false;
|
||||
|
||||
// forward
|
||||
m_client->enter(x, y, seqNum, static_cast<KeyModifierMask>(mask), false);
|
||||
|
|
@ -603,8 +606,25 @@ ServerProxy::keyDown()
|
|||
|
||||
// parse
|
||||
UInt16 id, mask, button;
|
||||
ProtocolUtil::readf(m_stream, kMsgDKeyDown + 4, &id, &mask, &button);
|
||||
LOG((CLOG_DEBUG1 "recv key down id=0x%08x, mask=0x%04x, button=0x%04x", id, mask, button));
|
||||
String lang;
|
||||
ProtocolUtil::readf(m_stream, kMsgDKeyDown + 4, &id, &mask, &button, &lang);
|
||||
LOG((CLOG_DEBUG1 "recv key down id=0x%08x, mask=0x%04x, button=0x%04x, lang=\"%s\"", id, mask, button, lang.c_str()));
|
||||
|
||||
if(m_serverLanguage != lang) {
|
||||
m_isUserNotifiedAboutLanguageSyncError = false;
|
||||
m_serverLanguage = lang;
|
||||
}
|
||||
|
||||
auto localList = AppUtil::instance().getKeyboardLayoutList();
|
||||
if(std::find(localList.begin(), localList.end(), lang) == localList.end()) {
|
||||
if(!m_isUserNotifiedAboutLanguageSyncError) {
|
||||
AppUtil::instance().showNotification("Language error", "Current server language is not installed on client!");
|
||||
m_isUserNotifiedAboutLanguageSyncError = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_isUserNotifiedAboutLanguageSyncError = false;
|
||||
}
|
||||
|
||||
// translate
|
||||
KeyID id2 = translateKey(static_cast<KeyID>(id));
|
||||
|
|
@ -615,7 +635,7 @@ ServerProxy::keyDown()
|
|||
LOG((CLOG_DEBUG1 "key down translated to id=0x%08x, mask=0x%04x", id2, mask2));
|
||||
|
||||
// forward
|
||||
m_client->keyDown(id2, mask2, button);
|
||||
m_client->keyDown(id2, mask2, button, lang);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -626,9 +646,10 @@ ServerProxy::keyRepeat()
|
|||
|
||||
// parse
|
||||
UInt16 id, mask, count, button;
|
||||
String lang;
|
||||
ProtocolUtil::readf(m_stream, kMsgDKeyRepeat + 4,
|
||||
&id, &mask, &count, &button);
|
||||
LOG((CLOG_DEBUG1 "recv key repeat id=0x%08x, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button));
|
||||
&id, &mask, &count, &button, &lang);
|
||||
LOG((CLOG_DEBUG1 "recv key repeat id=0x%08x, mask=0x%04x, count=%d, button=0x%04x, lang=\"%s\"", id, mask, count, button, lang.c_str()));
|
||||
|
||||
// translate
|
||||
KeyID id2 = translateKey(static_cast<KeyID>(id));
|
||||
|
|
@ -639,7 +660,7 @@ ServerProxy::keyRepeat()
|
|||
LOG((CLOG_DEBUG1 "key repeat translated to id=0x%08x, mask=0x%04x", id2, mask2));
|
||||
|
||||
// forward
|
||||
m_client->keyRepeat(id2, mask2, count, button);
|
||||
m_client->keyRepeat(id2, mask2, count, button, lang);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -136,4 +136,6 @@ private:
|
|||
|
||||
MessageParser m_parser;
|
||||
IEventQueue* m_events;
|
||||
String m_serverLanguage = "";
|
||||
bool m_isUserNotifiedAboutLanguageSyncError = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "base/TMethodEventJob.h"
|
||||
#include "base/TMethodJob.h"
|
||||
#include "base/IEventQueue.h"
|
||||
#include "synergy/win32/AppUtilWindows.h"
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
|
|
@ -145,7 +146,7 @@ MSWindowsDesks::MSWindowsDesks(
|
|||
|
||||
m_cursor = createBlankCursor();
|
||||
m_deskClass = createDeskWindowClass(m_isPrimary);
|
||||
m_keyLayout = GetKeyboardLayout(GetCurrentThreadId());
|
||||
m_keyLayout = AppUtilWindows::instance().getCurrentKeyboardLayout();
|
||||
resetOptions();
|
||||
}
|
||||
|
||||
|
|
@ -583,6 +584,9 @@ MSWindowsDesks::deskLeave(Desk* desk, HKL keyLayout)
|
|||
SetWindowPos(desk->m_window, HWND_TOP, x, y, w, h,
|
||||
SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
|
||||
// switch to requested keyboard layout
|
||||
ActivateKeyboardLayout(keyLayout, 0);
|
||||
|
||||
// if not using low-level hooks we have to also activate the
|
||||
// window to ensure we don't lose keyboard focus.
|
||||
// FIXME -- see if this can be avoided. if so then always
|
||||
|
|
@ -608,14 +612,12 @@ MSWindowsDesks::deskLeave(Desk* desk, HKL keyLayout)
|
|||
GetWindowThreadProcessId(desk->m_window, NULL);
|
||||
DWORD thatThread =
|
||||
GetWindowThreadProcessId(desk->m_foregroundWindow, NULL);
|
||||
|
||||
AttachThreadInput(thatThread, thisThread, TRUE);
|
||||
SetForegroundWindow(desk->m_window);
|
||||
AttachThreadInput(thatThread, thisThread, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
// switch to requested keyboard layout
|
||||
ActivateKeyboardLayout(keyLayout, 0);
|
||||
}
|
||||
else {
|
||||
// move hider window under the cursor center, raise, and show it
|
||||
|
|
|
|||
|
|
@ -577,8 +577,9 @@ static const Win32Modifiers s_modifiers[] =
|
|||
};
|
||||
|
||||
MSWindowsKeyState::MSWindowsKeyState(
|
||||
MSWindowsDesks* desks, void* eventTarget, IEventQueue* events) :
|
||||
KeyState(events),
|
||||
MSWindowsDesks* desks, void* eventTarget, IEventQueue* events,
|
||||
std::vector<String> layouts, bool isLangSyncEnabled) :
|
||||
KeyState(events, std::move(layouts), isLangSyncEnabled),
|
||||
m_eventTarget(eventTarget),
|
||||
m_desks(desks),
|
||||
m_keyLayout(GetKeyboardLayout(0)),
|
||||
|
|
@ -593,8 +594,9 @@ MSWindowsKeyState::MSWindowsKeyState(
|
|||
}
|
||||
|
||||
MSWindowsKeyState::MSWindowsKeyState(
|
||||
MSWindowsDesks* desks, void* eventTarget, IEventQueue* events, synergy::KeyMap& keyMap) :
|
||||
KeyState(events, keyMap),
|
||||
MSWindowsDesks* desks, void* eventTarget, IEventQueue* events, synergy::KeyMap& keyMap,
|
||||
std::vector<String> layouts, bool isLangSyncEnabled) :
|
||||
KeyState(events, keyMap, std::move(layouts), isLangSyncEnabled),
|
||||
m_eventTarget(eventTarget),
|
||||
m_desks(desks),
|
||||
m_keyLayout(GetKeyboardLayout(0)),
|
||||
|
|
@ -794,16 +796,16 @@ MSWindowsKeyState::sendKeyEvent(void* target,
|
|||
|
||||
void
|
||||
MSWindowsKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask,
|
||||
KeyButton button)
|
||||
KeyButton button, const String& lang)
|
||||
{
|
||||
KeyState::fakeKeyDown(id, mask, button);
|
||||
KeyState::fakeKeyDown(id, mask, button, lang);
|
||||
}
|
||||
|
||||
bool
|
||||
MSWindowsKeyState::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button)
|
||||
SInt32 count, KeyButton button, const String& lang)
|
||||
{
|
||||
return KeyState::fakeKeyRepeat(id, mask, count, button);
|
||||
return KeyState::fakeKeyRepeat(id, mask, count, button, lang);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -1331,8 +1333,10 @@ MSWindowsKeyState::setWindowGroup(SInt32 group)
|
|||
// XXX -- determine if m_groups[group] can be used with the system
|
||||
// character set.
|
||||
|
||||
PostMessage(targetWindow, WM_INPUTLANGCHANGEREQUEST,
|
||||
sysCharSet ? 1 : 0, (LPARAM)m_groups[group]);
|
||||
if(!PostMessage(targetWindow, WM_INPUTLANGCHANGEREQUEST,
|
||||
sysCharSet ? 1 : 0, (LPARAM)m_groups[group])) {
|
||||
LOG((CLOG_WARN "Failed to post change language message!"));
|
||||
}
|
||||
|
||||
// XXX -- use a short delay to let the target window process the message
|
||||
// before it sees the keyboard events. i'm not sure why this is
|
||||
|
|
|
|||
|
|
@ -36,8 +36,10 @@ This class maps KeyIDs to keystrokes.
|
|||
*/
|
||||
class MSWindowsKeyState : public KeyState {
|
||||
public:
|
||||
MSWindowsKeyState(MSWindowsDesks* desks, void* eventTarget, IEventQueue* events);
|
||||
MSWindowsKeyState(MSWindowsDesks* desks, void* eventTarget, IEventQueue* events, synergy::KeyMap& keyMap);
|
||||
MSWindowsKeyState(MSWindowsDesks* desks, void* eventTarget, IEventQueue* events,
|
||||
std::vector<String> layouts, bool isLangSyncEnabled);
|
||||
MSWindowsKeyState(MSWindowsDesks* desks, void* eventTarget, IEventQueue* events, synergy::KeyMap& keyMap,
|
||||
std::vector<String> layouts, bool isLangSyncEnabled);
|
||||
virtual ~MSWindowsKeyState();
|
||||
|
||||
//! @name manipulators
|
||||
|
|
@ -136,9 +138,9 @@ public:
|
|||
|
||||
// IKeyState overrides
|
||||
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
|
||||
KeyButton button);
|
||||
KeyButton button, const String& lang);
|
||||
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button);
|
||||
SInt32 count, KeyButton button, const String& lang);
|
||||
virtual bool fakeCtrlAltDel();
|
||||
virtual KeyModifierMask
|
||||
pollActiveModifiers() const;
|
||||
|
|
|
|||
|
|
@ -145,7 +145,9 @@ MSWindowsScreen::MSWindowsScreen(
|
|||
new TMethodJob<MSWindowsScreen>(
|
||||
this, &MSWindowsScreen::updateKeysCB),
|
||||
stopOnDeskSwitch);
|
||||
m_keyState = new MSWindowsKeyState(m_desks, getEventTarget(), m_events);
|
||||
m_keyState = new MSWindowsKeyState(m_desks, getEventTarget(), m_events,
|
||||
AppUtil::instance().getKeyboardLayoutList(),
|
||||
ClientApp::instance().args().m_enableLangSync);
|
||||
|
||||
updateScreenShape();
|
||||
m_class = createWindowClass();
|
||||
|
|
@ -331,9 +333,7 @@ MSWindowsScreen::leave()
|
|||
}
|
||||
// get keyboard layout of foreground window. we'll use this
|
||||
// keyboard layout for translating keys sent to clients.
|
||||
HWND window = GetForegroundWindow();
|
||||
DWORD thread = GetWindowThreadProcessId(window, NULL);
|
||||
m_keyLayout = GetKeyboardLayout(thread);
|
||||
m_keyLayout = AppUtilWindows::instance().getCurrentKeyboardLayout();
|
||||
|
||||
// tell the key mapper about the keyboard layout
|
||||
m_keyState->setKeyLayout(m_keyLayout);
|
||||
|
|
@ -844,17 +844,17 @@ MSWindowsScreen::updateKeys()
|
|||
|
||||
void
|
||||
MSWindowsScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
|
||||
KeyButton button)
|
||||
KeyButton button, const String& lang)
|
||||
{
|
||||
PlatformScreen::fakeKeyDown(id, mask, button);
|
||||
PlatformScreen::fakeKeyDown(id, mask, button, lang);
|
||||
updateForceShowCursor();
|
||||
}
|
||||
|
||||
bool
|
||||
MSWindowsScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button)
|
||||
SInt32 count, KeyButton button, const String& lang)
|
||||
{
|
||||
bool result = PlatformScreen::fakeKeyRepeat(id, mask, count, button);
|
||||
bool result = PlatformScreen::fakeKeyRepeat(id, mask, count, button, lang);
|
||||
updateForceShowCursor();
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1963,7 +1963,7 @@ MSWindowsScreen::getDraggingFilename()
|
|||
SWP_SHOWWINDOW);
|
||||
|
||||
// TODO: fake these keys properly
|
||||
fakeKeyDown(kKeyEscape, 8192, 1);
|
||||
fakeKeyDown(kKeyEscape, 8192, 1, AppUtil::instance().getCurrentLanguageCode());
|
||||
fakeKeyUp(1);
|
||||
fakeMouseButton(kButtonLeft, false);
|
||||
|
||||
|
|
|
|||
|
|
@ -116,9 +116,9 @@ public:
|
|||
// IKeyState overrides
|
||||
virtual void updateKeys();
|
||||
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
|
||||
KeyButton button);
|
||||
KeyButton button, const String& lang);
|
||||
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button);
|
||||
SInt32 count, KeyButton button, const String& lang);
|
||||
virtual bool fakeKeyUp(KeyButton button);
|
||||
virtual void fakeAllKeysUp();
|
||||
|
||||
|
|
|
|||
|
|
@ -134,14 +134,15 @@ static const KeyEntry s_controlKeys[] = {
|
|||
// OSXKeyState
|
||||
//
|
||||
|
||||
OSXKeyState::OSXKeyState(IEventQueue* events) :
|
||||
KeyState(events)
|
||||
OSXKeyState::OSXKeyState(IEventQueue* events, std::vector<String> layouts, bool isLangSyncEnabled) :
|
||||
KeyState(events, std::move(layouts), isLangSyncEnabled)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
OSXKeyState::OSXKeyState(IEventQueue* events, synergy::KeyMap& keyMap) :
|
||||
KeyState(events, keyMap)
|
||||
OSXKeyState::OSXKeyState(IEventQueue* events, synergy::KeyMap& keyMap,
|
||||
std::vector<String> layouts, bool isLangSyncEnabled) :
|
||||
KeyState(events, keyMap, std::move(layouts), isLangSyncEnabled)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
|
@ -410,7 +411,7 @@ OSXKeyState::pollActiveGroup() const
|
|||
return i->second;
|
||||
}
|
||||
|
||||
LOG((CLOG_DEBUG "can't get the active group, use the first group instead"));
|
||||
LOG((CLOG_WARN "can't get the active group, use the first group instead"));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -601,13 +602,19 @@ OSXKeyState::fakeKey(const Keystroke& keystroke)
|
|||
|
||||
case Keystroke::kGroup: {
|
||||
SInt32 group = keystroke.m_data.m_group.m_group;
|
||||
if (keystroke.m_data.m_group.m_absolute) {
|
||||
LOG((CLOG_DEBUG1 " group %d", group));
|
||||
setGroup(group);
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_DEBUG1 " group %+d", group));
|
||||
setGroup(getEffectiveGroup(pollActiveGroup(), group));
|
||||
if (!keystroke.m_data.m_group.m_restore) {
|
||||
if (keystroke.m_data.m_group.m_absolute) {
|
||||
LOG((CLOG_DEBUG1 " group %d", group));
|
||||
setGroup(group);
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_DEBUG1 " group %+d", group));
|
||||
setGroup(getEffectiveGroup(pollActiveGroup(), group));
|
||||
}
|
||||
|
||||
if(pollActiveGroup() != group) {
|
||||
LOG((CLOG_WARN "Failed to set new keyboard layout!"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -866,7 +873,29 @@ void
|
|||
OSXKeyState::setGroup(SInt32 group)
|
||||
{
|
||||
TISInputSourceRef keyboardLayout = (TISInputSourceRef)CFArrayGetValueAtIndex(m_groups.get(), group);
|
||||
TISSetInputMethodKeyboardLayoutOverride(keyboardLayout);
|
||||
if(!keyboardLayout) {
|
||||
LOG((CLOG_WARN "Nedeed keyboard layout is null"));
|
||||
return;
|
||||
}
|
||||
auto canBeSetted = (CFBooleanRef)TISGetInputSourceProperty(TISCopyCurrentKeyboardInputSource(), kTISPropertyInputSourceIsEnableCapable);
|
||||
if(!canBeSetted) {
|
||||
LOG((CLOG_WARN "Nedeed keyboard layout is disabled for programmatically selection"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(TISSelectInputSource(keyboardLayout) != noErr) {
|
||||
LOG((CLOG_WARN "Failed to set nedeed keyboard layout"));
|
||||
}
|
||||
|
||||
LOG((CLOG_DEBUG1 "Keyboard layout change to %d", group));
|
||||
|
||||
//A minimal delay is needed after a group change because the
|
||||
//keyboard key event often happens immediately after.
|
||||
//Language (TIS) and event (CG) systems are not in the mutual
|
||||
//event queue and without a delay the subsequent key press
|
||||
//event could be applied before the keyboard layout would
|
||||
//actually be changed.
|
||||
ARCH->sleep(.01);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ class OSXKeyState : public KeyState {
|
|||
public:
|
||||
typedef std::vector<KeyID> KeyIDs;
|
||||
|
||||
OSXKeyState(IEventQueue* events);
|
||||
OSXKeyState(IEventQueue* events, synergy::KeyMap& keyMap);
|
||||
OSXKeyState(IEventQueue* events, std::vector<String> layouts, bool isLangSyncEnabled);
|
||||
OSXKeyState(IEventQueue* events, synergy::KeyMap& keyMap,
|
||||
std::vector<String> layouts, bool isLangSyncEnabled);
|
||||
virtual ~OSXKeyState();
|
||||
|
||||
//! @name modifiers
|
||||
|
|
|
|||
|
|
@ -125,7 +125,9 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso
|
|||
|
||||
try {
|
||||
m_screensaver = new OSXScreenSaver(m_events, getEventTarget());
|
||||
m_keyState = new OSXKeyState(m_events);
|
||||
m_keyState = new OSXKeyState(m_events,
|
||||
AppUtil::instance().getKeyboardLayoutList(),
|
||||
ClientApp::instance().args().m_enableLangSync);
|
||||
|
||||
if (App::instance().argsBase().m_preventSleep) {
|
||||
m_powerManager.disableSleep();
|
||||
|
|
@ -2113,7 +2115,7 @@ OSXScreen::getDraggingFilename()
|
|||
}
|
||||
|
||||
// fake a escape key down and up then left mouse button up
|
||||
fakeKeyDown(kKeyEscape, 8192, 1);
|
||||
fakeKeyDown(kKeyEscape, 8192, 1, AppUtil::instance().getCurrentLanguageCode());
|
||||
fakeKeyUp(1);
|
||||
fakeMouseButton(kButtonLeft, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,19 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __APPLE__
|
||||
#include <QtDBus>
|
||||
#endif
|
||||
|
||||
#include "platform/XWindowsKeyState.h"
|
||||
|
||||
#include "platform/XWindowsUtil.h"
|
||||
#include "base/Log.h"
|
||||
#include "base/String.h"
|
||||
#include "common/stdmap.h"
|
||||
#include "synergy/AppUtil.h"
|
||||
#include "synergy/ClientApp.h"
|
||||
#include "synergy/ClientArgs.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
|
|
@ -43,7 +50,7 @@ static const size_t ModifiersFromXDefaultSize = 32;
|
|||
XWindowsKeyState::XWindowsKeyState(
|
||||
Display* display, bool useXKB,
|
||||
IEventQueue* events) :
|
||||
KeyState(events),
|
||||
KeyState(events, AppUtil::instance().getKeyboardLayoutList(), ClientApp::instance().args().m_enableLangSync),
|
||||
m_display(display),
|
||||
m_modifierFromX(ModifiersFromXDefaultSize)
|
||||
{
|
||||
|
|
@ -53,7 +60,7 @@ XWindowsKeyState::XWindowsKeyState(
|
|||
XWindowsKeyState::XWindowsKeyState(
|
||||
Display* display, bool useXKB,
|
||||
IEventQueue* events, synergy::KeyMap& keyMap) :
|
||||
KeyState(events, keyMap),
|
||||
KeyState(events, keyMap, AppUtil::instance().getKeyboardLayoutList(), ClientApp::instance().args().m_enableLangSync),
|
||||
m_display(display),
|
||||
m_modifierFromX(ModifiersFromXDefaultSize)
|
||||
{
|
||||
|
|
@ -82,7 +89,7 @@ XWindowsKeyState::init(Display* display, bool useXKB)
|
|||
m_xkb = NULL;
|
||||
}
|
||||
#endif
|
||||
setActiveGroup(kGroupPollAndSet);
|
||||
setActiveGroup(kGroupPoll);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -195,9 +202,13 @@ XWindowsKeyState::pollActiveGroup() const
|
|||
#if HAVE_XKB_EXTENSION
|
||||
if (m_xkb != NULL) {
|
||||
XkbStateRec state;
|
||||
XSync(m_display, False);
|
||||
if (XkbGetState(m_display, XkbUseCoreKbd, &state) == Success) {
|
||||
LOG((CLOG_WARN "LANGUAGE_DEBUG Poll result %d", state.group));
|
||||
return state.group;
|
||||
}
|
||||
|
||||
LOG((CLOG_WARN "Failed to poll active group"));
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
|
@ -238,12 +249,55 @@ XWindowsKeyState::getKeyMap(synergy::KeyMap& keyMap)
|
|||
updateKeysymMap(keyMap);
|
||||
}
|
||||
|
||||
bool XWindowsKeyState::setCurrentLanguageWithDBus(SInt32 group) const
|
||||
{
|
||||
QString service = "org.gnome.Shell";
|
||||
QString path = "/org/gnome/Shell";
|
||||
QString method = "Eval";
|
||||
QString param = "imports.ui.status.keyboard.getInputSourceManager().inputSources[" +
|
||||
QString::number(group) + "].activate()";
|
||||
|
||||
auto bus = QDBusConnection::sessionBus();
|
||||
if (!bus.isConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QDBusInterface screenSaverInterface(service, path, service, bus);
|
||||
if (!screenSaverInterface.isValid()) {
|
||||
LOG((CLOG_WARN "Keyboard layout fail. DBus interface is invalid"));
|
||||
return false;
|
||||
}
|
||||
|
||||
QDBusPendingReply<bool, QString> reply = screenSaverInterface.call(method, param);
|
||||
reply.waitForFinished();
|
||||
|
||||
if(!reply.isValid()) {
|
||||
auto qerror = reply.error();
|
||||
LOG((CLOG_WARN "Keyboard layout fail %s : %s", qerror.name().toStdString(), qerror.message().toStdString()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(reply.isError()) {
|
||||
LOG((CLOG_WARN "Keyboard layout fail. reply contains error"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!reply.argumentAt<0>() || reply.argumentAt<1>() != QString("")) {
|
||||
LOG((CLOG_WARN "Keyboard layout fail. Reply is unexpected!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
XWindowsKeyState::fakeKey(const Keystroke& keystroke)
|
||||
{
|
||||
switch (keystroke.m_type) {
|
||||
case Keystroke::kButton:
|
||||
LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up"));
|
||||
LOG((CLOG_WARN "LANGUAGE_DEBUG %03x (%08x) %s %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client,
|
||||
keystroke.m_data.m_button.m_press ? "down" : "up",
|
||||
keystroke.m_data.m_button.m_repeat ? "Repeat" : "New"));
|
||||
if (keystroke.m_data.m_button.m_repeat) {
|
||||
int c = keystroke.m_data.m_button.m_button;
|
||||
int i = (c >> 3);
|
||||
|
|
@ -260,8 +314,18 @@ XWindowsKeyState::fakeKey(const Keystroke& keystroke)
|
|||
break;
|
||||
|
||||
case Keystroke::kGroup:
|
||||
if (keystroke.m_data.m_group.m_restore) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (keystroke.m_data.m_group.m_absolute) {
|
||||
LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group));
|
||||
LOG((CLOG_WARN "LANGUAGE_DEBUG group %d", keystroke.m_data.m_group.m_group));
|
||||
|
||||
#ifndef __APPLE__
|
||||
if(setCurrentLanguageWithDBus(keystroke.m_data.m_group.m_group)) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if HAVE_XKB_EXTENSION
|
||||
if (m_xkb != NULL) {
|
||||
if (XkbLockGroup(m_display, XkbUseCoreKbd,
|
||||
|
|
@ -276,7 +340,13 @@ XWindowsKeyState::fakeKey(const Keystroke& keystroke)
|
|||
}
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group));
|
||||
LOG((CLOG_WARN "LANGUAGE_DEBUG group %+d", keystroke.m_data.m_group.m_group));
|
||||
|
||||
#ifndef __APPLE__
|
||||
if(setCurrentLanguageWithDBus(keystroke.m_data.m_group.m_group)) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if HAVE_XKB_EXTENSION
|
||||
if (m_xkb != NULL) {
|
||||
if (XkbLockGroup(m_display, XkbUseCoreKbd,
|
||||
|
|
@ -291,6 +361,7 @@ XWindowsKeyState::fakeKey(const Keystroke& keystroke)
|
|||
LOG((CLOG_DEBUG1 " ignored"));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
XFlush(m_display);
|
||||
|
|
|
|||
|
|
@ -123,6 +123,12 @@ private:
|
|||
int getEffectiveGroup(KeyCode, int group) const;
|
||||
UInt32 getGroupFromState(unsigned int state) const;
|
||||
|
||||
//! Create and send language change request to \p group by DBus interface
|
||||
/*!
|
||||
Returns the existance of nedeed DBus interface.
|
||||
*/
|
||||
bool setCurrentLanguageWithDBus(SInt32 group) const;
|
||||
|
||||
static void remapKeyModifiers(KeyID, SInt32,
|
||||
synergy::KeyMap::KeyItem&, void*);
|
||||
|
||||
|
|
|
|||
|
|
@ -74,9 +74,9 @@ public:
|
|||
virtual void setClipboard(ClipboardID, const IClipboard*) = 0;
|
||||
virtual void grabClipboard(ClipboardID) = 0;
|
||||
virtual void setClipboardDirty(ClipboardID, bool) = 0;
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton) = 0;
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton, const String&) = 0;
|
||||
virtual void keyRepeat(KeyID, KeyModifierMask,
|
||||
SInt32 count, KeyButton) = 0;
|
||||
SInt32 count, KeyButton, const String& lang) = 0;
|
||||
virtual void keyUp(KeyID, KeyModifierMask, KeyButton) = 0;
|
||||
virtual void mouseDown(ButtonID) = 0;
|
||||
virtual void mouseUp(ButtonID) = 0;
|
||||
|
|
|
|||
|
|
@ -75,9 +75,9 @@ public:
|
|||
void setClipboard(ClipboardID, const IClipboard*) override = 0;
|
||||
void grabClipboard(ClipboardID) override = 0;
|
||||
void setClipboardDirty(ClipboardID, bool) override = 0;
|
||||
void keyDown(KeyID, KeyModifierMask, KeyButton) override = 0;
|
||||
void keyDown(KeyID, KeyModifierMask, KeyButton, const String&) override = 0;
|
||||
void keyRepeat(KeyID, KeyModifierMask,
|
||||
SInt32 count, KeyButton) override = 0;
|
||||
SInt32 count, KeyButton, const String& lang) override = 0;
|
||||
void keyUp(KeyID, KeyModifierMask, KeyButton) override = 0;
|
||||
void mouseDown(ButtonID) override = 0;
|
||||
void mouseUp(ButtonID) override = 0;
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ ClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty)
|
|||
}
|
||||
|
||||
void
|
||||
ClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask, KeyButton)
|
||||
ClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask, KeyButton, const String&)
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask));
|
||||
ProtocolUtil::writef(getStream(), kMsgDKeyDown1_0, key, mask);
|
||||
|
|
@ -304,7 +304,7 @@ ClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask, KeyButton)
|
|||
|
||||
void
|
||||
ClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton)
|
||||
SInt32 count, KeyButton, const String&)
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d", getName().c_str(), key, mask, count));
|
||||
ProtocolUtil::writef(getStream(), kMsgDKeyRepeat1_0, key, mask, count);
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ public:
|
|||
void setClipboard(ClipboardID, const IClipboard*) override;
|
||||
void grabClipboard(ClipboardID) override;
|
||||
void setClipboardDirty(ClipboardID, bool) override;
|
||||
void keyDown(KeyID, KeyModifierMask, KeyButton) override;
|
||||
void keyDown(KeyID, KeyModifierMask, KeyButton, const String&) override;
|
||||
void keyRepeat(KeyID, KeyModifierMask,
|
||||
SInt32 count, KeyButton) override;
|
||||
SInt32 count, KeyButton, const String&) override;
|
||||
void keyUp(KeyID, KeyModifierMask, KeyButton) override;
|
||||
void mouseDown(ButtonID) override;
|
||||
void mouseUp(ButtonID) override;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "synergy/AppUtil.h"
|
||||
#include "server/ClientProxy1_1.h"
|
||||
|
||||
#include "synergy/ProtocolUtil.h"
|
||||
|
|
@ -39,18 +40,18 @@ ClientProxy1_1::~ClientProxy1_1()
|
|||
}
|
||||
|
||||
void
|
||||
ClientProxy1_1::keyDown(KeyID key, KeyModifierMask mask, KeyButton button)
|
||||
ClientProxy1_1::keyDown(KeyID key, KeyModifierMask mask, KeyButton button, const String& lang)
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button));
|
||||
ProtocolUtil::writef(getStream(), kMsgDKeyDown, key, mask, button);
|
||||
LOG((CLOG_DEBUG1 "send key down to \"%s\" id=%d, mask=0x%04x, button=0x%04x, lang=\"%s\"", getName().c_str(), key, mask, button, lang.c_str()));
|
||||
ProtocolUtil::writef(getStream(), kMsgDKeyDown, key, mask, button, &lang);
|
||||
}
|
||||
|
||||
void
|
||||
ClientProxy1_1::keyRepeat(KeyID key, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button)
|
||||
SInt32 count, KeyButton button, const String& lang)
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d, button=0x%04x", getName().c_str(), key, mask, count, button));
|
||||
ProtocolUtil::writef(getStream(), kMsgDKeyRepeat, key, mask, count, button);
|
||||
LOG((CLOG_DEBUG1 "send key repeat to \"%s\" id=%d, mask=0x%04x, count=%d, button=0x%04x, lang=\"%s\"", getName().c_str(), key, mask, count, button, lang.c_str()));
|
||||
ProtocolUtil::writef(getStream(), kMsgDKeyRepeat, key, mask, count, button, &lang);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ public:
|
|||
~ClientProxy1_1();
|
||||
|
||||
// IClient overrides
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
|
||||
virtual void keyDown(KeyID, KeyModifierMask, KeyButton, const String&);
|
||||
virtual void keyRepeat(KeyID, KeyModifierMask,
|
||||
SInt32 count, KeyButton);
|
||||
SInt32 count, KeyButton, const String&);
|
||||
virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -42,15 +42,15 @@ ClientProxy1_4::~ClientProxy1_4()
|
|||
}
|
||||
|
||||
void
|
||||
ClientProxy1_4::keyDown(KeyID key, KeyModifierMask mask, KeyButton button)
|
||||
ClientProxy1_4::keyDown(KeyID key, KeyModifierMask mask, KeyButton button, const String& lang)
|
||||
{
|
||||
ClientProxy1_3::keyDown(key, mask, button);
|
||||
ClientProxy1_3::keyDown(key, mask, button, lang);
|
||||
}
|
||||
|
||||
void
|
||||
ClientProxy1_4::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button)
|
||||
ClientProxy1_4::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button, const String& lang)
|
||||
{
|
||||
ClientProxy1_3::keyRepeat(key, mask, count, button);
|
||||
ClientProxy1_3::keyRepeat(key, mask, count, button, lang);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -37,8 +37,9 @@ public:
|
|||
//@}
|
||||
|
||||
// IClient overrides
|
||||
virtual void keyDown(KeyID key, KeyModifierMask mask, KeyButton button);
|
||||
virtual void keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button);
|
||||
virtual void keyDown(KeyID key, KeyModifierMask mask, KeyButton button, const String&);
|
||||
virtual void keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button,
|
||||
const String& lang);
|
||||
virtual void keyUp(KeyID key, KeyModifierMask mask, KeyButton button);
|
||||
virtual void keepAlive();
|
||||
|
||||
|
|
|
|||
|
|
@ -29,14 +29,17 @@
|
|||
#include "server/ClientProxy1_7.h"
|
||||
#include "synergy/protocol_types.h"
|
||||
#include "synergy/ProtocolUtil.h"
|
||||
#include "synergy/AppUtil.h"
|
||||
#include "synergy/XSynergy.h"
|
||||
#include "io/IStream.h"
|
||||
#include "io/XIO.h"
|
||||
#include "base/Log.h"
|
||||
#include "base/String.h"
|
||||
#include "base/IEventQueue.h"
|
||||
#include "base/TMethodEventJob.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
|
||||
//
|
||||
// ClientProxyUnknown
|
||||
//
|
||||
|
|
@ -57,9 +60,14 @@ ClientProxyUnknown::ClientProxyUnknown(synergy::IStream* stream, double timeout,
|
|||
addStreamHandlers();
|
||||
|
||||
LOG((CLOG_DEBUG1 "saying hello"));
|
||||
String allKeyboardLayoutsStr;
|
||||
for (const auto& layout : AppUtil::instance().getKeyboardLayoutList()) {
|
||||
allKeyboardLayoutsStr += layout;
|
||||
}
|
||||
ProtocolUtil::writef(m_stream, kMsgHello,
|
||||
kProtocolMajorVersion,
|
||||
kProtocolMinorVersion);
|
||||
kProtocolMinorVersion,
|
||||
&allKeyboardLayoutsStr);
|
||||
}
|
||||
|
||||
ClientProxyUnknown::~ClientProxyUnknown()
|
||||
|
|
@ -178,6 +186,7 @@ ClientProxyUnknown::handleData(const Event&, void*)
|
|||
LOG((CLOG_DEBUG1 "parsing hello reply"));
|
||||
|
||||
String name("<unknown>");
|
||||
String keyboardLayoutList("<unknown>");
|
||||
try {
|
||||
// limit the maximum length of the hello
|
||||
UInt32 n = m_stream->getSize();
|
||||
|
|
@ -189,7 +198,7 @@ ClientProxyUnknown::handleData(const Event&, void*)
|
|||
// parse the reply to hello
|
||||
SInt16 major, minor;
|
||||
if (!ProtocolUtil::readf(m_stream, kMsgHelloBack,
|
||||
&major, &minor, &name)) {
|
||||
&major, &minor, &name, &keyboardLayoutList)) {
|
||||
throw XBadClient();
|
||||
}
|
||||
|
||||
|
|
@ -245,6 +254,22 @@ ClientProxyUnknown::handleData(const Event&, void*)
|
|||
throw XIncompatibleClient(major, minor);
|
||||
}
|
||||
|
||||
std::vector<String> missed;
|
||||
std::vector<String> supported;
|
||||
AppUtil::getKeyboardLayoutsDiff(keyboardLayoutList,
|
||||
AppUtil::instance().getKeyboardLayoutList(),
|
||||
missed, supported);
|
||||
|
||||
if(!supported.empty()) {
|
||||
LOG((CLOG_DEBUG "Supported client languages: %s", AppUtil::joinStrVector(supported, ", ").c_str()));
|
||||
}
|
||||
|
||||
if(!missed.empty()) {
|
||||
auto result = AppUtil::joinStrVector(missed, ", ");
|
||||
AppUtil::instance().showNotification("Language synchronization error",
|
||||
"These languages are required for server proper work: " + result);
|
||||
}
|
||||
|
||||
// the proxy is created and now proxy now owns the stream
|
||||
LOG((CLOG_DEBUG1 "created proxy for client \"%s\" version %d.%d", name.c_str(), major, minor));
|
||||
m_stream = NULL;
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ PrimaryClient::setClipboardDirty(ClipboardID id, bool dirty)
|
|||
}
|
||||
|
||||
void
|
||||
PrimaryClient::keyDown(KeyID key, KeyModifierMask mask, KeyButton button)
|
||||
PrimaryClient::keyDown(KeyID key, KeyModifierMask mask, KeyButton button, const String&)
|
||||
{
|
||||
if (m_fakeInputCount > 0) {
|
||||
// XXX -- don't forward keystrokes to primary screen for now
|
||||
|
|
@ -196,7 +196,7 @@ PrimaryClient::keyDown(KeyID key, KeyModifierMask mask, KeyButton button)
|
|||
}
|
||||
|
||||
void
|
||||
PrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton)
|
||||
PrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton, const String&)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,9 +131,9 @@ public:
|
|||
void setClipboard(ClipboardID, const IClipboard*) override;
|
||||
void grabClipboard(ClipboardID) override;
|
||||
void setClipboardDirty(ClipboardID, bool) override;
|
||||
void keyDown(KeyID, KeyModifierMask, KeyButton) override;
|
||||
void keyDown(KeyID, KeyModifierMask, KeyButton, const String&) override;
|
||||
void keyRepeat(KeyID, KeyModifierMask,
|
||||
SInt32 count, KeyButton) override;
|
||||
SInt32 count, KeyButton, const String&) override;
|
||||
void keyUp(KeyID, KeyModifierMask, KeyButton) override;
|
||||
void mouseDown(ButtonID) override;
|
||||
void mouseUp(ButtonID) override;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "synergy/KeyState.h"
|
||||
#include "synergy/Screen.h"
|
||||
#include "synergy/PacketStreamFilter.h"
|
||||
#include "synergy/AppUtil.h"
|
||||
#include "net/TCPSocket.h"
|
||||
#include "net/IDataSocket.h"
|
||||
#include "net/IListenSocket.h"
|
||||
|
|
@ -100,7 +101,7 @@ Server::Server(
|
|||
m_waitDragInfoThread(true),
|
||||
m_args(args)
|
||||
{
|
||||
// must have a primary client and it must have a canonical name
|
||||
// must have a primary client and it must have a canonical name
|
||||
assert(m_primaryClient != NULL);
|
||||
assert(config.isScreen(primaryClient->getName()));
|
||||
assert(m_screen != NULL);
|
||||
|
|
@ -1334,7 +1335,8 @@ Server::handleKeyDownEvent(const Event& event, void*)
|
|||
{
|
||||
IPlatformScreen::KeyInfo* info =
|
||||
static_cast<IPlatformScreen::KeyInfo*>(event.getData());
|
||||
onKeyDown(info->m_key, info->m_mask, info->m_button, info->m_screens);
|
||||
auto lang = AppUtil::instance().getCurrentLanguageCode();
|
||||
onKeyDown(info->m_key, info->m_mask, info->m_button, lang, info->m_screens);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1350,7 +1352,8 @@ Server::handleKeyRepeatEvent(const Event& event, void*)
|
|||
{
|
||||
IPlatformScreen::KeyInfo* info =
|
||||
static_cast<IPlatformScreen::KeyInfo*>(event.getData());
|
||||
onKeyRepeat(info->m_key, info->m_mask, info->m_count, info->m_button);
|
||||
auto lang = AppUtil::instance().getCurrentLanguageCode();
|
||||
onKeyRepeat(info->m_key, info->m_mask, info->m_count, info->m_button, lang);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1666,15 +1669,15 @@ Server::onScreensaver(bool activated)
|
|||
}
|
||||
|
||||
void
|
||||
Server::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button,
|
||||
Server::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button, const String& lang,
|
||||
const char* screens)
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button));
|
||||
LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x lang=%s", id, mask, button, lang.c_str()));
|
||||
assert(m_active != NULL);
|
||||
|
||||
// relay
|
||||
if (!m_keyboardBroadcasting && IKeyState::KeyInfo::isDefault(screens)) {
|
||||
m_active->keyDown(id, mask, button);
|
||||
m_active->keyDown(id, mask, button, lang);
|
||||
}
|
||||
else {
|
||||
if (!screens && m_keyboardBroadcasting) {
|
||||
|
|
@ -1686,7 +1689,7 @@ Server::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button,
|
|||
for (ClientList::const_iterator index = m_clients.begin();
|
||||
index != m_clients.end(); ++index) {
|
||||
if (IKeyState::KeyInfo::contains(screens, index->first)) {
|
||||
index->second->keyDown(id, mask, button);
|
||||
index->second->keyDown(id, mask, button, lang);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1721,13 +1724,13 @@ Server::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button,
|
|||
|
||||
void
|
||||
Server::onKeyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button)
|
||||
SInt32 count, KeyButton button, const String& lang)
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button));
|
||||
LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x lang=\"%s\"", id, mask, count, button, lang.c_str()));
|
||||
assert(m_active != NULL);
|
||||
|
||||
// relay
|
||||
m_active->keyRepeat(id, mask, count, button);
|
||||
m_active->keyRepeat(id, mask, count, button, lang);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -325,11 +325,11 @@ private:
|
|||
void onClipboardChanged(BaseClientProxy* sender,
|
||||
ClipboardID id, UInt32 seqNum);
|
||||
void onScreensaver(bool activated);
|
||||
void onKeyDown(KeyID, KeyModifierMask, KeyButton,
|
||||
void onKeyDown(KeyID, KeyModifierMask, KeyButton, const String&,
|
||||
const char* screens);
|
||||
void onKeyUp(KeyID, KeyModifierMask, KeyButton,
|
||||
const char* screens);
|
||||
void onKeyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton);
|
||||
void onKeyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton, const String&);
|
||||
void onMouseDown(ButtonID);
|
||||
void onMouseUp(ButtonID);
|
||||
bool onMouseMovePrimary(SInt32 x, SInt32 y);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include "synergy/AppUtil.h"
|
||||
|
||||
#include "algorithm"
|
||||
|
||||
AppUtil* AppUtil::s_instance = nullptr;
|
||||
|
||||
AppUtil::AppUtil() :
|
||||
|
|
@ -50,3 +52,22 @@ AppUtil::instance()
|
|||
assert(s_instance != nullptr);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
void
|
||||
AppUtil::getKeyboardLayoutsDiff(const String& remoteCompressedLanguages,
|
||||
const std::vector<String>& localLanguages,
|
||||
std::vector<String>& missedLang,
|
||||
std::vector<String>& supportedLang)
|
||||
{
|
||||
missedLang.clear();
|
||||
supportedLang.clear();
|
||||
for(int i = 0; i <= (int)remoteCompressedLanguages.size() - 2; i +=2) {
|
||||
auto layout = remoteCompressedLanguages.substr(i, 2);
|
||||
if (std::find(localLanguages.begin(), localLanguages.end(), layout) == localLanguages.end()) {
|
||||
missedLang.push_back(layout);
|
||||
}
|
||||
else {
|
||||
supportedLang.push_back(layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ public:
|
|||
static AppUtil& instance();
|
||||
static void exitAppStatic(int code) { instance().exitApp(code); }
|
||||
virtual void beforeAppExit() {}
|
||||
static void getKeyboardLayoutsDiff(const String& remoteCompressedLanguages,
|
||||
const std::vector<String>& localLanguages,
|
||||
std::vector<String>& missedLang,
|
||||
std::vector<String>& supportedLang);
|
||||
|
||||
private:
|
||||
IApp* m_app;
|
||||
|
|
|
|||
|
|
@ -111,6 +111,9 @@ ArgParser::parseClientArgs(lib::synergy::ClientArgs& args, int argc, const char*
|
|||
// define scroll
|
||||
args.m_yscroll = atoi(argv[++i]);
|
||||
}
|
||||
else if (isArg(i, argc, argv, nullptr, "--sync-language")) {
|
||||
args.m_enableLangSync = true;
|
||||
}
|
||||
else {
|
||||
if (i + 1 == argc) {
|
||||
args.m_synergyAddress = argv[i];
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ namespace lib {
|
|||
String m_profileDirectory; /// @brief The profile DIR to use for the application
|
||||
String m_pluginDirectory; /// @brief //TODO Plugins? Get set in ARCH but doesn't seem to get used
|
||||
String m_tlsCertFile; /// @brief Contains the location of the TLS certificate file
|
||||
bool m_enableLangSync = true; /// @brief Should keyboard input be in same language as on server
|
||||
bool m_preventSleep = false; /// @brief Stop this computer from sleeping
|
||||
|
||||
#if SYSAPI_WIN32
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ elseif (UNIX)
|
|||
file(GLOB arch_sources "unix/*.cpp")
|
||||
endif()
|
||||
|
||||
include_directories("../../../ext/pugixml/src")
|
||||
list(APPEND arch_sources "../../../ext/pugixml/src/pugixml.cpp")
|
||||
|
||||
list(APPEND sources ${arch_sources})
|
||||
list(APPEND headers ${arch_headers})
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ ClientApp::help()
|
|||
buffer_size,
|
||||
"Usage: %s"
|
||||
" [--yscroll <delta>]"
|
||||
" [--sync-language]"
|
||||
WINAPI_ARG
|
||||
HELP_SYS_ARGS
|
||||
HELP_COMMON_ARGS
|
||||
|
|
@ -138,6 +139,7 @@ ClientApp::help()
|
|||
HELP_SYS_INFO
|
||||
" --yscroll <delta> defines the vertical scrolling delta, which is\n"
|
||||
" 120 by default.\n"
|
||||
" --sync-language set this parameter to enable language synchronization.\n"
|
||||
HELP_COMMON_INFO_2
|
||||
"\n"
|
||||
"* marks defaults.\n"
|
||||
|
|
|
|||
|
|
@ -32,11 +32,12 @@ namespace lib {
|
|||
~ClientArgs() override;
|
||||
|
||||
public:
|
||||
int m_yscroll = 0;
|
||||
int m_yscroll = 0;
|
||||
bool m_enableLangSync = false; /// @brief Should keyboard input be in same language as on server
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif //SYNERGY_CORE_CLIENTARGS_H
|
||||
#endif //SYNERGY_CORE_CLIENTARGS_H
|
||||
|
|
|
|||
|
|
@ -29,5 +29,21 @@ public:
|
|||
virtual int run(int argc, char** argv) = 0;
|
||||
virtual void beforeAppExit() = 0;
|
||||
virtual void startNode() = 0;
|
||||
virtual std::vector<String> getKeyboardLayoutList() = 0;
|
||||
virtual String getCurrentLanguageCode() = 0;
|
||||
virtual void showNotification(const String& title, const String& text) const = 0;
|
||||
|
||||
static String joinStrVector(const std::vector<String>& source, const String& delim)
|
||||
{
|
||||
String result;
|
||||
for(size_t i = 0; i < source.size(); i++)
|
||||
{
|
||||
result += source[i];
|
||||
|
||||
if(i < source.size() - 1) {
|
||||
result += delim;
|
||||
}
|
||||
}
|
||||
return std::move(result);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public:
|
|||
synthesize an up or repeat for the same client key synthesized by
|
||||
keyDown().
|
||||
*/
|
||||
virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0;
|
||||
virtual void keyDown(KeyID id, KeyModifierMask, KeyButton, const String&) = 0;
|
||||
|
||||
//! Notify of key repeat
|
||||
/*!
|
||||
|
|
@ -95,7 +95,7 @@ public:
|
|||
\c count times. If possible match the given modifier mask.
|
||||
*/
|
||||
virtual void keyRepeat(KeyID id, KeyModifierMask,
|
||||
SInt32 count, KeyButton) = 0;
|
||||
SInt32 count, KeyButton, const String& lang) = 0;
|
||||
|
||||
//! Notify of key release
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -94,14 +94,14 @@ public:
|
|||
Synthesizes a key press event and updates the key state.
|
||||
*/
|
||||
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
|
||||
KeyButton button) = 0;
|
||||
KeyButton button, const String& lang) = 0;
|
||||
|
||||
//! Fake a key repeat
|
||||
/*!
|
||||
Synthesizes a key repeat event and updates the key state.
|
||||
*/
|
||||
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button) = 0;
|
||||
SInt32 count, KeyButton button, const String& lang) = 0;
|
||||
|
||||
//! Fake a key release
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -194,9 +194,9 @@ public:
|
|||
virtual void updateKeyState() = 0;
|
||||
virtual void setHalfDuplexMask(KeyModifierMask) = 0;
|
||||
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
|
||||
KeyButton button) = 0;
|
||||
KeyButton button, const String& lang) = 0;
|
||||
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button) = 0;
|
||||
SInt32 count, KeyButton button, const String& lang) = 0;
|
||||
virtual bool fakeKeyUp(KeyButton button) = 0;
|
||||
virtual void fakeAllKeysUp() = 0;
|
||||
virtual bool fakeCtrlAltDel() = 0;
|
||||
|
|
|
|||
232
src/lib/synergy/ISO639Table.h
Normal file
232
src/lib/synergy/ISO639Table.h
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2016 Symless Ltd.
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/String.h"
|
||||
|
||||
//copy from
|
||||
//https://www.loc.gov/standards/iso639-2/php/code_list.php
|
||||
//10.06.2021
|
||||
//first param - ISO 639-2, second param - 639-1
|
||||
const std::vector<std::pair<String, String>> ISO_Table = {
|
||||
std::make_pair("aar", "aa"),
|
||||
std::make_pair("abk", "ab"),
|
||||
std::make_pair("afr", "af"),
|
||||
std::make_pair("aka", "ak"),
|
||||
std::make_pair("sqi", "sq"),
|
||||
std::make_pair("amh", "am"),
|
||||
std::make_pair("ara", "ar"),
|
||||
std::make_pair("arg", "an"),
|
||||
std::make_pair("hye", "hy"),
|
||||
std::make_pair("asm", "as"),
|
||||
std::make_pair("ava", "av"),
|
||||
std::make_pair("ave", "ae"),
|
||||
std::make_pair("aym", "ay"),
|
||||
std::make_pair("aze", "az"),
|
||||
std::make_pair("bak", "ba"),
|
||||
std::make_pair("bam", "bm"),
|
||||
std::make_pair("eus", "eu"),
|
||||
std::make_pair("bel", "be"),
|
||||
std::make_pair("ben", "bn"),
|
||||
std::make_pair("bih", "bh"),
|
||||
std::make_pair("bis", "bi"),
|
||||
std::make_pair("bod", "bo"),
|
||||
std::make_pair("bos", "bs"),
|
||||
std::make_pair("bre", "br"),
|
||||
std::make_pair("bul", "bg"),
|
||||
std::make_pair("mya", "my"),
|
||||
std::make_pair("cat", "ca"),
|
||||
std::make_pair("ces", "cs"),
|
||||
std::make_pair("cha", "ch"),
|
||||
std::make_pair("che", "ce"),
|
||||
std::make_pair("zho", "zh"),
|
||||
std::make_pair("chu", "cu"),
|
||||
std::make_pair("chv", "cv"),
|
||||
std::make_pair("cor", "kw"),
|
||||
std::make_pair("cos", "co"),
|
||||
std::make_pair("cre", "cr"),
|
||||
std::make_pair("cym", "cy"),
|
||||
std::make_pair("ces", "cs"),
|
||||
std::make_pair("dan", "da"),
|
||||
std::make_pair("deu", "de"),
|
||||
std::make_pair("div", "dv"),
|
||||
std::make_pair("nld", "nl"),
|
||||
std::make_pair("dzo", "dz"),
|
||||
std::make_pair("ell", "el"),
|
||||
std::make_pair("eng", "en"),
|
||||
std::make_pair("epo", "eo"),
|
||||
std::make_pair("est", "et"),
|
||||
std::make_pair("eus", "eu"),
|
||||
std::make_pair("ewe", "ee"),
|
||||
std::make_pair("fao", "fo"),
|
||||
std::make_pair("fas", "fa"),
|
||||
std::make_pair("fij", "fj"),
|
||||
std::make_pair("fin", "fi"),
|
||||
std::make_pair("fra", "fr"),
|
||||
std::make_pair("fra", "fr"),
|
||||
std::make_pair("fry", "fy"),
|
||||
std::make_pair("ful", "ff"),
|
||||
std::make_pair("kat", "ka"),
|
||||
std::make_pair("deu", "de"),
|
||||
std::make_pair("gla", "gd"),
|
||||
std::make_pair("gle", "ga"),
|
||||
std::make_pair("glg", "gl"),
|
||||
std::make_pair("glv", "gv"),
|
||||
std::make_pair("ell", "el"),
|
||||
std::make_pair("grn", "gn"),
|
||||
std::make_pair("guj", "gu"),
|
||||
std::make_pair("hat", "ht"),
|
||||
std::make_pair("hau", "ha"),
|
||||
std::make_pair("heb", "he"),
|
||||
std::make_pair("her", "hz"),
|
||||
std::make_pair("hin", "hi"),
|
||||
std::make_pair("hmo", "ho"),
|
||||
std::make_pair("hrv", "hr"),
|
||||
std::make_pair("hun", "hu"),
|
||||
std::make_pair("hye", "hy"),
|
||||
std::make_pair("ibo", "ig"),
|
||||
std::make_pair("isl", "is"),
|
||||
std::make_pair("ido", "io"),
|
||||
std::make_pair("iii", "ii"),
|
||||
std::make_pair("iku", "iu"),
|
||||
std::make_pair("ile", "ie"),
|
||||
std::make_pair("ina", "ia"),
|
||||
std::make_pair("ind", "id"),
|
||||
std::make_pair("ipk", "ik"),
|
||||
std::make_pair("isl", "is"),
|
||||
std::make_pair("ita", "it"),
|
||||
std::make_pair("jav", "jv"),
|
||||
std::make_pair("jpn", "ja"),
|
||||
std::make_pair("kal", "kl"),
|
||||
std::make_pair("kan", "kn"),
|
||||
std::make_pair("kas", "ks"),
|
||||
std::make_pair("kat", "ka"),
|
||||
std::make_pair("kau", "kr"),
|
||||
std::make_pair("kaz", "kk"),
|
||||
std::make_pair("khm", "km"),
|
||||
std::make_pair("kik", "ki"),
|
||||
std::make_pair("kin", "rw"),
|
||||
std::make_pair("kir", "ky"),
|
||||
std::make_pair("kom", "kv"),
|
||||
std::make_pair("kon", "kg"),
|
||||
std::make_pair("kor", "ko"),
|
||||
std::make_pair("kua", "kj"),
|
||||
std::make_pair("kur", "ku"),
|
||||
std::make_pair("lao", "lo"),
|
||||
std::make_pair("lat", "la"),
|
||||
std::make_pair("lav", "lv"),
|
||||
std::make_pair("lim", "li"),
|
||||
std::make_pair("lin", "ln"),
|
||||
std::make_pair("lit", "lt"),
|
||||
std::make_pair("ltz", "lb"),
|
||||
std::make_pair("lub", "lu"),
|
||||
std::make_pair("lug", "lg"),
|
||||
std::make_pair("mkd", "mk"),
|
||||
std::make_pair("mah", "mh"),
|
||||
std::make_pair("mal", "ml"),
|
||||
std::make_pair("mri", "mi"),
|
||||
std::make_pair("mar", "mr"),
|
||||
std::make_pair("msa", "ms"),
|
||||
std::make_pair("mkd", "mk"),
|
||||
std::make_pair("mlg", "mg"),
|
||||
std::make_pair("mlt", "mt"),
|
||||
std::make_pair("mon", "mn"),
|
||||
std::make_pair("mri", "mi"),
|
||||
std::make_pair("msa", "ms"),
|
||||
std::make_pair("mya", "my"),
|
||||
std::make_pair("nau", "na"),
|
||||
std::make_pair("nav", "nv"),
|
||||
std::make_pair("nbl", "nr"),
|
||||
std::make_pair("nde", "nd"),
|
||||
std::make_pair("ndo", "ng"),
|
||||
std::make_pair("nep", "ne"),
|
||||
std::make_pair("nld", "nl"),
|
||||
std::make_pair("nno", "nn"),
|
||||
std::make_pair("nob", "nb"),
|
||||
std::make_pair("nor", "no"),
|
||||
std::make_pair("nya", "ny"),
|
||||
std::make_pair("oci", "oc"),
|
||||
std::make_pair("oji", "oj"),
|
||||
std::make_pair("ori", "or"),
|
||||
std::make_pair("orm", "om"),
|
||||
std::make_pair("oss", "os"),
|
||||
std::make_pair("pan", "pa"),
|
||||
std::make_pair("fas", "fa"),
|
||||
std::make_pair("pli", "pi"),
|
||||
std::make_pair("pol", "pl"),
|
||||
std::make_pair("por", "pt"),
|
||||
std::make_pair("pus", "ps"),
|
||||
std::make_pair("que", "qu"),
|
||||
std::make_pair("roh", "rm"),
|
||||
std::make_pair("ron", "ro"),
|
||||
std::make_pair("ron", "ro"),
|
||||
std::make_pair("run", "rn"),
|
||||
std::make_pair("rus", "ru"),
|
||||
std::make_pair("sag", "sg"),
|
||||
std::make_pair("san", "sa"),
|
||||
std::make_pair("sin", "si"),
|
||||
std::make_pair("slk", "sk"),
|
||||
std::make_pair("slk", "sk"),
|
||||
std::make_pair("slv", "sl"),
|
||||
std::make_pair("sme", "se"),
|
||||
std::make_pair("smo", "sm"),
|
||||
std::make_pair("sna", "sn"),
|
||||
std::make_pair("snd", "sd"),
|
||||
std::make_pair("som", "so"),
|
||||
std::make_pair("sot", "st"),
|
||||
std::make_pair("spa", "es"),
|
||||
std::make_pair("sqi", "sq"),
|
||||
std::make_pair("srd", "sc"),
|
||||
std::make_pair("srp", "sr"),
|
||||
std::make_pair("ssw", "ss"),
|
||||
std::make_pair("sun", "su"),
|
||||
std::make_pair("swa", "sw"),
|
||||
std::make_pair("swe", "sv"),
|
||||
std::make_pair("tah", "ty"),
|
||||
std::make_pair("tam", "ta"),
|
||||
std::make_pair("tat", "tt"),
|
||||
std::make_pair("tel", "te"),
|
||||
std::make_pair("tgk", "tg"),
|
||||
std::make_pair("tgl", "tl"),
|
||||
std::make_pair("tha", "th"),
|
||||
std::make_pair("bod", "bo"),
|
||||
std::make_pair("tir", "ti"),
|
||||
std::make_pair("ton", "to"),
|
||||
std::make_pair("tsn", "tn"),
|
||||
std::make_pair("tso", "ts"),
|
||||
std::make_pair("tuk", "tk"),
|
||||
std::make_pair("tur", "tr"),
|
||||
std::make_pair("twi", "tw"),
|
||||
std::make_pair("uig", "ug"),
|
||||
std::make_pair("ukr", "uk"),
|
||||
std::make_pair("urd", "ur"),
|
||||
std::make_pair("uzb", "uz"),
|
||||
std::make_pair("ven", "ve"),
|
||||
std::make_pair("vie", "vi"),
|
||||
std::make_pair("vol", "vo"),
|
||||
std::make_pair("cym", "cy"),
|
||||
std::make_pair("wln", "wa"),
|
||||
std::make_pair("wol", "wo"),
|
||||
std::make_pair("xho", "xh"),
|
||||
std::make_pair("yid", "yi"),
|
||||
std::make_pair("yor", "yo"),
|
||||
std::make_pair("zha", "za"),
|
||||
std::make_pair("zho", "zh"),
|
||||
std::make_pair("zul", "zu"),
|
||||
};
|
||||
|
|
@ -264,9 +264,10 @@ KeyMap::mapKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
ModifierToKeys& activeModifiers,
|
||||
KeyModifierMask& currentState,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const
|
||||
bool isAutoRepeat, const String& lang) const
|
||||
{
|
||||
LOG((CLOG_DEBUG1 "mapKey %04x (%d) with mask %04x, start state: %04x", id, id, desiredMask, currentState));
|
||||
LOG((CLOG_DEBUG1 "mapKey %04x (%d) with mask %04x, start state: %04x, group: %d",
|
||||
id, id, desiredMask, currentState, group));
|
||||
|
||||
// handle group change
|
||||
if (id == kKeyNextGroup) {
|
||||
|
|
@ -295,7 +296,7 @@ KeyMap::mapKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
case kKeyNumLock:
|
||||
case kKeyScrollLock:
|
||||
item = mapModifierKey(keys, id, group, activeModifiers,
|
||||
currentState, desiredMask, isAutoRepeat);
|
||||
currentState, desiredMask, isAutoRepeat, lang);
|
||||
break;
|
||||
|
||||
case kKeySetModifiers:
|
||||
|
|
@ -318,11 +319,11 @@ KeyMap::mapKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
default:
|
||||
if (isCommand(desiredMask)) {
|
||||
item = mapCommandKey(keys, id, group, activeModifiers,
|
||||
currentState, desiredMask, isAutoRepeat);
|
||||
currentState, desiredMask, isAutoRepeat, lang);
|
||||
}
|
||||
else {
|
||||
item = mapCharacterKey(keys, id, group, activeModifiers,
|
||||
currentState, desiredMask, isAutoRepeat);
|
||||
currentState, desiredMask, isAutoRepeat, lang);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -333,6 +334,11 @@ KeyMap::mapKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
return item;
|
||||
}
|
||||
|
||||
void KeyMap::setLanguageData(std::vector<String> layouts)
|
||||
{
|
||||
m_keyboardLayouts = std::move(layouts);
|
||||
}
|
||||
|
||||
SInt32
|
||||
KeyMap::getNumGroups() const
|
||||
{
|
||||
|
|
@ -512,7 +518,7 @@ KeyMap::mapCommandKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
ModifierToKeys& activeModifiers,
|
||||
KeyModifierMask& currentState,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const
|
||||
bool isAutoRepeat, const String& lang) const
|
||||
{
|
||||
static const KeyModifierMask s_overrideModifiers = 0xffffu;
|
||||
|
||||
|
|
@ -574,7 +580,7 @@ KeyMap::mapCommandKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
// add the key
|
||||
if (!keysForKeyItem(*keyItem, newGroup, newModifiers,
|
||||
newState, desiredMask,
|
||||
s_overrideModifiers, isAutoRepeat, keys)) {
|
||||
s_overrideModifiers, isAutoRepeat, keys, lang)) {
|
||||
LOG((CLOG_DEBUG1 "can't map key"));
|
||||
keys.clear();
|
||||
return NULL;
|
||||
|
|
@ -588,11 +594,6 @@ KeyMap::mapCommandKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// add keystrokes to restore group
|
||||
if (newGroup != group) {
|
||||
keys.push_back(Keystroke(group, true, true));
|
||||
}
|
||||
|
||||
// save new modifiers
|
||||
activeModifiers = newModifiers;
|
||||
currentState = newState;
|
||||
|
|
@ -605,7 +606,7 @@ KeyMap::mapCharacterKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
ModifierToKeys& activeModifiers,
|
||||
KeyModifierMask& currentState,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const
|
||||
bool isAutoRepeat, const String& lang) const
|
||||
{
|
||||
// find KeySym in table
|
||||
KeyIDMap::const_iterator i = m_keyIDMap.find(id);
|
||||
|
|
@ -613,7 +614,6 @@ KeyMap::mapCharacterKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
// unknown key
|
||||
LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id));
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
const KeyGroupTable& keyGroupTable = i->second;
|
||||
|
|
@ -655,7 +655,7 @@ KeyMap::mapCharacterKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
for (size_t j = 0; j < itemList.size(); ++j) {
|
||||
if (!keysForKeyItem(itemList[j], newGroup, newModifiers,
|
||||
newState, desiredMask,
|
||||
0, isAutoRepeat, keys)) {
|
||||
0, isAutoRepeat, keys, lang)) {
|
||||
LOG((CLOG_DEBUG1 "can't map key"));
|
||||
keys.clear();
|
||||
return NULL;
|
||||
|
|
@ -670,11 +670,6 @@ KeyMap::mapCharacterKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// add keystrokes to restore group
|
||||
if (newGroup != group) {
|
||||
keys.push_back(Keystroke(group, true, true));
|
||||
}
|
||||
|
||||
// save new modifiers
|
||||
activeModifiers = newModifiers;
|
||||
currentState = newState;
|
||||
|
|
@ -682,15 +677,29 @@ KeyMap::mapCharacterKey(Keystrokes& keys, KeyID id, SInt32 group,
|
|||
return &keyItem;
|
||||
}
|
||||
|
||||
void
|
||||
KeyMap::addGroupToKeystroke(Keystrokes& keys, SInt32& group, const String& lang) const
|
||||
{
|
||||
auto it = std::find(m_keyboardLayouts.begin(), m_keyboardLayouts.end(), lang);
|
||||
if (it != m_keyboardLayouts.end()) {
|
||||
LOG((CLOG_DEBUG2 "keystroke group detected as %s, group is %d", lang.c_str(), group));
|
||||
group = static_cast<int>(std::distance(m_keyboardLayouts.begin(), it));
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_DEBUG2 "could not found requested language, guessing that correct is %d", group));
|
||||
}
|
||||
keys.push_back(Keystroke(group, true, false));
|
||||
}
|
||||
|
||||
const KeyMap::KeyItem*
|
||||
KeyMap::mapModifierKey(Keystrokes& keys, KeyID id, SInt32 group,
|
||||
ModifierToKeys& activeModifiers,
|
||||
KeyModifierMask& currentState,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const
|
||||
bool isAutoRepeat, const String& lang) const
|
||||
{
|
||||
return mapCharacterKey(keys, id, group, activeModifiers,
|
||||
currentState, desiredMask, isAutoRepeat);
|
||||
currentState, desiredMask, isAutoRepeat, lang);
|
||||
}
|
||||
|
||||
SInt32
|
||||
|
|
@ -759,17 +768,16 @@ KeyMap::keysForKeyItem(const KeyItem& keyItem, SInt32& group,
|
|||
KeyModifierMask& currentState, KeyModifierMask desiredState,
|
||||
KeyModifierMask overrideModifiers,
|
||||
bool isAutoRepeat,
|
||||
Keystrokes& keystrokes) const
|
||||
Keystrokes& keystrokes,
|
||||
const String& lang) const
|
||||
{
|
||||
static const KeyModifierMask s_notRequiredMask =
|
||||
KeyModifierAltGr | KeyModifierNumLock | KeyModifierScrollLock;
|
||||
|
||||
// add keystrokes to adjust the group
|
||||
|
||||
if (App::instance().argsBase().m_enableLangSync &&
|
||||
group != keyItem.m_group) {
|
||||
if (group != keyItem.m_group) {
|
||||
group = keyItem.m_group;
|
||||
keystrokes.push_back(Keystroke(group, true, false));
|
||||
addGroupToKeystroke(keystrokes, group, lang);
|
||||
}
|
||||
|
||||
EKeystroke type;
|
||||
|
|
|
|||
|
|
@ -224,7 +224,9 @@ public:
|
|||
ModifierToKeys& activeModifiers,
|
||||
KeyModifierMask& currentState,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const;
|
||||
bool isAutoRepeat, const String& lang) const;
|
||||
|
||||
void setLanguageData(std::vector<String> layouts);
|
||||
|
||||
//! Get number of groups
|
||||
/*!
|
||||
|
|
@ -375,7 +377,8 @@ private:
|
|||
ModifierToKeys& activeModifiers,
|
||||
KeyModifierMask& currentState,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const;
|
||||
bool isAutoRepeat,
|
||||
const String& lang) const;
|
||||
|
||||
// maps a character key. a character key is trying to synthesize a
|
||||
// particular KeyID and isn't entirely concerned with the modifiers
|
||||
|
|
@ -385,15 +388,17 @@ private:
|
|||
ModifierToKeys& activeModifiers,
|
||||
KeyModifierMask& currentState,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const;
|
||||
bool isAutoRepeat,
|
||||
const String& lang) const;
|
||||
|
||||
// maps a modifier key
|
||||
const KeyItem* mapModifierKey(Keystrokes& keys,
|
||||
const KeyItem* mapModifierKey(Keystrokes& keys,
|
||||
KeyID id, SInt32 group,
|
||||
ModifierToKeys& activeModifiers,
|
||||
KeyModifierMask& currentState,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat) const;
|
||||
bool isAutoRepeat,
|
||||
const String& lang) const;
|
||||
|
||||
// returns the index into \p entryList of the KeyItemList requiring
|
||||
// the fewest modifier changes between \p currentState and
|
||||
|
|
@ -419,7 +424,8 @@ private:
|
|||
KeyModifierMask desiredState,
|
||||
KeyModifierMask overrideModifiers,
|
||||
bool isAutoRepeat,
|
||||
Keystrokes& keystrokes) const;
|
||||
Keystrokes& keystrokes,
|
||||
const String& lang) const;
|
||||
|
||||
// fills \p keystrokes with the keys to synthesize the modifiers
|
||||
// in \p desiredModifiers from the active modifiers listed in
|
||||
|
|
@ -459,6 +465,8 @@ private:
|
|||
// Initialize key name/id maps
|
||||
static void initKeyNameMaps();
|
||||
|
||||
void addGroupToKeystroke(Keystrokes& keys, SInt32& group, const String& lang) const;
|
||||
|
||||
// not implemented
|
||||
KeyMap(const KeyMap&);
|
||||
KeyMap& operator=(const KeyMap&);
|
||||
|
|
@ -505,6 +513,9 @@ private:
|
|||
// dummy KeyItem for changing modifiers
|
||||
KeyItem m_modifierKeyItem;
|
||||
|
||||
//Language sync data
|
||||
std::vector<String> m_keyboardLayouts;
|
||||
|
||||
// parsing/formatting tables
|
||||
static NameToKeyMap* s_nameToKeyMap;
|
||||
static NameToModifierMap* s_nameToModifierMap;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include "synergy/KeyState.h"
|
||||
#include "base/Log.h"
|
||||
#include "synergy/ClientApp.h"
|
||||
#include "synergy/ClientArgs.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
|
@ -383,23 +385,27 @@ static const KeyID s_numpadTable[] = {
|
|||
// KeyState
|
||||
//
|
||||
|
||||
KeyState::KeyState(IEventQueue* events) :
|
||||
KeyState::KeyState(IEventQueue* events, std::vector<String> layouts, bool isLangSyncEnabled) :
|
||||
IKeyState(events),
|
||||
m_keyMapPtr(new synergy::KeyMap()),
|
||||
m_keyMap(*m_keyMapPtr),
|
||||
m_mask(0),
|
||||
m_events(events)
|
||||
m_events(events),
|
||||
m_isLangSyncEnabled(isLangSyncEnabled)
|
||||
{
|
||||
m_keyMap.setLanguageData(std::move(layouts));
|
||||
init();
|
||||
}
|
||||
|
||||
KeyState::KeyState(IEventQueue* events, synergy::KeyMap& keyMap) :
|
||||
KeyState::KeyState(IEventQueue* events, synergy::KeyMap& keyMap, std::vector<String> layouts, bool isLangSyncEnabled) :
|
||||
IKeyState(events),
|
||||
m_keyMapPtr(0),
|
||||
m_keyMap(keyMap),
|
||||
m_mask(0),
|
||||
m_events(events)
|
||||
m_events(events),
|
||||
m_isLangSyncEnabled(isLangSyncEnabled)
|
||||
{
|
||||
m_keyMap.setLanguageData(std::move(layouts));
|
||||
init();
|
||||
}
|
||||
|
||||
|
|
@ -553,13 +559,13 @@ KeyState::setHalfDuplexMask(KeyModifierMask mask)
|
|||
}
|
||||
|
||||
void
|
||||
KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID)
|
||||
KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID, const String& lang)
|
||||
{
|
||||
// if this server key is already down then this is probably a
|
||||
// mis-reported autorepeat.
|
||||
serverID &= kButtonMask;
|
||||
if (m_serverKeys[serverID] != 0) {
|
||||
fakeKeyRepeat(id, mask, 1, serverID);
|
||||
fakeKeyRepeat(id, mask, 1, serverID, lang);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -569,13 +575,13 @@ KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID)
|
|||
return;
|
||||
}
|
||||
|
||||
// get keys for key press
|
||||
Keystrokes keys;
|
||||
ModifierToKeys oldActiveModifiers = m_activeModifiers;
|
||||
const synergy::KeyMap::KeyItem* keyItem =
|
||||
m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
|
||||
getActiveModifiersRValue(), mask, false);
|
||||
if (keyItem == NULL) {
|
||||
m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
|
||||
getActiveModifiersRValue(), mask, false, lang);
|
||||
|
||||
if (keyItem == nullptr) {
|
||||
// a media key won't be mapped on mac, so we need to fake it in a
|
||||
// special way
|
||||
if (id == kKeyAudioDown || id == kKeyAudioUp ||
|
||||
|
|
@ -586,10 +592,10 @@ KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID)
|
|||
LOG((CLOG_DEBUG1 "emulating media key"));
|
||||
fakeMediaKey(id);
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask);
|
||||
updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers);
|
||||
if (localID != 0) {
|
||||
|
|
@ -607,8 +613,9 @@ KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID)
|
|||
bool
|
||||
KeyState::fakeKeyRepeat(
|
||||
KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton serverID)
|
||||
SInt32 count, KeyButton serverID, const String& lang)
|
||||
{
|
||||
LOG((CLOG_DEBUG2 "fakeKeyRepeat"));
|
||||
serverID &= kButtonMask;
|
||||
|
||||
// if we haven't seen this button go down then ignore it
|
||||
|
|
@ -622,7 +629,7 @@ KeyState::fakeKeyRepeat(
|
|||
ModifierToKeys oldActiveModifiers = m_activeModifiers;
|
||||
const synergy::KeyMap::KeyItem* keyItem =
|
||||
m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
|
||||
getActiveModifiersRValue(), mask, true);
|
||||
getActiveModifiersRValue(), mask, true, lang);
|
||||
if (keyItem == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -874,13 +881,18 @@ KeyState::fakeKeys(const Keystrokes& keys, UInt32 count)
|
|||
// note -- k is now on the first non-repeat key after the
|
||||
// repeat keys, exactly where we'd like to continue from.
|
||||
}
|
||||
else {
|
||||
else if (k->m_type != Keystroke::kGroup ||
|
||||
(!k->m_data.m_group.m_restore && m_isLangSyncEnabled)) {
|
||||
// send event
|
||||
fakeKey(*k);
|
||||
|
||||
// next key
|
||||
++k;
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_DEBUG1 "Skipping keystroke (because language sync is disabled)"));
|
||||
++k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ platform specific methods.
|
|||
*/
|
||||
class KeyState : public IKeyState {
|
||||
public:
|
||||
KeyState(IEventQueue* events);
|
||||
KeyState(IEventQueue* events, synergy::KeyMap& keyMap);
|
||||
KeyState(IEventQueue* events, std::vector<String> layouts, bool isLangSyncEnabled);
|
||||
KeyState(IEventQueue* events, synergy::KeyMap& keyMap, std::vector<String> layouts, bool isLangSyncEnabled);
|
||||
virtual ~KeyState();
|
||||
|
||||
//! @name manipulators
|
||||
|
|
@ -70,9 +70,9 @@ public:
|
|||
void updateKeyState() override;
|
||||
void setHalfDuplexMask(KeyModifierMask) override;
|
||||
void fakeKeyDown(KeyID id, KeyModifierMask mask,
|
||||
KeyButton button) override;
|
||||
KeyButton button, const String& lang) override;
|
||||
bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button) override;
|
||||
SInt32 count, KeyButton button, const String& lang) override;
|
||||
bool fakeKeyUp(KeyButton button) override;
|
||||
void fakeAllKeysUp() override;
|
||||
bool fakeMediaKey(KeyID id) override;
|
||||
|
|
@ -233,4 +233,6 @@ private:
|
|||
KeyButton m_serverKeys[kNumButtons];
|
||||
|
||||
IEventQueue* m_events;
|
||||
|
||||
bool m_isLangSyncEnabled;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -53,16 +53,16 @@ PlatformScreen::setHalfDuplexMask(KeyModifierMask mask)
|
|||
|
||||
void
|
||||
PlatformScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
|
||||
KeyButton button)
|
||||
KeyButton button, const String& lang)
|
||||
{
|
||||
getKeyState()->fakeKeyDown(id, mask, button);
|
||||
getKeyState()->fakeKeyDown(id, mask, button, lang);
|
||||
}
|
||||
|
||||
bool
|
||||
PlatformScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button)
|
||||
SInt32 count, KeyButton button, const String& lang)
|
||||
{
|
||||
return getKeyState()->fakeKeyRepeat(id, mask, count, button);
|
||||
return getKeyState()->fakeKeyRepeat(id, mask, count, button, lang);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -63,9 +63,9 @@ public:
|
|||
virtual void updateKeyState();
|
||||
virtual void setHalfDuplexMask(KeyModifierMask);
|
||||
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
|
||||
KeyButton button);
|
||||
KeyButton button, const String&);
|
||||
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
|
||||
SInt32 count, KeyButton button);
|
||||
SInt32 count, KeyButton button, const String& lang);
|
||||
virtual bool fakeKeyUp(KeyButton button);
|
||||
virtual void fakeAllKeysUp();
|
||||
virtual bool fakeCtrlAltDel();
|
||||
|
|
|
|||
|
|
@ -279,13 +279,11 @@ ProtocolUtil::getLength(const char* fmt, va_list args)
|
|||
case 's':
|
||||
assert(len == 0);
|
||||
len = (UInt32)(va_arg(args, String*))->size() + 4;
|
||||
(void)va_arg(args, UInt8*);
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
assert(len == 0);
|
||||
len = va_arg(args, UInt32) + 4;
|
||||
(void)va_arg(args, UInt8*);
|
||||
break;
|
||||
|
||||
case '%':
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ Screen::screensaver(bool) const
|
|||
}
|
||||
|
||||
void
|
||||
Screen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
|
||||
Screen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button, const String &lang)
|
||||
{
|
||||
// check for ctrl+alt+del emulation
|
||||
if (id == kKeyDelete &&
|
||||
|
|
@ -202,15 +202,15 @@ Screen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
|
|||
return;
|
||||
}
|
||||
}
|
||||
m_screen->fakeKeyDown(id, mask, button);
|
||||
m_screen->fakeKeyDown(id, mask, button, lang);
|
||||
}
|
||||
|
||||
void
|
||||
Screen::keyRepeat(KeyID id,
|
||||
KeyModifierMask mask, SInt32 count, KeyButton button)
|
||||
KeyModifierMask mask, SInt32 count, KeyButton button, const String& lang)
|
||||
{
|
||||
assert(!m_isPrimary);
|
||||
m_screen->fakeKeyRepeat(id, mask, count, button);
|
||||
m_screen->fakeKeyRepeat(id, mask, count, button, lang);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ public:
|
|||
synthesize an up or repeat for the same client key synthesized by
|
||||
keyDown().
|
||||
*/
|
||||
void keyDown(KeyID id, KeyModifierMask, KeyButton);
|
||||
void keyDown(KeyID id, KeyModifierMask, KeyButton, const String&);
|
||||
|
||||
//! Notify of key repeat
|
||||
/*!
|
||||
|
|
@ -135,7 +135,7 @@ public:
|
|||
\c count times. If possible match the given modifier mask.
|
||||
*/
|
||||
void keyRepeat(KeyID id, KeyModifierMask,
|
||||
SInt32 count, KeyButton);
|
||||
SInt32 count, KeyButton, const String& lang);
|
||||
|
||||
//! Notify of key release
|
||||
/*!
|
||||
|
|
|
|||
233
src/lib/synergy/X11LayoutsParser.cpp
Normal file
233
src/lib/synergy/X11LayoutsParser.cpp
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2016 Symless Ltd.
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/Log.h"
|
||||
#include "synergy/X11LayoutsParser.h"
|
||||
#include "ISO639Table.h"
|
||||
#include "pugixml.hpp"
|
||||
|
||||
bool
|
||||
X11LayoutsParser::readXMLConfigItemElem(const pugi::xml_node* root, std::vector<Lang>& langList)
|
||||
{
|
||||
auto configItemElem = root->child("configItem");
|
||||
if(!configItemElem) {
|
||||
LOG((CLOG_WARN "Failed to read \"configItem\" in xml file"));
|
||||
return false;
|
||||
}
|
||||
|
||||
langList.emplace_back();
|
||||
auto nameElem = configItemElem.child("name");
|
||||
if(nameElem) {
|
||||
langList.back().name = nameElem.text().as_string();
|
||||
}
|
||||
|
||||
auto languageListElem = configItemElem.child("languageList");
|
||||
if(languageListElem) {
|
||||
for (pugi::xml_node isoElem : languageListElem.children("iso639Id")) {
|
||||
langList.back().layoutBaseISO639_2.emplace_back(isoElem.text().as_string());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<X11LayoutsParser::Lang>
|
||||
X11LayoutsParser::getAllLanguageData(const String& pathToEvdevFile)
|
||||
{
|
||||
std::vector<Lang> allCodes;
|
||||
pugi::xml_document doc;
|
||||
if(!doc.load_file(pathToEvdevFile.c_str())) {
|
||||
LOG((CLOG_WARN "Failed to open %s", pathToEvdevFile.c_str()));
|
||||
return allCodes;
|
||||
}
|
||||
|
||||
auto xkbConfigElem = doc.child("xkbConfigRegistry");
|
||||
if(!xkbConfigElem) {
|
||||
LOG((CLOG_WARN "Failed to read xkbConfigRegistry in %s", pathToEvdevFile.c_str()));
|
||||
return allCodes;
|
||||
}
|
||||
|
||||
auto layoutListElem = xkbConfigElem.child("layoutList");
|
||||
if(!layoutListElem) {
|
||||
LOG((CLOG_WARN "Failed to read layoutList in %s", pathToEvdevFile.c_str()));
|
||||
return allCodes;
|
||||
}
|
||||
|
||||
for (pugi::xml_node layoutElem : layoutListElem.children("layout")) {
|
||||
if (!readXMLConfigItemElem(&layoutElem, allCodes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto variantListElem = layoutElem.child("variantList");
|
||||
if (variantListElem) {
|
||||
for (pugi::xml_node variantElem : variantListElem.children("variant")) {
|
||||
readXMLConfigItemElem(&variantElem, allCodes.back().variants);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allCodes;
|
||||
}
|
||||
|
||||
void
|
||||
X11LayoutsParser::appendVectorUniq(const std::vector<String>& source, std::vector<String>& dst) {
|
||||
for(const auto& elem : source) {
|
||||
if(std::find_if(dst.begin(), dst.end(), [elem](const String& s) {return s == elem;}) == dst.end()) {
|
||||
dst.push_back(elem);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
X11LayoutsParser::convertLayoutToISO639_2(const String& pathToEvdevFile,
|
||||
bool needToReloadEvdev,
|
||||
std::vector<String> layoutNames,
|
||||
std::vector<String> layoutVariantNames,
|
||||
std::vector<String>& iso639_2Codes)
|
||||
{
|
||||
if(layoutNames.size() != layoutVariantNames.size()) {
|
||||
LOG((CLOG_WARN "Error in language layout or language layout variants list"));
|
||||
return;
|
||||
}
|
||||
|
||||
static std::vector<X11LayoutsParser::Lang> allLang;
|
||||
if(allLang.empty() || needToReloadEvdev) {
|
||||
allLang = getAllLanguageData(pathToEvdevFile);
|
||||
}
|
||||
for (size_t i = 0; i < layoutNames.size(); i++) {
|
||||
auto langIter = std::find_if(allLang.begin(), allLang.end(), [n=layoutNames[i]](const Lang& l) {return l.name == n;});
|
||||
if(langIter == allLang.end()) {
|
||||
LOG((CLOG_WARN "Language \"%s\" is unknown", layoutNames[i].c_str()));
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<String>* toCopy = nullptr;
|
||||
if(layoutVariantNames[i].empty()) {
|
||||
toCopy = &langIter->layoutBaseISO639_2;
|
||||
}
|
||||
else {
|
||||
auto langVariantIter = std::find_if(langIter->variants.begin(), langIter->variants.end(),
|
||||
[n=layoutVariantNames[i]](const Lang& l) {return l.name == n;});
|
||||
if(langVariantIter == langIter->variants.end()) {
|
||||
LOG((CLOG_WARN "Variant \"%s\" of language \"%s\" is unknown", layoutVariantNames[i].c_str(), layoutNames[i].c_str()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(langVariantIter->layoutBaseISO639_2.empty()) {
|
||||
toCopy = &langIter->layoutBaseISO639_2;
|
||||
}
|
||||
else {
|
||||
toCopy = &langVariantIter->layoutBaseISO639_2;
|
||||
}
|
||||
}
|
||||
|
||||
if(toCopy) {
|
||||
appendVectorUniq(*toCopy, iso639_2Codes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<String>
|
||||
X11LayoutsParser::getX11LanguageList(const String& pathToKeyboardFile, const String& pathToEvdevFile)
|
||||
{
|
||||
std::vector<String> layoutNames;
|
||||
std::vector<String> layoutVariantNames;
|
||||
std::vector<String> iso639_2Codes;
|
||||
|
||||
parseKeyboardFile(pathToKeyboardFile, layoutNames, layoutVariantNames);
|
||||
convertLayoutToISO639_2(pathToEvdevFile, true, layoutNames, layoutVariantNames, iso639_2Codes);
|
||||
return convertISO639_2ToISO639_1(iso639_2Codes);
|
||||
}
|
||||
|
||||
String
|
||||
X11LayoutsParser::convertLayotToISO(const String& pathToEvdevFile, const String& layoutLangCode, bool needToReloadFiles)
|
||||
{
|
||||
std::vector<String> iso639_2Codes;
|
||||
convertLayoutToISO639_2(pathToEvdevFile, needToReloadFiles, {layoutLangCode}, {""}, iso639_2Codes);
|
||||
if(iso639_2Codes.empty()) {
|
||||
LOG((CLOG_WARN "Failed to convert layout lang code! Code: \"%s\"", layoutLangCode.c_str()));
|
||||
return "";
|
||||
}
|
||||
|
||||
auto iso639_1Codes = convertISO639_2ToISO639_1(iso639_2Codes);
|
||||
if(iso639_1Codes.empty()) {
|
||||
LOG((CLOG_WARN "Failed to convert ISO639/2 lang code to ISO639/1!"));
|
||||
return "";
|
||||
}
|
||||
|
||||
return *iso639_1Codes.begin();
|
||||
}
|
||||
|
||||
void
|
||||
X11LayoutsParser::parseKeyboardFile(const String& pathToKeyboardFile,
|
||||
std::vector<String>& layoutNames,
|
||||
std::vector<String>& layoutVariantNames)
|
||||
{
|
||||
layoutNames.clear();
|
||||
layoutVariantNames.clear();
|
||||
|
||||
std::ifstream file(pathToKeyboardFile);
|
||||
if (!file.is_open()) {
|
||||
LOG((CLOG_WARN "x11 keyboard layouts file \"%s\" is missed.", pathToKeyboardFile.c_str()));
|
||||
return;
|
||||
}
|
||||
|
||||
const String layoutLinePrefix = "XKBLAYOUT=";
|
||||
const String variantLinePrefix = "XKBVARIANT=";
|
||||
|
||||
auto splitLine = [](std::vector<String>& splitted, const String& line, char delim) {
|
||||
std::stringstream ss(line);
|
||||
String code;
|
||||
while(ss.good()) {
|
||||
getline(ss, code, delim);
|
||||
splitted.push_back(code);
|
||||
}
|
||||
};
|
||||
|
||||
String line;
|
||||
while (std::getline(file, line)) {
|
||||
if (line.rfind(layoutLinePrefix, 0) == 0) {
|
||||
splitLine(layoutNames, line.substr(layoutLinePrefix.size()), ',');
|
||||
}
|
||||
else if (line.rfind(variantLinePrefix, 0) == 0) {
|
||||
splitLine(layoutVariantNames, line.substr(variantLinePrefix.size()), ',');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<String>
|
||||
X11LayoutsParser::convertISO639_2ToISO639_1(const std::vector<String>& iso639_2Codes)
|
||||
{
|
||||
std::vector<String> result;
|
||||
for (const auto& isoCode : iso639_2Codes) {
|
||||
const auto& tableIter = std::find_if(ISO_Table.begin(), ISO_Table.end(),
|
||||
[&isoCode](const std::pair<String, String>& c) {return c.first == isoCode;});
|
||||
if(tableIter == ISO_Table.end()) {
|
||||
LOG((CLOG_WARN "ISO 639-2 code \"%s\" is missed in table", isoCode.c_str()));
|
||||
continue;
|
||||
}
|
||||
|
||||
appendVectorUniq({tableIter->second}, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
58
src/lib/synergy/X11LayoutsParser.h
Normal file
58
src/lib/synergy/X11LayoutsParser.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2016 Symless Ltd.
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "base/String.h"
|
||||
|
||||
namespace pugi
|
||||
{
|
||||
class xml_node;
|
||||
}
|
||||
|
||||
class X11LayoutsParser {
|
||||
public:
|
||||
static std::vector<String> getX11LanguageList(const String& pathToKeyboardFile, const String& pathToEvdevFile);
|
||||
static String convertLayotToISO(const String& pathToEvdevFile, const String& layoutLangCode, bool needToReloadFiles = false);
|
||||
|
||||
private:
|
||||
struct Lang {
|
||||
String name = "";
|
||||
std::vector<String> layoutBaseISO639_2;
|
||||
std::vector<Lang> variants;
|
||||
};
|
||||
|
||||
static bool readXMLConfigItemElem(const pugi::xml_node* root,
|
||||
std::vector<Lang>& langList);
|
||||
|
||||
static std::vector<Lang> getAllLanguageData(const String& pathToEvdevFile);
|
||||
|
||||
static void appendVectorUniq(const std::vector<String>& source,
|
||||
std::vector<String>& dst);
|
||||
|
||||
static void convertLayoutToISO639_2(const String& pathToEvdevFile,
|
||||
bool needToReloadEvdev,
|
||||
std::vector<String> layoutNames,
|
||||
std::vector<String> layoutVariantNames,
|
||||
std::vector<String>& iso639_2Codes);
|
||||
|
||||
static void parseKeyboardFile(const String& pathToKeyboardFile,
|
||||
std::vector<String>& layoutNames,
|
||||
std::vector<String>& layoutVariantNames);
|
||||
|
||||
static std::vector<String> convertISO639_2ToISO639_1(const std::vector<String>& iso639_2Codes);
|
||||
};
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
#include "synergy/protocol_types.h"
|
||||
|
||||
const char* const kMsgHello = "Synergy%2i%2i";
|
||||
const char* const kMsgHelloBack = "Synergy%2i%2i%s";
|
||||
const char* const kMsgHello = "Synergy%2i%2i%s";
|
||||
const char* const kMsgHelloBack = "Synergy%2i%2i%s%s";
|
||||
const char* const kMsgCNoop = "CNOP";
|
||||
const char* const kMsgCClose = "CBYE";
|
||||
const char* const kMsgCEnter = "CINN%2i%2i%4i%2i";
|
||||
|
|
@ -29,9 +29,9 @@ const char* const kMsgCScreenSaver = "CSEC%1i";
|
|||
const char* const kMsgCResetOptions = "CROP";
|
||||
const char* const kMsgCInfoAck = "CIAK";
|
||||
const char* const kMsgCKeepAlive = "CALV";
|
||||
const char* const kMsgDKeyDown = "DKDN%2i%2i%2i";
|
||||
const char* const kMsgDKeyDown = "DKDN%2i%2i%2i%s";
|
||||
const char* const kMsgDKeyDown1_0 = "DKDN%2i%2i";
|
||||
const char* const kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i";
|
||||
const char* const kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i%s";
|
||||
const char* const kMsgDKeyRepeat1_0 = "DKRP%2i%2i%2i";
|
||||
const char* const kMsgDKeyUp = "DKUP%2i%2i%2i";
|
||||
const char* const kMsgDKeyUp1_0 = "DKUP%2i%2i";
|
||||
|
|
|
|||
|
|
@ -102,13 +102,14 @@ enum EDataReceived {
|
|||
|
||||
// say hello to client; primary -> secondary
|
||||
// $1 = protocol major version number supported by server. $2 =
|
||||
// protocol minor version number supported by server.
|
||||
// protocol minor version number supported by server. $3 = server
|
||||
// keyboard layout list.
|
||||
extern const char* const kMsgHello;
|
||||
|
||||
// respond to hello from server; secondary -> primary
|
||||
// $1 = protocol major version number supported by client. $2 =
|
||||
// protocol minor version number supported by client. $3 = client
|
||||
// name.
|
||||
// name. $4 = client language list
|
||||
extern const char* const kMsgHelloBack;
|
||||
|
||||
|
||||
|
|
@ -177,7 +178,7 @@ extern const char* const kMsgCKeepAlive;
|
|||
//
|
||||
|
||||
// key pressed: primary -> secondary
|
||||
// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton
|
||||
// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton, $4 = languageCode
|
||||
// the KeyButton identifies the physical key on the primary used to
|
||||
// generate this key. the secondary should note the KeyButton along
|
||||
// with the physical key it uses to generate the key press. on
|
||||
|
|
@ -187,6 +188,8 @@ extern const char* const kMsgCKeepAlive;
|
|||
// the press. this can happen with combining (dead) keys or if
|
||||
// the keyboard layouts are not identical and the user releases
|
||||
// a modifier key before releasing the modified key.
|
||||
// languageCode is parameter which helps client to react on unknwon
|
||||
// language letters
|
||||
extern const char* const kMsgDKeyDown;
|
||||
|
||||
// key pressed 1.0: same as above but without KeyButton
|
||||
|
|
@ -194,6 +197,7 @@ extern const char* const kMsgDKeyDown1_0;
|
|||
|
||||
// key auto-repeat: primary -> secondary
|
||||
// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton
|
||||
// $5 =language code
|
||||
extern const char* const kMsgDKeyRepeat;
|
||||
|
||||
// key auto-repeat 1.0: same as above but without KeyButton
|
||||
|
|
|
|||
|
|
@ -18,6 +18,16 @@
|
|||
|
||||
#include "synergy/unix/AppUtilUnix.h"
|
||||
#include "synergy/ArgsBase.h"
|
||||
#include <thread>
|
||||
|
||||
#if WINAPI_XWINDOWS
|
||||
#include "synergy/X11LayoutsParser.h"
|
||||
#include <X11/XKBlib.h>
|
||||
#elif WINAPI_CARBON
|
||||
#include <Carbon/Carbon.h>
|
||||
#else
|
||||
#error Platform not supported.
|
||||
#endif
|
||||
#include "base/Log.h"
|
||||
#include "base/log_outputters.h"
|
||||
|
||||
|
|
@ -51,6 +61,114 @@ AppUtilUnix::startNode()
|
|||
app().startNode();
|
||||
}
|
||||
|
||||
std::vector<String>
|
||||
AppUtilUnix::getKeyboardLayoutList()
|
||||
{
|
||||
std::vector<String> layoutLangCodes;
|
||||
|
||||
#if WINAPI_XWINDOWS
|
||||
layoutLangCodes = X11LayoutsParser::getX11LanguageList("/etc/default/keyboard", "/usr/share/X11/xkb/rules/evdev.xml");
|
||||
#elif WINAPI_CARBON
|
||||
CFStringRef keys[] = { kTISPropertyInputSourceCategory };
|
||||
CFStringRef values[] = { kTISCategoryKeyboardInputSource };
|
||||
CFDictionaryRef dict = CFDictionaryCreate(NULL, (const void **)keys, (const void **)values, 1, NULL, NULL);
|
||||
CFArrayRef kbds = TISCreateInputSourceList(dict, false);
|
||||
|
||||
for (CFIndex i = 0; i < CFArrayGetCount(kbds); ++i) {
|
||||
TISInputSourceRef keyboardLayout = (TISInputSourceRef)CFArrayGetValueAtIndex(kbds, i);
|
||||
auto layoutLanguages = (CFArrayRef)TISGetInputSourceProperty(keyboardLayout, kTISPropertyInputSourceLanguages);
|
||||
char temporaryCString[128] = {0};
|
||||
for(CFIndex index = 0; index < CFArrayGetCount(layoutLanguages) && layoutLanguages; index++) {
|
||||
auto languageCode = (CFStringRef)CFArrayGetValueAtIndex(layoutLanguages, index);
|
||||
if(!languageCode ||
|
||||
!CFStringGetCString(languageCode, temporaryCString, 128, kCFStringEncodingUTF8)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string langCode(temporaryCString);
|
||||
if(langCode.size() == 2 &&
|
||||
std::find(layoutLangCodes.begin(), layoutLangCodes.end(), langCode) == layoutLangCodes.end()){
|
||||
layoutLangCodes.push_back(langCode);
|
||||
}
|
||||
|
||||
//Save only first language code
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return layoutLangCodes;
|
||||
}
|
||||
|
||||
String
|
||||
AppUtilUnix::getCurrentLanguageCode()
|
||||
{
|
||||
String result = "";
|
||||
#if WINAPI_XWINDOWS
|
||||
|
||||
auto display = XOpenDisplay(nullptr);
|
||||
if (!display) {
|
||||
LOG((CLOG_WARN "Failed to open x11 default display"));
|
||||
return result;
|
||||
}
|
||||
|
||||
auto kbdDescr= XkbAllocKeyboard();
|
||||
if (!kbdDescr) {
|
||||
LOG((CLOG_WARN "Failed to get x11 keyboard description"));
|
||||
return result;
|
||||
}
|
||||
XkbGetNames(display, XkbSymbolsNameMask, kbdDescr);
|
||||
|
||||
Atom symNameAtom = kbdDescr->names->symbols;
|
||||
auto rawLayouts = std::string(XGetAtomName(display, symNameAtom));
|
||||
|
||||
XkbStateRec state;
|
||||
XkbGetState(display, XkbUseCoreKbd, &state);
|
||||
auto nedeedGroupIndex = static_cast<int>(state.group);
|
||||
|
||||
size_t groupIdx = 0;
|
||||
size_t groupStartI = 0;
|
||||
for(size_t strI = 0; strI < rawLayouts.size(); strI++) {
|
||||
if(rawLayouts[strI] != '+') {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto group = rawLayouts.substr(groupStartI, strI - groupStartI);
|
||||
if(group.find("group", 0, 5) == std::string::npos &&
|
||||
group.find("inet", 0, 4) == std::string::npos &&
|
||||
group.find("pc", 0, 2) == std::string::npos) {
|
||||
if(nedeedGroupIndex == groupIdx) {
|
||||
result = group.substr(0, std::min(group.find('(', 0), group.find(':', 0)));
|
||||
break;
|
||||
}
|
||||
groupIdx++;
|
||||
}
|
||||
|
||||
groupStartI = strI + 1;
|
||||
}
|
||||
|
||||
XFree(kbdDescr);
|
||||
XkbFreeNames(kbdDescr, XkbSymbolsNameMask, true);
|
||||
XCloseDisplay(display);
|
||||
|
||||
result = X11LayoutsParser::convertLayotToISO("/usr/share/X11/xkb/rules/evdev.xml", result);
|
||||
|
||||
#elif WINAPI_CARBON
|
||||
auto layoutLanguages = (CFArrayRef)TISGetInputSourceProperty(TISCopyCurrentKeyboardInputSource(), kTISPropertyInputSourceLanguages);
|
||||
char temporaryCString[128] = {0};
|
||||
for(CFIndex index = 0; index < CFArrayGetCount(layoutLanguages) && layoutLanguages; index++) {
|
||||
auto languageCode = (CFStringRef)CFArrayGetValueAtIndex(layoutLanguages, index);
|
||||
if(!languageCode || !CFStringGetCString(languageCode, temporaryCString, 128, kCFStringEncodingUTF8)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = std::string(temporaryCString);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
AppUtilUnix::showNotification(const String & title, const String & text) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,5 +31,7 @@ public:
|
|||
|
||||
int run(int argc, char** argv);
|
||||
void startNode();
|
||||
std::vector<String> getKeyboardLayoutList() override;
|
||||
String getCurrentLanguageCode() override;
|
||||
void showNotification(const String& title, const String& text) const override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,11 +33,14 @@
|
|||
#include "common/Version.h"
|
||||
#include "wintoastlib.h"
|
||||
|
||||
#include <thread>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <conio.h>
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
AppUtilWindows::AppUtilWindows(IEventQueue* events) :
|
||||
m_events(events),
|
||||
m_exitMode(kExitModeNormal)
|
||||
|
|
@ -182,6 +185,57 @@ AppUtilWindows::startNode()
|
|||
app().startNode();
|
||||
}
|
||||
|
||||
std::vector<String>
|
||||
AppUtilWindows::getKeyboardLayoutList()
|
||||
{
|
||||
std::vector<String> layoutLangCodes;
|
||||
{
|
||||
auto uLayouts = GetKeyboardLayoutList(0, NULL);
|
||||
auto lpList = (HKL*)LocalAlloc(LPTR, (uLayouts * sizeof(HKL)));
|
||||
uLayouts = GetKeyboardLayoutList(uLayouts, lpList);
|
||||
|
||||
for (int i = 0; i < uLayouts; ++i){
|
||||
String code("", 2);
|
||||
GetLocaleInfoA(MAKELCID(((UINT)lpList[i] & 0xffffffff), SORT_DEFAULT), LOCALE_SISO639LANGNAME, &code[0], code.size());
|
||||
layoutLangCodes.push_back(code);
|
||||
}
|
||||
|
||||
if (lpList) {
|
||||
LocalFree(lpList);
|
||||
}
|
||||
}
|
||||
return layoutLangCodes;
|
||||
}
|
||||
|
||||
String
|
||||
AppUtilWindows::getCurrentLanguageCode()
|
||||
{
|
||||
String code("", 2);
|
||||
|
||||
auto hklLayout = getCurrentKeyboardLayout();
|
||||
if (hklLayout) {
|
||||
auto localLayoutID = MAKELCID(LOWORD(hklLayout), SORT_DEFAULT);
|
||||
GetLocaleInfoA(localLayoutID, LOCALE_SISO639LANGNAME, &code[0], code.size());
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
HKL AppUtilWindows::getCurrentKeyboardLayout() const
|
||||
{
|
||||
HKL layout = nullptr;
|
||||
|
||||
GUITHREADINFO gti = {sizeof(GUITHREADINFO)};
|
||||
if (GetGUIThreadInfo(0, >i) && gti.hwndActive) {
|
||||
layout = GetKeyboardLayout(GetWindowThreadProcessId(gti.hwndActive, NULL));
|
||||
}
|
||||
else {
|
||||
LOG((CLOG_WARN "Failed to determine current keyboard layout"));
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
class WinToastHandler : public WinToastLib::IWinToastHandler {
|
||||
public:
|
||||
WinToastHandler() {}
|
||||
|
|
@ -225,3 +279,4 @@ AppUtilWindows::showNotification(const String & title, const String & text) cons
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "synergy/AppUtil.h"
|
||||
#include "base/String.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include "Windows.h"
|
||||
|
|
@ -54,8 +53,15 @@ public:
|
|||
|
||||
void startNode();
|
||||
|
||||
std::vector<String> getKeyboardLayoutList() override;
|
||||
|
||||
String getCurrentLanguageCode() override;
|
||||
|
||||
HKL getCurrentKeyboardLayout() const;
|
||||
|
||||
void showNotification(const String& title, const String& text) const override;
|
||||
|
||||
|
||||
private:
|
||||
AppExitMode m_exitMode;
|
||||
IEventQueue* m_events;
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ TEST_F(MSWindowsKeyStateTests, disable_eventQueueNotUsed)
|
|||
NiceMock<MockEventQueue> eventQueue;
|
||||
MSWindowsDesks* desks = newDesks(&eventQueue);
|
||||
MockKeyMap keyMap;
|
||||
MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap);
|
||||
MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap, {"en"}, true);
|
||||
|
||||
EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(0);
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ TEST_F(MSWindowsKeyStateTests, testAutoRepeat_noRepeatAndButtonIsZero_resultIsTr
|
|||
NiceMock<MockEventQueue> eventQueue;
|
||||
MSWindowsDesks* desks = newDesks(&eventQueue);
|
||||
MockKeyMap keyMap;
|
||||
MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap);
|
||||
MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap, {"en"}, true);
|
||||
keyState.setLastDown(1);
|
||||
|
||||
bool actual = keyState.testAutoRepeat(true, false, 1);
|
||||
|
|
@ -100,7 +100,7 @@ TEST_F(MSWindowsKeyStateTests, testAutoRepeat_pressFalse_lastDownIsZero)
|
|||
NiceMock<MockEventQueue> eventQueue;
|
||||
MSWindowsDesks* desks = newDesks(&eventQueue);
|
||||
MockKeyMap keyMap;
|
||||
MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap);
|
||||
MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap, {"en"}, true);
|
||||
keyState.setLastDown(1);
|
||||
|
||||
keyState.testAutoRepeat(false, false, 1);
|
||||
|
|
@ -114,7 +114,7 @@ TEST_F(MSWindowsKeyStateTests, saveModifiers_noModifiers_savedModifiers0)
|
|||
NiceMock<MockEventQueue> eventQueue;
|
||||
MSWindowsDesks* desks = newDesks(&eventQueue);
|
||||
MockKeyMap keyMap;
|
||||
MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap);
|
||||
MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap, {"en"}, true);
|
||||
|
||||
keyState.saveModifiers();
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ TEST_F(MSWindowsKeyStateTests, testKoreanLocale_inputModeKey_resultCorrectKeyID)
|
|||
NiceMock<MockEventQueue> eventQueue;
|
||||
MSWindowsDesks* desks = newDesks(&eventQueue);
|
||||
MockKeyMap keyMap;
|
||||
MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap);
|
||||
MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap, {"en"}, true);
|
||||
|
||||
keyState.setKeyLayout((HKL)0x00000412u); // for ko-KR local ID
|
||||
ASSERT_EQ(0xEF31, keyState.getKeyID(0x15u, 0x1f2u)); // VK_HANGUL from Hangul key
|
||||
|
|
|
|||
|
|
@ -42,16 +42,16 @@ TEST_F(OSXKeyStateTests, fakeAndPoll_shift)
|
|||
{
|
||||
synergy::KeyMap keyMap;
|
||||
MockEventQueue eventQueue;
|
||||
OSXKeyState keyState(&eventQueue, keyMap);
|
||||
OSXKeyState keyState(&eventQueue, keyMap, {"en"}, true);
|
||||
keyState.updateKeyMap();
|
||||
|
||||
keyState.fakeKeyDown(SHIFT_ID_L, 0, 1);
|
||||
keyState.fakeKeyDown(SHIFT_ID_L, 0, 1, "en");
|
||||
EXPECT_TRUE(isKeyPressed(keyState, SHIFT_BUTTON));
|
||||
|
||||
keyState.fakeKeyUp(1);
|
||||
EXPECT_TRUE(!isKeyPressed(keyState, SHIFT_BUTTON));
|
||||
|
||||
keyState.fakeKeyDown(SHIFT_ID_R, 0, 2);
|
||||
keyState.fakeKeyDown(SHIFT_ID_R, 0, 2, "en");
|
||||
EXPECT_TRUE(isKeyPressed(keyState, SHIFT_BUTTON));
|
||||
|
||||
keyState.fakeKeyUp(2);
|
||||
|
|
@ -62,10 +62,10 @@ TEST_F(OSXKeyStateTests, fakeAndPoll_charKey)
|
|||
{
|
||||
synergy::KeyMap keyMap;
|
||||
MockEventQueue eventQueue;
|
||||
OSXKeyState keyState(&eventQueue, keyMap);
|
||||
OSXKeyState keyState(&eventQueue, keyMap, {"en"}, true);
|
||||
keyState.updateKeyMap();
|
||||
|
||||
keyState.fakeKeyDown(A_CHAR_ID, 0, 1);
|
||||
keyState.fakeKeyDown(A_CHAR_ID, 0, 1, "en");
|
||||
EXPECT_TRUE(isKeyPressed(keyState, A_CHAR_BUTTON));
|
||||
|
||||
keyState.fakeKeyUp(1);
|
||||
|
|
@ -73,7 +73,7 @@ TEST_F(OSXKeyStateTests, fakeAndPoll_charKey)
|
|||
|
||||
// HACK: delete the key in case it was typed into a text editor.
|
||||
// we should really set focus to an invisible window.
|
||||
keyState.fakeKeyDown(kKeyBackSpace, 0, 2);
|
||||
keyState.fakeKeyDown(kKeyBackSpace, 0, 2, "en");
|
||||
keyState.fakeKeyUp(2);
|
||||
}
|
||||
|
||||
|
|
@ -81,10 +81,10 @@ TEST_F(OSXKeyStateTests, fakeAndPoll_charKeyAndModifier)
|
|||
{
|
||||
synergy::KeyMap keyMap;
|
||||
MockEventQueue eventQueue;
|
||||
OSXKeyState keyState(&eventQueue, keyMap);
|
||||
OSXKeyState keyState(&eventQueue, keyMap, {"en"}, true);
|
||||
keyState.updateKeyMap();
|
||||
|
||||
keyState.fakeKeyDown(A_CHAR_ID, KeyModifierShift, 1);
|
||||
keyState.fakeKeyDown(A_CHAR_ID, KeyModifierShift, 1, "en");
|
||||
EXPECT_TRUE(isKeyPressed(keyState, A_CHAR_BUTTON));
|
||||
|
||||
keyState.fakeKeyUp(1);
|
||||
|
|
@ -92,7 +92,7 @@ TEST_F(OSXKeyStateTests, fakeAndPoll_charKeyAndModifier)
|
|||
|
||||
// HACK: delete the key in case it was typed into a text editor.
|
||||
// we should really set focus to an invisible window.
|
||||
keyState.fakeKeyDown(kKeyBackSpace, 0, 2);
|
||||
keyState.fakeKeyDown(kKeyBackSpace, 0, 2, "en");
|
||||
keyState.fakeKeyUp(2);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/String.h"
|
||||
#include "synergy/KeyMap.h"
|
||||
|
||||
#include "test/global/gmock.h"
|
||||
|
|
@ -31,5 +31,5 @@ public:
|
|||
MOCK_METHOD(void, addHalfDuplexModifier, (KeyID), (override));
|
||||
MOCK_METHOD(bool, isHalfDuplex, (KeyID, KeyButton), (const, override));
|
||||
MOCK_METHOD(const KeyMap::KeyItem*, mapKey, (Keystrokes&, KeyID, SInt32, ModifierToKeys&, KeyModifierMask&,
|
||||
KeyModifierMask, bool), (const, override));
|
||||
KeyModifierMask, bool, const String&), (const, override));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ class MockKeyState : public KeyState
|
|||
{
|
||||
public:
|
||||
MockKeyState(const MockEventQueue& eventQueue) :
|
||||
KeyState((IEventQueue*)&eventQueue)
|
||||
KeyState((IEventQueue*)&eventQueue, {"en"}, true)
|
||||
{
|
||||
}
|
||||
|
||||
MockKeyState(const MockEventQueue& eventQueue, const synergy::KeyMap& keyMap) :
|
||||
KeyState((IEventQueue*)&eventQueue, (synergy::KeyMap&)keyMap)
|
||||
KeyState((IEventQueue*)&eventQueue, (synergy::KeyMap&)keyMap, {"en"}, true)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ TEST(OSXKeyStateTests, mapModifiersFromOSX_OSXMask_returnSynergyMask)
|
|||
{
|
||||
synergy::KeyMap keyMap;
|
||||
MockEventQueue eventQueue;
|
||||
OSXKeyState keyState(&eventQueue, keyMap);
|
||||
OSXKeyState keyState(&eventQueue, keyMap, { "en" }, true);
|
||||
|
||||
KeyModifierMask outMask = 0;
|
||||
|
||||
|
|
|
|||
39
src/test/unittests/synergy/AppUtilTests.cpp
Normal file
39
src/test/unittests/synergy/AppUtilTests.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2014-2016 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "test/global/gtest.h"
|
||||
#include "synergy/AppUtil.h"
|
||||
|
||||
TEST(AppUtilTests, keyboardLayoutTest)
|
||||
{
|
||||
String layoutList1 = "ruenby";
|
||||
std::vector<String> layoutList2 = { "ru", "en" };
|
||||
std::vector<String> missed;
|
||||
std::vector<String> supported;
|
||||
AppUtil::getKeyboardLayoutsDiff(layoutList1, layoutList2, missed, supported);
|
||||
|
||||
EXPECT_EQ(missed, std::vector<String>{ "by" });
|
||||
EXPECT_EQ(supported, layoutList2);
|
||||
}
|
||||
|
||||
TEST(AppUtilTests, joinVectorTest)
|
||||
{
|
||||
std::vector<String> layouts = { "ru", "en" };
|
||||
auto result = AppUtil::joinStrVector(layouts, ", ");
|
||||
|
||||
EXPECT_EQ(result, "ru, en");
|
||||
}
|
||||
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include "test/global/gtest.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::NiceMock;
|
||||
|
|
@ -51,6 +53,42 @@ TEST(ClientArgsParsingTests, parseClientArgs_yScrollArg_setYScroll)
|
|||
EXPECT_EQ(1, clientArgs.m_yscroll);
|
||||
}
|
||||
|
||||
TEST(ClientArgsParsingTests, parseClientArgs_setLangSync)
|
||||
{
|
||||
NiceMock<MockArgParser> argParser;
|
||||
ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(client_stubParseGenericArgs));
|
||||
ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(client_stubCheckUnexpectedArgs));
|
||||
lib::synergy::ClientArgs clientArgs;
|
||||
clientArgs.m_enableLangSync = false;
|
||||
const int argc = 2;
|
||||
std::array<const char*, argc> kLangCmd = { "stub", "--sync-language" };
|
||||
|
||||
argParser.parseClientArgs(clientArgs, argc, kLangCmd.data());
|
||||
|
||||
EXPECT_TRUE(clientArgs.m_enableLangSync);
|
||||
}
|
||||
|
||||
TEST(ClientArgsParsingTests, parseClientArgs_setCommonArgs)
|
||||
{
|
||||
NiceMock<MockArgParser> argParser;
|
||||
ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(client_stubParseGenericArgs));
|
||||
ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(client_stubCheckUnexpectedArgs));
|
||||
lib::synergy::ClientArgs clientArgs;
|
||||
clientArgs.m_enableLangSync = false;
|
||||
const int argc = 9;
|
||||
std::array<const char*, argc> kLangCmd = { "stub", "--enable-crypto", "--profile-dir", "profileDir",
|
||||
"--plugin-dir", "pluginDir", "--tls-cert", "tlsCertPath",
|
||||
"--prevent-sleep" };
|
||||
|
||||
argParser.parseClientArgs(clientArgs, argc, kLangCmd.data());
|
||||
|
||||
EXPECT_TRUE(clientArgs.m_enableCrypto);
|
||||
EXPECT_EQ(clientArgs.m_profileDirectory, "profileDir");
|
||||
EXPECT_EQ(clientArgs.m_pluginDirectory, "pluginDir");
|
||||
EXPECT_EQ(clientArgs.m_tlsCertFile, "tlsCertPath");
|
||||
EXPECT_TRUE(clientArgs.m_preventSleep);
|
||||
}
|
||||
|
||||
TEST(ClientArgsParsingTests, parseClientArgs_addressArg_setSynergyAddress)
|
||||
{
|
||||
NiceMock<MockArgParser> argParser;
|
||||
|
|
|
|||
|
|
@ -229,10 +229,10 @@ TEST(KeyMapTests, mapkey_handles_setmodifier_with_no_mapped)
|
|||
KeyMap::ModifierToKeys activeModifiers {};
|
||||
KeyModifierMask currentState {};
|
||||
KeyModifierMask desiredMask {};
|
||||
auto result = keyMap.mapKey(strokes, kKeySetModifiers, 1, activeModifiers, currentState, desiredMask, false);
|
||||
auto result = keyMap.mapKey(strokes, kKeySetModifiers, 1, activeModifiers, currentState, desiredMask, false, "en");
|
||||
EXPECT_FALSE(result == nullptr);
|
||||
desiredMask = KeyModifierControl;
|
||||
result = keyMap.mapKey(strokes, kKeySetModifiers, 1, activeModifiers, currentState, desiredMask, false);
|
||||
result = keyMap.mapKey(strokes, kKeySetModifiers, 1, activeModifiers, currentState, desiredMask, false, "en");
|
||||
EXPECT_TRUE(result == nullptr);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ stubMapKey(
|
|||
synergy::KeyMap::ModifierToKeys& activeModifiers,
|
||||
KeyModifierMask& currentState,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat);
|
||||
bool isAutoRepeat,
|
||||
const String& lang);
|
||||
|
||||
synergy::KeyMap::Keystroke s_stubKeystroke(1, false, false);
|
||||
synergy::KeyMap::KeyItem s_stubKeyItem;
|
||||
|
|
@ -100,7 +101,7 @@ TEST(KeyStateTests, sendKeyEvent_halfDuplex_addEventCalledTwice)
|
|||
KeyStateImpl keyState(eventQueue, keyMap);
|
||||
IKeyStateEvents keyStateEvents;
|
||||
keyStateEvents.setEvents(&eventQueue);
|
||||
|
||||
|
||||
ON_CALL(keyMap, isHalfDuplex(_, _)).WillByDefault(Return(true));
|
||||
ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents));
|
||||
|
||||
|
|
@ -116,7 +117,7 @@ TEST(KeyStateTests, sendKeyEvent_keyRepeat_addEventCalledOnce)
|
|||
KeyStateImpl keyState(eventQueue, keyMap);
|
||||
IKeyStateEvents keyStateEvents;
|
||||
keyStateEvents.setEvents(&eventQueue);
|
||||
|
||||
|
||||
ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents));
|
||||
|
||||
EXPECT_CALL(eventQueue, addEvent(_)).Times(1);
|
||||
|
|
@ -131,7 +132,7 @@ TEST(KeyStateTests, sendKeyEvent_keyDown_addEventCalledOnce)
|
|||
KeyStateImpl keyState(eventQueue, keyMap);
|
||||
IKeyStateEvents keyStateEvents;
|
||||
keyStateEvents.setEvents(&eventQueue);
|
||||
|
||||
|
||||
ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents));
|
||||
|
||||
EXPECT_CALL(eventQueue, addEvent(_)).Times(1);
|
||||
|
|
@ -146,7 +147,7 @@ TEST(KeyStateTests, sendKeyEvent_keyUp_addEventCalledOnce)
|
|||
KeyStateImpl keyState(eventQueue, keyMap);
|
||||
IKeyStateEvents keyStateEvents;
|
||||
keyStateEvents.setEvents(&eventQueue);
|
||||
|
||||
|
||||
ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents));
|
||||
|
||||
EXPECT_CALL(eventQueue, addEvent(_)).Times(1);
|
||||
|
|
@ -270,15 +271,15 @@ TEST(KeyStateTests, fakeKeyDown_serverKeyAlreadyDown_fakeKeyCalledTwice)
|
|||
KeyStateImpl keyState(eventQueue, keyMap);
|
||||
s_stubKeyItem.m_client = 0;
|
||||
s_stubKeyItem.m_button = 1;
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
|
||||
// 2 calls to fakeKeyDown should still call fakeKey, even though
|
||||
// repeated keys are handled differently.
|
||||
EXPECT_CALL(keyState, fakeKey(_)).Times(2);
|
||||
|
||||
// call twice to simulate server key already down (a misreported autorepeat).
|
||||
keyState.fakeKeyDown(1, 0, 0);
|
||||
keyState.fakeKeyDown(1, 0, 0);
|
||||
keyState.fakeKeyDown(1, 0, 0, "en");
|
||||
keyState.fakeKeyDown(1, 0, 0, "en");
|
||||
}
|
||||
|
||||
TEST(KeyStateTests, fakeKeyDown_isIgnoredKey_fakeKeyNotCalled)
|
||||
|
|
@ -289,7 +290,7 @@ TEST(KeyStateTests, fakeKeyDown_isIgnoredKey_fakeKeyNotCalled)
|
|||
|
||||
EXPECT_CALL(keyState, fakeKey(_)).Times(0);
|
||||
|
||||
keyState.fakeKeyDown(kKeyCapsLock, 0, 0);
|
||||
keyState.fakeKeyDown(kKeyCapsLock, 0, 0, "en");
|
||||
}
|
||||
|
||||
TEST(KeyStateTests, fakeKeyDown_mapReturnsKeystrokes_fakeKeyCalled)
|
||||
|
|
@ -299,11 +300,11 @@ TEST(KeyStateTests, fakeKeyDown_mapReturnsKeystrokes_fakeKeyCalled)
|
|||
KeyStateImpl keyState(eventQueue, keyMap);
|
||||
s_stubKeyItem.m_button = 0;
|
||||
s_stubKeyItem.m_client = 0;
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
|
||||
EXPECT_CALL(keyState, fakeKey(_)).Times(1);
|
||||
|
||||
keyState.fakeKeyDown(1, 0, 0);
|
||||
keyState.fakeKeyDown(1, 0, 0, "en");
|
||||
}
|
||||
|
||||
TEST(KeyStateTests, fakeKeyRepeat_invalidKey_returnsFalse)
|
||||
|
|
@ -312,7 +313,7 @@ TEST(KeyStateTests, fakeKeyRepeat_invalidKey_returnsFalse)
|
|||
MockEventQueue eventQueue;
|
||||
KeyStateImpl keyState(eventQueue, keyMap);
|
||||
|
||||
bool actual = keyState.fakeKeyRepeat(0, 0, 0, 0);
|
||||
bool actual = keyState.fakeKeyRepeat(0, 0, 0, 0, "en");
|
||||
|
||||
ASSERT_FALSE(actual);
|
||||
}
|
||||
|
|
@ -327,14 +328,14 @@ TEST(KeyStateTests, fakeKeyRepeat_nullKey_returnsFalse)
|
|||
synergy::KeyMap::KeyItem keyItem;
|
||||
keyItem.m_client = 0;
|
||||
keyItem.m_button = 1;
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
|
||||
keyState.fakeKeyDown(1, 0, 0);
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
|
||||
keyState.fakeKeyDown(1, 0, 0, "en");
|
||||
|
||||
// change mapKey to return NULL so that fakeKeyRepeat exits early.
|
||||
synergy::KeyMap::KeyItem* nullKeyItem = NULL;
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(nullKeyItem));
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Return(nullKeyItem));
|
||||
|
||||
bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0);
|
||||
bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0, "en");
|
||||
|
||||
ASSERT_FALSE(actual);
|
||||
}
|
||||
|
|
@ -349,14 +350,14 @@ TEST(KeyStateTests, fakeKeyRepeat_invalidButton_returnsFalse)
|
|||
synergy::KeyMap::KeyItem keyItem;
|
||||
keyItem.m_client = 0;
|
||||
keyItem.m_button = 1; // set to 1 to make fakeKeyDown work.
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
|
||||
keyState.fakeKeyDown(1, 0, 0);
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
|
||||
keyState.fakeKeyDown(1, 0, 0, "en");
|
||||
|
||||
// change button to 0 so that fakeKeyRepeat will return early.
|
||||
keyItem.m_button = 0;
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
|
||||
|
||||
bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0);
|
||||
bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0, "en");
|
||||
|
||||
ASSERT_FALSE(actual);
|
||||
}
|
||||
|
|
@ -372,14 +373,14 @@ TEST(KeyStateTests, fakeKeyRepeat_validKey_returnsTrue)
|
|||
|
||||
// set the button to 1 for fakeKeyDown call
|
||||
s_stubKeyItem.m_button = 1;
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
keyState.fakeKeyDown(1, 0, 0);
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
keyState.fakeKeyDown(1, 0, 0, "en");
|
||||
|
||||
// change the button to 2
|
||||
s_stubKeyItem.m_button = 2;
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
|
||||
bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0);
|
||||
bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0, "en");
|
||||
|
||||
ASSERT_TRUE(actual);
|
||||
}
|
||||
|
|
@ -407,8 +408,8 @@ TEST(KeyStateTests, fakeKeyUp_buttonAlreadyDown_returnsTrue)
|
|||
|
||||
// press button 1 down.
|
||||
s_stubKeyItem.m_button = 1;
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
keyState.fakeKeyDown(1, 0, 1);
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
keyState.fakeKeyDown(1, 0, 1, "en");
|
||||
|
||||
// this takes the button id, which is the 3rd arg of fakeKeyDown
|
||||
bool actual = keyState.fakeKeyUp(1);
|
||||
|
|
@ -424,8 +425,8 @@ TEST(KeyStateTests, fakeAllKeysUp_keysWereDown_keysAreUp)
|
|||
|
||||
// press button 1 down.
|
||||
s_stubKeyItem.m_button = 1;
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
keyState.fakeKeyDown(1, 0, 1);
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
keyState.fakeKeyDown(1, 0, 1, "en");
|
||||
|
||||
// method under test
|
||||
keyState.fakeAllKeysUp();
|
||||
|
|
@ -442,8 +443,8 @@ TEST(KeyStateTests, isKeyDown_keyDown_returnsTrue)
|
|||
|
||||
// press button 1 down.
|
||||
s_stubKeyItem.m_button = 1;
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
keyState.fakeKeyDown(1, 0, 1);
|
||||
ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
|
||||
keyState.fakeKeyDown(1, 0, 1, "en");
|
||||
|
||||
// method under test
|
||||
bool actual = keyState.isKeyDown(1);
|
||||
|
|
@ -492,12 +493,9 @@ assertMaskIsOne(ForeachKeyCallback cb, void* userData)
|
|||
}
|
||||
|
||||
const synergy::KeyMap::KeyItem*
|
||||
stubMapKey(
|
||||
synergy::KeyMap::Keystrokes& keys, KeyID id, SInt32 group,
|
||||
synergy::KeyMap::ModifierToKeys& activeModifiers,
|
||||
KeyModifierMask& currentState,
|
||||
KeyModifierMask desiredMask,
|
||||
bool isAutoRepeat)
|
||||
stubMapKey(synergy::KeyMap::Keystrokes& keys, KeyID, SInt32,
|
||||
synergy::KeyMap::ModifierToKeys&, KeyModifierMask&,
|
||||
KeyModifierMask, bool, const String&)
|
||||
{
|
||||
keys.push_back(s_stubKeystroke);
|
||||
return &s_stubKeyItem;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include "test/global/gtest.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::NiceMock;
|
||||
|
|
@ -72,3 +74,29 @@ TEST(ServerArgsParsingTests, parseServerArgs_configArg_setConfigFile)
|
|||
|
||||
EXPECT_EQ("mock_configFile", serverArgs.m_configFile);
|
||||
}
|
||||
|
||||
TEST(ServerArgsParsingTests, parseServerArgs_checkSerialKeyParams)
|
||||
{
|
||||
NiceMock<MockArgParser> argParser;
|
||||
ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(server_stubParseGenericArgs));
|
||||
ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(server_stubCheckUnexpectedArgs));
|
||||
lib::synergy::ServerArgs serverArgs;
|
||||
const int argc = 3;
|
||||
const char* serial = "7B76323B737562736372697074696F6E3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D";
|
||||
std::array<const char*, argc> kSerialCmd = { "stub", "--serial-key", serial };
|
||||
|
||||
argParser.parseServerArgs(serverArgs, argc, kSerialCmd.data());
|
||||
EXPECT_EQ(serial, serverArgs.m_serial.toString());
|
||||
}
|
||||
|
||||
TEST(ServerArgsParsingTests, parseServerArgs_checkUnexpectedParams)
|
||||
{
|
||||
NiceMock<MockArgParser> argParser;
|
||||
ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(server_stubParseGenericArgs));
|
||||
ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(server_stubCheckUnexpectedArgs));
|
||||
lib::synergy::ServerArgs serverArgs;
|
||||
const int argc = 2;
|
||||
std::array<const char*, argc> kUnknownCmd = { "stub", "--unknown" };
|
||||
|
||||
EXPECT_FALSE(argParser.parseServerArgs(serverArgs, argc, kUnknownCmd.data()));
|
||||
}
|
||||
|
|
|
|||
221
src/test/unittests/synergy/X11LayoutParserTests.cpp
Normal file
221
src/test/unittests/synergy/X11LayoutParserTests.cpp
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2014-2016 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "synergy/X11LayoutsParser.h"
|
||||
#include "test/global/gtest.h"
|
||||
#include <fstream>
|
||||
|
||||
void createTestFiles()
|
||||
{
|
||||
std::ofstream correctKeyboardFile ("correctKeyboard");
|
||||
if(!correctKeyboardFile.is_open()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
correctKeyboardFile << "XKBLAYOUT=us,ru" << std::endl;
|
||||
correctKeyboardFile << "XKBVARIANT=eng," << std::endl;
|
||||
correctKeyboardFile << "BACKSPACE=guess" << std::endl;
|
||||
correctKeyboardFile.close();
|
||||
|
||||
std::ofstream keyboardFromFutureFile ("keyboardFromFuture");
|
||||
if(!keyboardFromFutureFile.is_open()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
keyboardFromFutureFile << "XKBLAYOUT=futureLangName" << std::endl;
|
||||
keyboardFromFutureFile << "XKBVARIANT=" << std::endl;
|
||||
keyboardFromFutureFile.close();
|
||||
|
||||
std::ofstream incorrectKeyboardFile1 ("incorrectKeyboard1");
|
||||
if(!incorrectKeyboardFile1.is_open()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
incorrectKeyboardFile1 << "XKBLAYOUT=unknownLangCode" << std::endl;
|
||||
incorrectKeyboardFile1 << "XKBVARIANT=eng" << std::endl;
|
||||
incorrectKeyboardFile1 << "BACKSPACE=guess" << std::endl;
|
||||
incorrectKeyboardFile1.close();
|
||||
|
||||
std::ofstream incorrectKeyboardFile2 ("incorrectKeyboard2");
|
||||
if(!incorrectKeyboardFile2.is_open()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
incorrectKeyboardFile2 << "XKBLAYOUT=us" << std::endl;
|
||||
incorrectKeyboardFile2 << "XKBVARIANT=unknownLangVariant" << std::endl;
|
||||
incorrectKeyboardFile2 << "BACKSPACE=guess" << std::endl;
|
||||
incorrectKeyboardFile2.close();
|
||||
|
||||
std::ofstream incorrectKeyboardFile3 ("incorrectKeyboard3");
|
||||
if(!incorrectKeyboardFile3.is_open()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
incorrectKeyboardFile3 << "XKBLAYOUT=us,ru,by" << std::endl;
|
||||
incorrectKeyboardFile3 << "XKBVARIANT=," << std::endl;
|
||||
incorrectKeyboardFile3.close();
|
||||
|
||||
std::ofstream correctEvdevFile ("correctEvdev.xml");
|
||||
if(!correctEvdevFile.is_open()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
correctEvdevFile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
|
||||
correctEvdevFile << "<xkbConfigRegistry version=\"1.1\">" << std::endl;
|
||||
correctEvdevFile << " <layoutList>" << std::endl;
|
||||
correctEvdevFile << " <layout>" << std::endl;
|
||||
correctEvdevFile << " <configItem>" << std::endl;
|
||||
correctEvdevFile << " <name>us</name>" << std::endl;
|
||||
correctEvdevFile << " <!-- Keyboard indicator for English layouts -->" << std::endl;
|
||||
correctEvdevFile << " <shortDescription>en</shortDescription>" << std::endl;
|
||||
correctEvdevFile << " <description>English (US)</description>" << std::endl;
|
||||
correctEvdevFile << " <languageList>" << std::endl;
|
||||
correctEvdevFile << " <iso639Id>eng</iso639Id>" << std::endl;
|
||||
correctEvdevFile << " </languageList>" << std::endl;
|
||||
correctEvdevFile << " </configItem>" << std::endl;
|
||||
correctEvdevFile << " <variantList>" << std::endl;
|
||||
correctEvdevFile << " <variant>" << std::endl;
|
||||
correctEvdevFile << " <configItem>" << std::endl;
|
||||
correctEvdevFile << " <name>eng</name>" << std::endl;
|
||||
correctEvdevFile << " <shortDescription>eng</shortDescription>" << std::endl;
|
||||
correctEvdevFile << " <description>Cherokee</description>" << std::endl;
|
||||
correctEvdevFile << " <languageList>" << std::endl;
|
||||
correctEvdevFile << " <iso639Id>eng</iso639Id>" << std::endl;
|
||||
correctEvdevFile << " </languageList>" << std::endl;
|
||||
correctEvdevFile << " </configItem>" << std::endl;
|
||||
correctEvdevFile << " </variant>" << std::endl;
|
||||
correctEvdevFile << " </variantList>" << std::endl;
|
||||
correctEvdevFile << " </layout>" << std::endl;
|
||||
correctEvdevFile << " <layout>" << std::endl;
|
||||
correctEvdevFile << " <configItem>" << std::endl;
|
||||
correctEvdevFile << " <name>ru</name>" << std::endl;
|
||||
correctEvdevFile << " <!-- Keyboard indicator for Russian layouts -->" << std::endl;
|
||||
correctEvdevFile << " <shortDescription>ru</shortDescription>" << std::endl;
|
||||
correctEvdevFile << " <description>Russian</description>" << std::endl;
|
||||
correctEvdevFile << " <languageList>" << std::endl;
|
||||
correctEvdevFile << " <iso639Id>rus</iso639Id>" << std::endl;
|
||||
correctEvdevFile << " </languageList>" << std::endl;
|
||||
correctEvdevFile << " </configItem>" << std::endl;
|
||||
correctEvdevFile << " </layout>" << std::endl;
|
||||
correctEvdevFile << " </layoutList>" << std::endl;
|
||||
correctEvdevFile << "</xkbConfigRegistry>" << std::endl;
|
||||
correctEvdevFile.close();
|
||||
|
||||
std::ofstream evdevFromFutureFile ("evdevFromFuture.xml");
|
||||
if(!evdevFromFutureFile.is_open()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
evdevFromFutureFile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
|
||||
evdevFromFutureFile << "<xkbConfigRegistry version=\"1.1\">" << std::endl;
|
||||
evdevFromFutureFile << " <layoutList>" << std::endl;
|
||||
evdevFromFutureFile << " <layout>" << std::endl;
|
||||
evdevFromFutureFile << " <configItem>" << std::endl;
|
||||
evdevFromFutureFile << " <name>futureLangName</name>" << std::endl;
|
||||
evdevFromFutureFile << " <languageList>" << std::endl;
|
||||
evdevFromFutureFile << " <iso639Id>fln</iso639Id>" << std::endl;
|
||||
evdevFromFutureFile << " </languageList>" << std::endl;
|
||||
evdevFromFutureFile << " </configItem>" << std::endl;
|
||||
evdevFromFutureFile << " </layout>" << std::endl;
|
||||
evdevFromFutureFile << " </layoutList>" << std::endl;
|
||||
evdevFromFutureFile << "</xkbConfigRegistry>" << std::endl;
|
||||
evdevFromFutureFile.close();
|
||||
|
||||
std::ofstream incorrectEvdevFile1 ("incorrectEvdev1.xml");
|
||||
if(!incorrectEvdevFile1.is_open()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
incorrectEvdevFile1 << "<incorrectRootTag></incorrectRootTag>" << std::endl;
|
||||
incorrectEvdevFile1.close();
|
||||
|
||||
std::ofstream incorrectEvdevFile2 ("incorrectEvdev2.xml");
|
||||
if(!incorrectEvdevFile2.is_open()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
incorrectEvdevFile2 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
|
||||
incorrectEvdevFile2 << "<xkbConfigRegistry version=\"1.1\">" << std::endl;
|
||||
incorrectEvdevFile2 << "</xkbConfigRegistry>" << std::endl;
|
||||
incorrectEvdevFile2.close();
|
||||
|
||||
std::ofstream incorrectEvdevFile3 ("incorrectEvdev3.xml");
|
||||
if(!incorrectEvdevFile3.is_open()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
incorrectEvdevFile3 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
|
||||
incorrectEvdevFile3 << "<xkbConfigRegistry version=\"1.1\">" << std::endl;
|
||||
incorrectEvdevFile3 << " <layoutList>" << std::endl;
|
||||
incorrectEvdevFile3 << " <layout>" << std::endl;
|
||||
incorrectEvdevFile3 << " </layout>" << std::endl;
|
||||
incorrectEvdevFile3 << " </layoutList>" << std::endl;
|
||||
incorrectEvdevFile3 << "</xkbConfigRegistry>" << std::endl;
|
||||
incorrectEvdevFile3.close();
|
||||
}
|
||||
|
||||
TEST(X11LayoutsParsingTests, xmlCorrectParsingTest)
|
||||
{
|
||||
createTestFiles();
|
||||
std::vector<String> expectedResult = { "en", "ru" };
|
||||
auto parsedResult = X11LayoutsParser::getX11LanguageList("correctKeyboard", "correctEvdev.xml");
|
||||
|
||||
EXPECT_EQ(parsedResult, parsedResult);
|
||||
}
|
||||
|
||||
TEST(X11LayoutsParsingTests, xmlParsingMissedKeyboardFileTest)
|
||||
{
|
||||
auto parsedResult = X11LayoutsParser::getX11LanguageList("missedFile", "correctEvdev.xml");
|
||||
EXPECT_TRUE(parsedResult.empty());
|
||||
}
|
||||
|
||||
TEST(X11LayoutsParsingTests, xmlParsingMissedEvdevFileTest)
|
||||
{
|
||||
auto parsedResult = X11LayoutsParser::getX11LanguageList("correctKeyboard", "missedFile");
|
||||
EXPECT_TRUE(parsedResult.empty());
|
||||
}
|
||||
|
||||
TEST(X11LayoutsParsingTests, xmlParsingIncorrectEvdevFileTest)
|
||||
{
|
||||
std::vector<String> parsedResult;
|
||||
parsedResult = X11LayoutsParser::getX11LanguageList("correctKeyboard", "incorrectEvdev1.xml");
|
||||
EXPECT_TRUE(parsedResult.empty());
|
||||
parsedResult = X11LayoutsParser::getX11LanguageList("correctKeyboard", "incorrectEvdev2.xml");
|
||||
EXPECT_TRUE(parsedResult.empty());
|
||||
parsedResult = X11LayoutsParser::getX11LanguageList("correctKeyboard", "incorrectEvdev3.xml");
|
||||
EXPECT_TRUE(parsedResult.empty());
|
||||
}
|
||||
|
||||
TEST(X11LayoutsParsingTests, xmlParsingIncorrectKeyboardFileTest)
|
||||
{
|
||||
std::vector<String> parsedResult;
|
||||
parsedResult = X11LayoutsParser::getX11LanguageList("incorrectKeyboard1", "correctEvdev.xml");
|
||||
EXPECT_TRUE(parsedResult.empty());
|
||||
parsedResult = X11LayoutsParser::getX11LanguageList("incorrectKeyboard2", "correctEvdev.xml");
|
||||
EXPECT_TRUE(parsedResult.empty());
|
||||
parsedResult = X11LayoutsParser::getX11LanguageList("incorrectKeyboard3", "correctEvdev.xml");
|
||||
EXPECT_TRUE(parsedResult.empty());
|
||||
parsedResult = X11LayoutsParser::getX11LanguageList("keyboardFromFuture", "evdevFromFuture.xml");
|
||||
EXPECT_TRUE(parsedResult.empty());
|
||||
}
|
||||
|
||||
TEST(X11LayoutsParsingTests, layoutConvertTest)
|
||||
{
|
||||
EXPECT_EQ(X11LayoutsParser::convertLayotToISO("correctEvdev.xml", "us", true), "en");
|
||||
EXPECT_EQ(X11LayoutsParser::convertLayotToISO("incorrectEvdev1.xml", "us", true), "");
|
||||
EXPECT_EQ(X11LayoutsParser::convertLayotToISO("evdevFromFuture.xml", "us", true), "");
|
||||
}
|
||||
Loading…
Reference in a new issue