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:
Andrey Batyiev 2021-10-04 18:51:12 +03:00 committed by GitHub
parent 64139e092b
commit e9e21aad8f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
74 changed files with 1586 additions and 257 deletions

3
.gitmodules vendored
View file

@ -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

View file

@ -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

@ -0,0 +1 @@
Subproject commit 9e382f98076e57581fcc61323728443374889646

View file

@ -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;

View file

@ -204,7 +204,8 @@ protected:
kServerHostname,
kTLSCertPath,
kTLSKeyLength,
kPreventSleep
kPreventSleep,
kLanguageSync
};
void setScreenName(const QString& s);

View file

@ -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,

View file

@ -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());

View file

@ -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();

View file

@ -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);

View file

@ -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

View file

@ -136,4 +136,6 @@ private:
MessageParser m_parser;
IEventQueue* m_events;
String m_serverLanguage = "";
bool m_isUserNotifiedAboutLanguageSyncError = false;
};

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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();

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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*);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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);
};

View file

@ -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

View file

@ -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();

View file

@ -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;

View file

@ -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
}

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -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];

View file

@ -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

View file

@ -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})

View file

@ -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"

View file

@ -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

View file

@ -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);
}
};

View file

@ -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
/*!

View file

@ -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
/*!

View file

@ -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;

View 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"),
};

View file

@ -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;

View file

@ -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;

View file

@ -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;
}
}
}

View file

@ -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;
};

View file

@ -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

View file

@ -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();

View file

@ -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 '%':

View file

@ -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

View file

@ -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
/*!

View 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;
}

View 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);
};

View file

@ -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";

View file

@ -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

View file

@ -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
{

View file

@ -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;
};

View file

@ -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, &gti) && 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;
}
}

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View file

@ -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));
};

View file

@ -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)
{
}

View file

@ -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;

View 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");
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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;

View file

@ -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()));
}

View 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), "");
}